2010-01-16 17 views
10

मेरे पास ~ 20000 जेपीजी छवियां हैं, जिनमें से कुछ डुप्लिकेट हैं। दुर्भाग्यवश, कुछ फ़ाइलों को EXIF ​​मेटाडेटा के साथ टैग किया गया है, इसलिए एक साधारण फ़ाइल हैश डुप्लिकेट की पहचान नहीं कर सकता है।डॉटनेट के साथ एक jpg फ़ाइल में केवल छवि डेटा हैश कैसे है?

मैं इन प्रक्रियाओं के लिए पावरहेल स्क्रिप्ट बनाने का प्रयास कर रहा हूं, लेकिन केवल बिटमैप डेटा निकालने का कोई तरीका नहीं ढूंढ सकता।

system.drawing.bitmap केवल बिटमैप ऑब्जेक्ट को वापस कर सकता है, बाइट्स नहीं। एक GetHash() फ़ंक्शन है, लेकिन यह स्पष्ट रूप से पूरी फ़ाइल पर कार्य करता है।

मैं इन फ़ाइलों को इस तरह से कैसे रख सकता हूं कि EXIF ​​जानकारी को बाहर रखा गया है? यदि संभव हो तो मैं बाहरी निर्भरताओं से बचना पसंद करूंगा।

उत्तर

8

यह एक पावरशेल वी 2.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() } 
      } 
     } 
    } 
} 
4

आप एक 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(); 
} 
+1

आपका पहला दृष्टिकोण काम नहीं करता है । यह एक ही छवि (विभिन्न मेटाडाटा) के लिए अलग हैशकोड देता है। दूसरा दृष्टिकोण काम करता है और PowerShell स्क्रिप्ट में पूर्णता के विभिन्न स्तरों पर हर कोई क्या कर रहा है। :-) –

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 "" 
     } 
} 

यह इनपुट फ़ाइल के बावजूद एक ही हैश उत्पन्न करता है, इसलिए कुछ अभी भी q नहीं है सही सही

5

यहाँ एक powershell स्क्रिप्ट LockBits का उपयोग कर निकाले के रूप में छवि का केवल बाइट्स पर एक SHA256 हैश पैदा करता है: यह एक पाइप लाइन के सक्षम संस्करण भी वाइल्डकार्ड और शाब्दिक रास्तों को स्वीकार करता है। यह अलग-अलग फाइलों के लिए एक अद्वितीय हैश उत्पन्न करना चाहिए जो अलग है। कृपया ध्यान दें, कि मैंने फ़ाइल पुनरावृत्त कोड शामिल नहीं किया है, हालांकि वर्तमान में हार्डकोड c: \ test.bmp को फ़ोरैच निर्देशिका इटरेटर के साथ प्रतिस्थापित करने के लिए यह अपेक्षाकृत सरल कार्य होना चाहिए। परिवर्तनीय $ फ़ाइनल में अंतिम हैश की हेक्स-एसीआई स्ट्रिंग शामिल है।

[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 
[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing.Imaging") 
[System.Reflection.Assembly]::LoadWithPartialName("System.Security") 


$bmp = [System.Drawing.Bitmap]::FromFile("c:\\test.bmp") 
$rect = [System.Drawing.Rectangle]::FromLTRB(0, 0, $bmp.width, $bmp.height) 
$lockmode = [System.Drawing.Imaging.ImageLockMode]::ReadOnly    
$bmpData = $bmp.LockBits($rect, $lockmode, $bmp.PixelFormat); 
$dataPointer = $bmpData.Scan0; 
$totalBytes = $bmpData.Stride * $bmp.Height; 
$values = New-Object byte[] $totalBytes 
[System.Runtime.InteropServices.Marshal]::Copy($dataPointer, $values, 0, $totalBytes);     
$bmp.UnlockBits($bmpData); 

$sha = new-object System.Security.Cryptography.SHA256Managed 
$hash = $sha.ComputeHash($values); 
$final = [System.BitConverter]::ToString($hash).Replace("-", ""); 

शायद बराबर सी # कोड भी समझ में आप सहायता करेगा:

private static String ImageDataHash(FileInfo imgFile) 
{ 
    using (Bitmap bmp = (Bitmap)Bitmap.FromFile(imgFile.FullName)) 
    {     
     BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat); 
     IntPtr dataPointer = bmpData.Scan0; 
     int totalBytes = bmpData.Stride * bmp.Height; 
     byte[] values = new byte[totalBytes];     
     System.Runtime.InteropServices.Marshal.Copy(dataPointer, values, 0, totalBytes);     
     bmp.UnlockBits(bmpData); 
     SHA256 sha = new SHA256Managed(); 
     byte[] hash = sha.ComputeHash(values); 
     return BitConverter.ToString(hash).Replace("-", "");     
    } 
} 
+0

बिटकोनवर्टर। टॉस्ट्रिंग() - अच्छा! –

0

यह एक MemoryStream को बचाने के लिए एक तेजी से विधि है:

$ms = New-Object System.IO.MemoryStream 
$bmp.Save($ms, [System.Drawing.Imaging.ImageFormat]::Bmp) 
[void]$ms.Seek(0,'Begin') 
संबंधित मुद्दे