前几天其他项目组接了个项目,客户那边要求顺便给做个图片比较的工具,因为他们要看看同样的网页在IE7和IE8上显示有什么不同,不同的地方用用户指定的某种颜色标示出来,生成第三张图片。因为他们组比较忙,就帮他们做了做,UI界面就不用说了。
起初的想法是这样,先比较两幅图片的hash散列,如果相同就不比较了,下面就是这个算法:
/// <summary>
/// compare the hash value of two image files.
/// </summary>
/// <param name="leftImage"></param>
/// <param name="rightImage"></param>
/// <returns></returns>
private static bool HashCompare(Bitmap leftImage, Bitmap rightImage)
{
if (leftImage.Size != rightImage.Size)
{
return false;
}
byte[] buffer = new byte[1];
byte[] buffer2 = new byte[1];
ImageConverter converter = new ImageConverter();
buffer = (byte[])converter.ConvertTo(leftImage, buffer.GetType());
buffer2 = (byte[])converter.ConvertTo(rightImage, buffer2.GetType());
SHA256Managed managed = new SHA256Managed();
byte[] buffer3 = managed.ComputeHash(buffer);
byte[] buffer4 = managed.ComputeHash(buffer2);
for (int i = 0; (i < buffer3.Length) && (i < buffer4.Length); i++)
{
if (buffer3[i] != buffer4[i])
{
return false;
}
}
return true;
}
如果hash散列不同,就挨个像素比较,可这样有个问题,就是两幅图片有时候其实我们看起来是一样的,但是实际上你取他们像素的时候他们是不同的,如果我们将用户都不能分辨出不同的地方都标示出来意义不大,于是最后决定只比较像素的R.G.B,只要他们相同就行(这里的相同也做了妥协,就是两个像素的R.G.B值也允许有一定的误差),最后算法如下:
/// <summary>
/// compare two bitmaps.
/// </summary>
/// <param name="leftImage">the first bitmap</param>
/// <param name="rightImage">the second bitmap</param>
/// <param name="resultImageFullPath">the result bitmap</param>
/// <returns>return the comparision result</returns>
public static bool Compare(Bitmap leftImage, Bitmap rightImage, string resultImageFullPath)
{
bool matched = true;
//if the two bitmaps are not the same size,then resize the large one.
if (leftImage.Height > rightImage.Height)
{
leftImage = BitMapAdapter.ResizeImage(leftImage, rightImage.Width, rightImage.Height);
}
else
{
rightImage = BitMapAdapter.ResizeImage(rightImage, leftImage.Width, leftImage.Height);
}
if (!HashCompare(leftImage, rightImage))
{
BitmapData leftBitmapdata = leftImage.LockBits(new Rectangle(0, 0, leftImage.Width, leftImage.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
BitmapData rightBitmapdata = rightImage.LockBits(new Rectangle(0, 0, rightImage.Width, rightImage.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
unsafe
{
byte* leftImageDataPointer = (byte*)(leftBitmapdata.Scan0.ToPointer());
byte* rightImageDataPointer = (byte*)(rightBitmapdata.Scan0.ToPointer());
for (int y = 0; y < leftBitmapdata.Height; y++)
{
for (int x = 0; x < leftBitmapdata.Width; x++)
{
if (Math.Abs((int)leftImageDataPointer[0] - (int)rightImageDataPointer[0]) > RGBDifference &&
Math.Abs((int)leftImageDataPointer[1] - (int)rightImageDataPointer[1]) > RGBDifference &&
Math.Abs((int)leftImageDataPointer[2] - (int)rightImageDataPointer[2]) > RGBDifference)
{
matched = false;
leftImageDataPointer[0] = (byte)mismatchedColor.B;
leftImageDataPointer[1] = (byte)mismatchedColor.G;
leftImageDataPointer[2] = (byte)mismatchedColor.R;
}
leftImageDataPointer += 3;
rightImageDataPointer += 3;
}
leftImageDataPointer += leftBitmapdata.Stride - leftBitmapdata.Width * 3;
rightImageDataPointer += rightBitmapdata.Stride - rightBitmapdata.Width * 3;
}
}
leftImage.UnlockBits(leftBitmapdata);
rightImage.UnlockBits(rightBitmapdata);
if (!matched)
{
leftImage.Save(resultImageFullPath, ImageFormat.Jpeg);
}
return matched;
}
else
{
return true;
}
}
但是最后又出来个问题,因为对同一个网页,IE7跟IE8上显示的竟然不是同一个尺寸,最后就又加了一个调整尺寸的函数(上面红色的就是后来加的),调整函数如下:
/// <summary>
/// Resize bitmap
/// </summary>
/// <param name="bmp">original Bitmap</param>
/// <param name="newW">new width</param>
/// <param name="newH">new height</param>
/// <returns>worked bitmap</returns>
public static Bitmap ResizeImage(Bitmap bmp, int newW, int newH)
{
try
{
Bitmap bmap = new Bitmap(newW, newH);
Graphics graph = Graphics.FromImage(bmap);
graph.InterpolationMode = InterpolationMode.HighQualityBicubic;
graph.DrawImage(bmp, new Rectangle(0, 0, newW, newH), new Rectangle(0, 0, bmp.Width, bmp.Height), GraphicsUnit.Pixel);
graph.Dispose();
return bmap;
}
catch
{
return null;
}
}