使用GDI+比較圖像


使用GDI+比較圖像

介紹

.NET 使用託管的GDI+ 提供了一些重要的方法來處理圖像和位圖. 儘管如此, 當我想要藉助GDI+ 比較兩幅圖像看它們是否相同時, 我感到有點困惑. 我試着在我們的圖表組件 ( SimpleChart ) 上運行一些自動化的測試, 並且在測試說明書中我需要知道那些產生出來的圖表是否都相同. 要這樣做, 我需要在測試中將由SimpleChart產生的每個圖表和一個事先被認爲是好的圖表進行比較. 如果這兩個是相同的就通過測試.

Comparing Images

首次嘗試

比較兩幅圖像看它們是否相同的第一步是檢查每幅圖像的尺寸. 如果它們不匹配, 我們幾乎可以立刻知道圖像是不相同的. 一旦這個快速的測試完成, 我們就需要看看實際圖像的內容是否匹配. 最初, 我決定使用GDI+ 中Bitmap 類裏的GetPixel方法來比較第一幅圖像和第二幅圖像中每個相對應的像素. 如果在任意一點兩個像素不匹配, 我們就可以確實地說這兩幅圖像是不同的. 然而, 如果我們結束了比較測試而沒有出現不匹配現象, 那麼我們就能推斷出兩幅圖像真正是相同的.

public static CompareResult Compare(Bitmap bmp1, Bitmap bmp2)
{
CompareResult cr = CompareResult.ciCompareOk;

//測試並觀察, 看看我們是否擁有同樣尺寸的圖像
if (bmp1.Size != bmp2.Size)
{
cr = CompareResult.ciSizeMismatch;
}
else
{
//尺寸相同因此開始比較像素
for (int x = 0; x < bmp1.Width
&& cr == CompareResult.ciCompareOk; x++)
{
for (int y = 0; y < bmp1.Height
&& cr == CompareResult.ciCompareOk; y++)
{
if (bmp1.GetPixel(x, y) != bmp2.GetPixel(x, y))
cr = CompareResult.ciPixelMismatch;
}
}
}
return cr;
}

這個方法工作得很好, 但有一個主要的缺點, 缺乏速度. 使用這個方法來比較兩幅2000 × 1500像素的圖像將花費超過17秒! 要是有超過200張的圖像要比較, 這就意味着我的測試將花費接近1小時的時間來完成, 可是我並沒有準備要等那麼長的時間.

快速哈希

我所需要的就是一個更快的方法去比較兩幅圖像, 讓測試在某種意義上能及時的完成. 與其在每幅圖像上使用GetPixel方法來比較每個像素, 我決定使用比較每幅圖像的哈希值來看看它們是否相同, 那將會變得更快. 正如我們知道的, 哈希作爲一個固定長度的大量數據的表現, 在本例中指圖像數據, 它是一個唯一的值. 兩個圖像的哈希值匹配當且僅當相應的圖像也匹配. 在哈希中圖像的小小改變會導致極大的不可預計的改變.

在.NET的System.Security.Cryptography名稱空間下提供了許多不同的哈希算法, 比如SHA1和MD5, 但是我決定使用SHA256Managed 類. 類中的ComputeHash方法將數據的字節數組形式作爲輸入參數, 並且產生出一個256位的哈希值. 通過計算並且比較每幅圖像的哈希值, 我將很快能夠知道圖像是否是相同的.

現在剩下的問題僅僅是怎樣將儲存在GDI+ 中Bitmap對象裏的圖像數據轉換爲一個合適的形式, 也就是字節數組, 以傳遞給 ComputeHash 方法. 最初, 我考慮Bitmap類中的LockBits方法, 它可以允許我訪問每個像素字節, 但這意味着走進一個非託管代碼的地帶, 那是我最不想訪問的地方. 相反的, GDI+好意地提供了一個ImageConvertor類, 它允許我們將圖像對象轉換成另一種類型,比如字節數組.

想了解兩幅圖像是否相同的最後步驟就是比較兩個哈希值(或者說是字節數組)看它們是否匹配. 以下是最終的代碼.

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Security.Cryptography;

namespace Imagio
{
public class ComparingImages
{
public enum CompareResult
{
ciCompareOk,
ciPixelMismatch,
ciSizeMismatch
};

public static CompareResult Compare(Bitmap bmp1, Bitmap bmp2)
{
CompareResult cr = CompareResult.ciCompareOk;

//測試並觀察, 看看我們是否擁有同樣尺寸的圖像
  if (bmp1.Size != bmp2.Size)
{
cr = CompareResult.ciSizeMismatch;
}
else
{
//將每幅圖像轉換成字節數組
   System.Drawing.ImageConverter ic =
new System.Drawing.ImageConverter();
byte[] btImage1 = new byte[1];
btImage1 = (byte[])ic.ConvertTo(bmp1, btImage1.GetType());
byte[] btImage2 = new byte[1];
btImage2 = (byte[])ic.ConvertTo(bmp2, btImage2.GetType());

//爲每幅圖像計算哈希值
   SHA256Managed shaM = new SHA256Managed();
byte[] hash1 = shaM.ComputeHash(btImage1);
byte[] hash2 = shaM.ComputeHash(btImage2);

//比較哈希值
   for (int i = 0; i < hash1.Length && i < hash2.Length
&& cr == CompareResult.ciCompareOk; i++)
{
if (hash1[i] != hash2[i])
cr = CompareResult.ciPixelMismatch;
}
}
return cr;
}
}
}

結論

在一個2000 x 1500像素的位圖上運行這個新的比較方法僅花費0.28秒的比較時間, 這意味着自動測試200個SimpleChart圖像現在只要56秒就可以完成了.

哈希算法通常作爲一種安全工具來使用看看是否符合資格, 比如密碼的匹配. 使用相同的哈希方法, 我們也能很快的比較兩幅圖像看看它們是否相同.


備註: 以上文章選自www.CodeProject.com

原文鏈接: http://www.codeproject.com/dotnet/comparingimages.asp

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章