2011-05-13 126 views
3

如果一個圖像已被保存兩次,兩個不同的文件名,有沒有辦法比較他們,看看他們是否是相同的..?匹配兩個相同的圖像具有不同的文件名

我希望基本的哈希或CRC類型檢查可以工作..?

文件大小可能不會,因爲有百萬計的圖像在游泳池和不同的圖像可能具有相同的大小..

希望有一個簡單的方法來做到這一點在C#..

+1

什麼是圖像的平均大小? – Rusty 2011-05-13 18:08:57

回答

7

如果文件內容相同,那麼加密散列至少會給出一個非常好的平等指示。 SHA256班將是一個很好的候選人,儘管這可能有點過頭了。例如:

static byte[] Sha256HashFile(string file) 
{ 
    using (SHA256 sha256 = SHA256.Create()) 
    { 
     using (Stream input = File.OpenRead(file)) 
     { 
      return sha256.ComputeHash(input); 
     } 
    } 
} 

比較兩個返回的字節數組的最簡單的方法可能是他們兩個轉換爲使用Convert.ToBase64字符串,然後比較字符串。醜但很容易:)你也可以使用Enumerable.SequenceEqual

byte[] hash1 = Sha256HashFile("file1.png"); 
byte[] hash2 = Sha256HashFile("file2.png"); 
bool same = hash1.SequenceEqual(hash2);  

如果你想存儲的哈希值作爲一組或字典,你可以實現自己IEqualityComparer<byte[]>但坦率地說這將是最容易使用一個base64字符串。例如,這將打印出的重複文件:

var hashToNameMap = new Dictionary<string, string>(); 
foreach (string file in files) 
{ 
    byte[] hash = Sha256HashFile(file); 
    string base64 = Convert.ToBase64(hash); 
    string existingName; 
    if (hashToNameMap.TryGetValue(base64, out existingName)) 
    { 
     Console.WriteLine("{0} is a duplicate of {1}", file, existingName); 
    } 
    else 
    { 
     hashToNameMap[base64] = file; 
    } 
} 

的幾個注意事項:

  • 這不是保證是準確的,但得到的碰撞的可能性非常小,特別是如果文件也必須是有效的圖像。
  • 這包括讀取全部文件 - 即使沒有其他文件具有相同的大小(因此沒有可能的重複項)。這對你來說可能是也可能不是問題。
  • 即使多個相同大小的文件,您只需要讀取所有這些文件以查找重複項即可......您可能會讀取文件並隨時計算散列值,只要找到該文件是不同的。

你如何處理這個取決於你的目標是絕對速度,簡化代碼等,也可能取決於池是否會隨時間而增加 - 例如,您可能希望儘快散列文件你會得到兩個或多個相同大小的文件,這樣當你添加另一個相同大小的文件時,你可以散列那個並添加它,而不用重新讀取現有的數據。

+0

+1(@Jon Skeet - 恭喜300K代表:))。正如Henk Holterman所指出的那樣,簡單的散列算法對於這種情況可能同樣有效,並且與加密算法相比可能會更快。但是Sha *可能是編寫代碼最簡單的方法。 – 2011-05-13 16:36:51

+0

@Alexei:我還沒有達到300K ... – 2011-05-13 16:44:26

+0

FWIW:我意識到這是一個有點老的線程,但是在兩個相同的100 MB位圖文件中,使用您在此描述的方法花費了大約7秒的時間, 〜200毫秒讀取這兩個文件並逐字節比較。 – Anders 2011-08-17 20:20:59

0

你可以從每個文件讀取二進制文件,然後比較包含的二進制文件。相同的圖像應該在每個數組中具有完全相同的二進制。

只是一個想法。

+1

「游泳池中有數百萬的圖像」...... – 2011-05-13 16:20:10

4

首先,無論如何檢查長度。只有當它們匹配時,你才能看得更深。

對於所有尺寸相同的圖像,計算一個哈希。當哈希匹配時,你可以相當確定圖像是相同的。該庫提供了許多密碼安全的哈希,但您可能想要尋求優化:

  • 樣本。如果圖像很大(> 100 kB),則只需通過計算幾個段上的散列即可節省I/O。在開始,中間和結束幾kB可能足以得到一個好的指紋。對於這些塊的大小和偏移量,請使用512的倍數。 Jpeg壓縮的工作方式有點像哈希:幾個像素的差異通常會導致比特流中的巨大差異。

  • 使用更快的散列。在這種情況下,一個簡單的xor算法就足夠了。

  • 如果你真的想一次比較2張圖片,那麼使用Hash實現來檢查中間結果。只要有區別,你可以停下來。

  • 但是,當你有很多相同大小的文件,每個文件計算一次哈希,並找到(大小,哈希)重複。
+0

如果散列衝突是一個問題,一旦發現衝突,一個字節對每個字節的比較便宜。 – Gabe 2011-05-13 16:21:26

+0

我認爲散列算法的選擇取決於它是CPU限制還是IO限制。但是,是的,那裏顯然有選擇。 – 2011-05-13 16:45:07

+0

@Jon,是的,我的兩個建議的順序是錯誤的。我會編輯一下。 – 2011-05-13 17:53:41

1

System.Security.Cryptography; 

使用SHA1

using(SHA1 sha = SHA1.Create()) { //added using based on Jon Skeet comment 
    byte[] newData = sha.ComputeHash(data); 
} 

數據

是文件

newData是散列

的字節[]數據這只是適當如果你想知道這兩個圖像文件是字面上的th Ë相同字節,而不是如果他們只是編碼相同的像素(可能是不同的文件,如果元數據是不同的)

+0

請注意,HashAlgorithm實現了IDisposable,因此您應該隨後處置SHA1。 – 2011-05-13 16:26:11

+0

謝謝 - 添加使用 – 2011-05-13 16:28:31

0

你也可以做這樣的事情

public string ImageToBase64(Image image, 
          System.Drawing.Imaging.ImageFormat format) 
{ 
    using (MemoryStream ms = new MemoryStream()) 
    { 
     // Convert Image to byte[] 
     image.Save(ms, format); 
     byte[] imageBytes = ms.ToArray(); 

     // Convert byte[] to Base64 String 
     string base64String = Convert.ToBase64String(imageBytes); 
     return base64String; 
    } 
} 

那麼你可以做一個String.Compare() 。這可能是較大的圖片緩慢,因爲它會產生一個相當大的字符串,但我已經發布它只是爲了completnes :)

相關問題