C# 使用win32 api c进行两幅图像比较#
我有两张图片,我想比较两张图片,想得到不同。我在谷歌上搜索,找到了一个链接,我从那里复制粘贴代码,以便使用win32 api进行图像比较。 这就是url 我在这里粘贴代码C# 使用win32 api c进行两幅图像比较#,c#,image-comparison,C#,Image Comparison,我有两张图片,我想比较两张图片,想得到不同。我在谷歌上搜索,找到了一个链接,我从那里复制粘贴代码,以便使用win32 api进行图像比较。 这就是url 我在这里粘贴代码 private void button1_Click(object sender, EventArgs e) { Bitmap _prevBitmap = new Bitmap(@"d:\prev.jpg"); Bitmap _newBitmap = new B
private void button1_Click(object sender, EventArgs e)
{
Bitmap _prevBitmap = new Bitmap(@"d:\prev.jpg");
Bitmap _newBitmap = new Bitmap(@"d:\current.jpg");
Rectangle bounds = GetBoundingBoxForChanges(_prevBitmap, _newBitmap);
if (bounds == Rectangle.Empty)
{
}
Bitmap diff = new Bitmap(bounds.Width, bounds.Height);
Graphics g = Graphics.FromImage(diff);
g.DrawImage(_newBitmap, 0, 0, bounds, GraphicsUnit.Pixel);
g.Dispose();
// Set the current bitmap as the previous to prepare
// for the next screen capture.
//
diff.Save(@"d:\diff.bmp");
//return diff;
}
private Rectangle GetBoundingBoxForChanges(Bitmap _prevBitmap, Bitmap _newBitmap)
{
// The search algorithm starts by looking
// for the top and left bounds. The search
// starts in the upper-left corner and scans
// left to right and then top to bottom. It uses
// an adaptive approach on the pixels it
// searches. Another pass is looks for the
// lower and right bounds. The search starts
// in the lower-right corner and scans right
// to left and then bottom to top. Again, an
// adaptive approach on the search area is used.
//
// Note: The GetPixel member of the Bitmap class
// is too slow for this purpose. This is a good
// case of using unsafe code to access pointers
// to increase the speed.
//
// Validate the images are the same shape and type.
//
if (_prevBitmap.Width != _newBitmap.Width ||
_prevBitmap.Height != _newBitmap.Height ||
_prevBitmap.PixelFormat != _newBitmap.PixelFormat)
{
// Not the same shape...can't do the search.
//
return Rectangle.Empty;
}
// Init the search parameters.
//
int width = _newBitmap.Width;
int height = _newBitmap.Height;
int left = width;
int right = 0;
int top = height;
int bottom = 0;
BitmapData bmNewData = null;
BitmapData bmPrevData = null;
try
{
// Lock the bits into memory.
//
bmNewData = _newBitmap.LockBits(
new Rectangle(0, 0, _newBitmap.Width, _newBitmap.Height),
ImageLockMode.ReadOnly, _newBitmap.PixelFormat);
bmPrevData = _prevBitmap.LockBits(
new Rectangle(0, 0, _prevBitmap.Width, _prevBitmap.Height),
ImageLockMode.ReadOnly, _prevBitmap.PixelFormat);
// The images are ARGB (4 bytes)
//
int numBytesPerPixel = 4;
// Get the number of integers (4 bytes) in each row
// of the image.
//
int strideNew = bmNewData.Stride / numBytesPerPixel;
int stridePrev = bmPrevData.Stride / numBytesPerPixel;
// Get a pointer to the first pixel.
//
// Note: Another speed up implemented is that I don't
// need the ARGB elements. I am only trying to detect
// change. So this algorithm reads the 4 bytes as an
// integer and compares the two numbers.
//
System.IntPtr scanNew0 = bmNewData.Scan0;
System.IntPtr scanPrev0 = bmPrevData.Scan0;
// Enter the unsafe code.
//
unsafe
{
// Cast the safe pointers into unsafe pointers.
//
int* pNew = (int*)(void*)scanNew0;
int* pPrev = (int*)(void*)scanPrev0;
// First Pass - Find the left and top bounds
// of the minimum bounding rectangle. Adapt the
// number of pixels scanned from left to right so
// we only scan up to the current bound. We also
// initialize the bottom & right. This helps optimize
// the second pass.
//
// For all rows of pixels (top to bottom)
//
for (int y = 0; y < _newBitmap.Height; ++y)
{
// For pixels up to the current bound (left to right)
//
for (int x = 0; x < left; ++x)
{
// Use pointer arithmetic to index the
// next pixel in this row.
//
if ((pNew + x)[0] != (pPrev + x)[0])
{
// Found a change.
//
if (x < left)
{
left = x;
}
if (x > right)
{
right = x;
}
if (y < top)
{
top = y;
}
if (y > bottom)
{
bottom = y;
}
}
}
// Move the pointers to the next row.
//
pNew += strideNew;
pPrev += stridePrev;
}
// If we did not find any changed pixels
// then no need to do a second pass.
//
if (left != width)
{
// Second Pass - The first pass found at
// least one different pixel and has set
// the left & top bounds. In addition, the
// right & bottom bounds have been initialized.
// Adapt the number of pixels scanned from right
// to left so we only scan up to the current bound.
// In addition, there is no need to scan past
// the top bound.
//
// Set the pointers to the first element of the
// bottom row.
//
pNew = (int*)(void*)scanNew0;
pPrev = (int*)(void*)scanPrev0;
pNew += (_newBitmap.Height - 1) * strideNew;
pPrev += (_prevBitmap.Height - 1) * stridePrev;
// For each row (bottom to top)
//
for (int y = _newBitmap.Height - 1; y > top; y--)
{
// For each column (right to left)
//
for (int x = _newBitmap.Width - 1; x > right; x--)
{
// Use pointer arithmetic to index the
// next pixel in this row.
//
if ((pNew + x)[0] != (pPrev + x)[0])
{
// Found a change.
//
if (x > right)
{
right = x;
}
if (y > bottom)
{
bottom = y;
}
}
}
// Move up one row.
//
pNew -= strideNew;
pPrev -= stridePrev;
}
}
}
}
catch (Exception ex)
{
int xxx = 0;
}
finally
{
// Unlock the bits of the image.
//
if (bmNewData != null)
{
_newBitmap.UnlockBits(bmNewData);
}
if (bmPrevData != null)
{
_prevBitmap.UnlockBits(bmPrevData);
}
}
// Validate we found a bounding box. If not
// return an empty rectangle.
//
int diffImgWidth = right - left + 1;
int diffImgHeight = bottom - top + 1;
if (diffImgHeight < 0 || diffImgWidth < 0)
{
// Nothing changed
return Rectangle.Empty;
}
// Return the bounding box.
//
return new Rectangle(left, top, diffImgWidth, diffImgHeight);
}
private void按钮1\u单击(对象发送者,事件参数e)
{
位图\u prevBitmap=新位图(@“d:\prev.jpg”);
位图_newBitmap=新位图(@“d:\current.jpg”);
矩形边界=GetBoundingBoxForChanges(\u prevBitmap,\u newBitmap);
如果(边界==矩形.空)
{
}
位图差异=新位图(bounds.Width、bounds.Height);
Graphics g=Graphics.FromImage(diff);
g、 DrawImage(_newBitmap,0,0,bounds,GraphicsUnit.Pixel);
g、 处置();
//将当前位图设置为要准备的上一个位图
//下一个屏幕截图。
//
差异保存(@“d:\diff.bmp”);
//返回差;
}
私有矩形GetBoundingBoxForChanges(位图_prevBitmap,位图_newBitmap)
{
//搜索算法从查找开始
//用于上边界和左边界。搜索
//从左上角开始扫描
//从左到右,然后从上到下。它使用
//一种基于像素的自适应方法
//搜索。另一个过程是查找
//下限和右边界。搜索开始
//在右下角,向右扫描
//从左到右,再从下到上
//在搜索区域使用自适应方法。
//
//注意:位图类的GetPixel成员
//对于这个目的来说太慢了。这是一个很好的方法
//使用不安全代码访问指针的情况
//提高速度。
//
//验证图像是否具有相同的形状和类型。
//
如果(_previtmap.Width!=_newBitmap.Width||
_prevBitmap.Height!=\u newBitmap.Height||
_prevBitmap.PixelFormat!=\u newBitmap.PixelFormat)
{
//形状不同…无法进行搜索。
//
返回矩形。为空;
}
//初始化搜索参数。
//
int width=_newBitmap.width;
int height=_newBitmap.height;
int左=宽度;
int right=0;
int top=高度;
int-bottom=0;
BitmapData bmNewData=null;
BitmapData bmPrevData=null;
尝试
{
//将位锁定到内存中。
//
bmNewData=_newBitmap.LockBits(
新矩形(0,0,_newBitmap.Width,_newBitmap.Height),
ImageLockMode.ReadOnly,_newBitmap.PixelFormat);
bmPrevData=_prevBitmap.LockBits(
新矩形(0,0,_prevBitmap.Width,_prevBitmap.Height),
ImageLockMode.ReadOnly,_prevBitmap.PixelFormat);
//图像为ARGB(4字节)
//
int numbytesperpix=4;
//获取每行中的整数数(4字节)
//这是图像的一部分。
//
int-stridnew=bmNewData.Stride/numbytesperpix;
int-stripprev=bmPrevData.stripe/numbytesperpix;
//获取指向第一个像素的指针。
//
//注意:实现的另一个加速是我没有
//需要ARGB元素。我只是想检测
//所以这个算法把4个字节作为
//整数并比较这两个数字。
//
System.IntPtr scanNew0=bmNewData.Scan0;
System.IntPtr scanPrev0=bmPrevData.Scan0;
//输入不安全代码。
//
不安全的
{
//将安全指针转换为不安全指针。
//
int*pNew=(int*)(void*)scanNew0;
int*pPrev=(int*)(void*)扫描预览0;
//第一次传球-找到左侧和顶部边界
//最小边界矩形的。调整
//从左到右扫描的像素数,因此
//我们只扫描到当前边界。我们还
//初始化底部和右侧。这有助于优化
//第二关。
//
//对于所有像素行(从上到下)
//
对于(int y=0;y<_newBitmap.Height;++y)
{
//对于当前边界以下的像素(从左到右)
//
对于(int x=0;x右)
{
右=x;
}
如果(y底部)
{
底部=y;
}
}
}
//将指针移动到下一行。
//
pNew+=新的;
pPrev+=Prev;
}
//如果我们没有发现任何改变的像素
//那就不需要做第二件事了
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using log4net;
namespace ImageDiff
{
public class ImageDifferences
{
private static ILog mLog = LogManager.GetLogger("ImageDifferences");
public static unsafe Bitmap PixelDiff(Image a, Image b)
{
if (!a.Size.Equals(b.Size)) return null;
if (!(a is Bitmap) || !(b is Bitmap)) return null;
return PixelDiff(a as Bitmap, b as Bitmap);
}
public static unsafe Bitmap PixelDiff(Bitmap a, Bitmap b)
{
Bitmap output = new Bitmap(
Math.Max(a.Width, b.Width),
Math.Max(a.Height, b.Height),
PixelFormat.Format32bppArgb);
Rectangle recta = new Rectangle(Point.Empty, a.Size);
Rectangle rectb = new Rectangle(Point.Empty, b.Size);
Rectangle rectOutput = new Rectangle(Point.Empty, output.Size);
BitmapData aData = a.LockBits(recta, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
BitmapData bData = b.LockBits(rectb, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
BitmapData outputData = output.LockBits(rectOutput, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
try
{
byte* aPtr = (byte*)aData.Scan0;
byte* bPtr = (byte*)bData.Scan0;
byte* outputPtr = (byte*)outputData.Scan0;
int len = aData.Stride * aData.Height;
for (int i = 0; i < len; i++)
{
// For alpha use the average of both images (otherwise pixels with the same alpha won't be visible)
if ((i + 1) % 4 == 0)
*outputPtr = (byte)((*aPtr + *bPtr) / 2);
else
*outputPtr = (byte)~(*aPtr ^ *bPtr);
outputPtr++;
aPtr++;
bPtr++;
}
return output;
}
catch (Exception ex)
{
mLog.Error("Error calculating image differences: " + ex.Message);
return null;
}
finally
{
a.UnlockBits(aData);
b.UnlockBits(bData);
output.UnlockBits(outputData);
}
}
}
}
bmNewData = _newBitmap.LockBits(...., _newBitmap.PixelFormat);
Bitmap overlayImage;
Bitmap sourceImage;
//ToDo: Load the two images.
// Create filter.
Difference filter = new Difference(overlayImage);
// Apply the filter and return a new bitmap that is the difference between the source and overlay images.
Bitmap resultImage = filter.Apply(sourceImage);
// If you don't want a new image the you can apply the filter directly to the source image.
filter.ApplyInPlace(sourceImage);