使用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

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