Как хешировать только данные изображения в jpg-файле с помощью dotnet?

У меня есть ~ 20000 изображений jpg, некоторые из которых являются дубликатами. К сожалению, некоторые файлы были помечены метаданными EXIF, поэтому простой хэш-файл не может идентифицировать дублированный.

Я пытаюсь создать сценарий Powershell для их обработки, но не могу найти способ извлечь только растровые данные.

system.drawing.bitmap может возвращать только растровый объект, а не байты. Есть функция GetHash (), но она, очевидно, действует на весь файл.

Как я могу хэшировать эти файлы таким образом, чтобы исключить информацию EXIF? Я бы предпочел избегать внешних зависимостей, если это возможно.

10 голосов | спросил JSacksteder 16 Jpm1000000pmSat, 16 Jan 2010 19:20:10 +030010 2010, 19:20:10

3 ответа


0

Это реализация расширенных функций PowerShell V2.0. Это немного долго, но я убедился, что он дает тот же хэш-код (сгенерированный из пикселей растрового изображения) на одном и том же изображении, но с разными метаданными и размерами файлов. Это версия с поддержкой конвейера, которая также принимает символы подстановки и буквенные пути:

function Get-BitmapHashCode
{
    [CmdletBinding(DefaultParameterSetName="Path")]
    param(
        [Parameter(Mandatory=$true, 
                   Position=0, 
                   ParameterSetName="Path", 
                   ValueFromPipeline=$true, 
                   ValueFromPipelineByPropertyName=$true,
                   HelpMessage="Path to bitmap file")]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $Path,

        [Alias("PSPath")]
        [Parameter(Mandatory=$true, 
                   Position=0, 
                   ParameterSetName="LiteralPath", 
                   ValueFromPipelineByPropertyName=$true,
                   HelpMessage="Path to bitmap file")]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $LiteralPath
    )

    Begin {
        Add-Type -AssemblyName System.Drawing
        $sha = new-object System.Security.Cryptography.SHA256Managed
    }

    Process {
        if ($psCmdlet.ParameterSetName -eq "Path")
        {
            # In -Path case we may need to resolve a wildcarded path
            $resolvedPaths = @($Path | Resolve-Path | Convert-Path)
        }
        else 
        {
            # Must be -LiteralPath
            $resolvedPaths = @($LiteralPath | Convert-Path)
        }

        # Find PInvoke info for each specified path       
        foreach ($rpath in $resolvedPaths) 
        {           
            Write-Verbose "Processing $rpath"
            try {
                $bmp    = new-object System.Drawing.Bitmap $rpath
                $stream = new-object System.IO.MemoryStream
                $writer = new-object System.IO.BinaryWriter $stream
                for ($w = 0; $w -lt $bmp.Width; $w++) {
                    for ($h = 0; $h -lt $bmp.Height; $h++) {
                        $pixel = $bmp.GetPixel($w,$h)
                        $writer.Write($pixel.ToArgb())
                    }
                }
                $writer.Flush()
                [void]$stream.Seek(0,'Begin')
                $hash = $sha.ComputeHash($stream)
                [BitConverter]::ToString($hash) -replace '-',''
            }
            finally {
                if ($bmp)    { $bmp.Dispose() }
                if ($writer) { $writer.Close() }
            }
        }  
    }
}
ответил Keith Hill 18 Jpm1000000pmMon, 18 Jan 2010 21:33:30 +030010 2010, 21:33:30
0

Вы можете загрузить JPEG в System.Drawing.Image и использовать его метод GetHashCode

using (var image = Image.FromFile("a.jpg"))
    return image.GetHashCode();

Получить байты вы можете

using (var image = Image.FromFile("a.jpg"))
using (var output = new MemoryStream())
{
    image.Save(output, ImageFormat.Bmp);
    return output.ToArray();
}
ответил Jader Dias 16 Jpm1000000pmSat, 16 Jan 2010 19:23:57 +030010 2010, 19:23:57
0

Переводя на powershell, я получаю это -

[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$provider = New-Object System.Security.Cryptography.SHA1CryptoServiceProvider

foreach ($location in $args)
{
    $files=get-childitem $location | where{$_.Extension -match "jpg|jpeg"}
    foreach ($f in $files)
        {
        $bitmap = New-Object -TypeName System.Drawing.Bitmap -ArgumentList $f.FullName
        $stream = New-Object -TypeName System.IO.MemoryStream
        $bitmap.Save($stream)

        $hashbytes = $provider.ComputeHash($stream.ToArray())
        $hashstring = ""
        foreach ($byte in $hashbytes) 
            {$hashstring += $byte.tostring("x2")}  
        $f.FullName
        $hashstring
        echo ""
        }
} 

Это создает один и тот же хэш независимо от входного файла, поэтому что-то все еще не совсем корректно.

ответил JSacksteder 17 Jam1000000amSun, 17 Jan 2010 03:26:15 +030010 2010, 03:26:15

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132