winfrom 版 图片对比
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Drawing.Imaging; using System.Runtime.InteropServices; namespace CaptureImages { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { } private void button2_Click(object sender, EventArgs e) { List<Point> po = getPoint(@"e:\cpImage\IMG_0001.jpg"); //for (int i = 0; i < po.Count; i++) //{ // label1.Text += po[i].X+","+po[i].Y; //} label1.Text = po[0].X + "," + po[0].Y; } private void button1_Click(object sender, EventArgs e) { //Bitmap bt1 = ImageScal(@"e:\问卷模板.jpg", 150, 340, 1213, 526); //Bitmap bt2 = ImageScal(@"e:\已经答题.jpg", 150, 340, 1221, 531); //Bitmap bt1 = ImageScal(@"e:\问卷模板.jpg", 50, 50, 1390, 940); Bitmap bt1 = ImageScal(@"e:\cpImage\IMG.jpg", 80, 80, 1428, 693); Bitmap bt2 = ImageScal(@"e:\cpImage\IMG_0001.jpg", 80, 80, 1500, 692); //Bitmap bt1 = ImageScal(@"e:\cpImage\IMG.jpg", 80, 80, 1290, 1263); //Bitmap bt2 = ImageScal(@"e:\cpImage\IMG_0001.jpg", 80, 80, 1362, 1262); //Bitmap bt1 = ImageScal(@"e:\已经答题.jpg", 50, 50, 1394, 1832); //Bitmap bt2 = ImageScal(@"e:\已经答题.jpg", 50, 50, 1397, 945); //Bitmap bt1 = ImageScal(@"e:\已经答题.jpg", 50, 50, 1394, 1832); //Bitmap bt2 = ImageScal(@"e:\已经答题.jpg", 50, 50, 1390, 945); List<Rectangle> rec = Compare(bt1, bt2, new Size(5, 5)); if (rec.Count > 60) { label1.Text = "已勾选或偏移太大"; } else { label1.Text = "未勾选"; } pictureBox1.Image = bt1; pictureBox2.Image = bt2; // bool i = ImgComp(bt1, bt2); } //截取图片某部分并转换为二值图像 private Bitmap ImageScal(string imagePath, int width, int height, int offsetX, int offsetY) { //获取原始图片 Bitmap originalImage = new Bitmap(imagePath); //新建一个图像 Bitmap newImage = new Bitmap(width, height); //新建一个画板 Graphics grp = Graphics.FromImage(newImage); //清空画布并以透明背景色填充 grp.Clear(Color.Transparent); //画图 grp.DrawImage(originalImage, new Rectangle(0, 0, width, height), new Rectangle(offsetX, offsetY, width, height), GraphicsUnit.Pixel); originalImage.Dispose(); grp.Dispose(); #region 图像变为灰度图像 //if (newImage != null) //{ // Rectangle rect = new Rectangle(0, 0, width,height); // System.Drawing.Imaging.BitmapData bmpData = newImage.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, newImage.PixelFormat); // IntPtr ptr = bmpData.Scan0; // int bytes = width * height * 3; // byte[] rgbValues = new byte[bytes]; // System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes); // double colorTemp = 0; // for (int i = 0; i < rgbValues.Length; i += 3) // { // colorTemp = rgbValues[i + 2] * 0.299 + rgbValues[i + 1] * 0.587 + rgbValues[i] * 0.114; // //rgbValues[i] = rgbValues[i + 1] = rgbValues[i + 2] = colorTemp>200?(byte)(255):(byte)0; // rgbValues[i] = rgbValues[i + 1] = rgbValues[i + 2] =(byte)colorTemp; // } // System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes); // newImage.UnlockBits(bmpData); //} #endregion //灰度图像变为二值图像 return Imgbinaryzation(newImage); } /// <summary> /// 图像二值化 /// </summary> private Bitmap Imgbinaryzation(Bitmap newImage) { if (newImage != null) { int threshold = ousu(newImage); Color color = new Color();//定义颜色分量 Color newColor = new Color(); for (int i = 0; i < newImage.Width; i++) { for (int j = 0; j <newImage.Height; j++) { color = newImage.GetPixel(i, j); //取得像素值存入颜色变量中 if (color.R > threshold) newColor = Color.White; else newColor = Color.Black; newImage.SetPixel(i, j, newColor); } } } return newImage; } //迭代计算阀值(即计算转换为黑白的中间区间) private int ousu(Bitmap img) { int thresholdValue = 1; int[] ihist = new int[256]; int i, j, k; int n, n1, n2, gmin, gmax; double m1, m2, sum, csum, fmax, sb; for (i = 0; i < 256; i++) ihist[i] = 0; gmin = 255; gmax = 0; for (i = 1; i < img.Width - 1; i++) { for (j = 1; j < img.Height - 1; j++) { Color c = img.GetPixel(i, j); int cn = ((c.G * 59 + c.R * 30 + c.B * 11) / 100); ihist[cn]++; // if (cn > gmax) gmax = cn; if (cn < gmin) gmin = cn; } } sum = csum = 0.0; n = 0; for (k = 0; k <= 255; k++) { sum += (double)k * (double)ihist[k]; n += ihist[k]; } if (n == 0) { return (60); } fmax = -1.0; n1 = 0; for (k = 0; k < 255; k++) { n1 += ihist[k]; if (n1 == 0) { continue; } n2 = n - n1; if (n2 == 0) { break; } csum += (double)k * ihist[k]; m1 = csum / n1; m2 = (sum - csum) / n2; sb = (double)n1 * (double)n2 * (m1 - m2) * (m1 - m2); /* bbg: note: can be optimized. */ if (sb > fmax) { fmax = sb; thresholdValue = k; } } return thresholdValue; } #region 通过直方图比较图像 /// <summary> /// 比较两张图片是否一致 /// </summary> /// <param name="actualImage">被测图片</param> /// <param name="expectedImage">参照图片</param> /// <returns>如果为真,两张图片一样否则不一样</returns> private bool ImgComp(Bitmap actualImage, Bitmap expectedImage) { //获取转换后的图片的直方图 int[] actualHisogram = GetHisogram(actualImage); int[] expectHisogram = GetHisogram(expectedImage); //比较两个直方图数据的相似性 float result = GetFinalResult(actualHisogram, expectHisogram); //如果相似度为1则表示两张图片是一样的 return (result > 0.999); //其实我们可以看到如果仅仅只是要比较两个直方图是不是一样的,完全可以比较数组中相应的index的元素是不是相等即可 } private int[] GetHisogram(Bitmap bmpImg) { BitmapData data = bmpImg.LockBits(new System.Drawing.Rectangle(0, 0, bmpImg.Width, bmpImg.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int[] histogram = new int[256]; unsafe { byte* ptr = (byte*)data.Scan0; int remain = data.Stride - data.Width * 3; for (int i = 0; i < histogram.Length; i++) histogram[i] = 0; for (int i = 0; i < data.Height; i++) { for (int j = 0; j < data.Width; j++) { int mean = ptr[0] + ptr[1] + ptr[2]; mean /= 3; histogram[mean]++; ptr += 3; } ptr += remain; } } bmpImg.UnlockBits(data); return histogram; } /// <summary> /// 计算两个直方图的相似度 /// </summary> /// <param name="actualHisogram">待测图片的直方图</param> /// <param name="expectedHisogram">参考图片的直方图</param> /// <returns>得到直方图的相似度</returns> private float GetFinalResult(int[] actualHisogram, int[] expectedHisogram) { if (actualHisogram.Length != expectedHisogram.Length) { return 0; } float result = 0; int j = actualHisogram.Length; for (int i = 0; i < j; i++) { result += 1 - GetAbs(actualHisogram[i], expectedHisogram[i]); } return result / j; } /// <summary> /// 计算两个整数相减后的绝对值除以两数中的较大值 /// </summary> /// <param name="firstNum">被减数</param> /// <param name="secondNum">减数</param> /// <returns>计算结果</returns> private float GetAbs(int firstNum, int secondNum) { int abs = Math.Abs(firstNum - secondNum); int maxNum = Math.Max(firstNum, secondNum); return (float)abs / ((maxNum == 0) ? 1 : maxNum); } #endregion #region 通过图像像素颜色比较图像 // /// <summary> ///// 图像颜色 ///// </summary> [StructLayout(LayoutKind.Explicit)] private struct ICColor { [FieldOffset(0)] public byte B; [FieldOffset(1)] public byte G; [FieldOffset(2)] public byte R; } //比较两个图像 //</summary> //<param name="bmp1"></param> //<param name="bmp2"></param> //<param name="block"></param> //<returns></returns> public static List<Rectangle> Compare(Bitmap bmp1, Bitmap bmp2, Size block) { List<Rectangle> rects = new List<Rectangle>(); PixelFormat pf = PixelFormat.Format24bppRgb; BitmapData bd1 = bmp1.LockBits(new Rectangle(0, 0, bmp1.Width, bmp1.Height), ImageLockMode.ReadOnly, pf); BitmapData bd2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width, bmp2.Height), ImageLockMode.ReadOnly, pf); try { unsafe { int w = 0, h = 0; while (h < bd1.Height && h < bd2.Height) { byte* p1 = (byte*)bd1.Scan0 + h * bd1.Stride; byte* p2 = (byte*)bd2.Scan0 + h * bd2.Stride; w = 0; while (w < bd1.Width && w < bd2.Width) { //按块大小进行扫描 for (int i = 0; i < block.Width; i++) { int wi = w + i; if (wi >= bd1.Width || wi >= bd2.Width) break; for (int j = 0; j < block.Height; j++) { int hj = h + j; if (hj >= bd1.Height || hj >= bd2.Height) break; ICColor* pc1 = (ICColor*)(p1 + wi * 3 + bd1.Stride * j); ICColor* pc2 = (ICColor*)(p2 + wi * 3 + bd2.Stride * j); if (pc1->R != pc2->R || pc1->G != pc2->G || pc1->B != pc2->B) { // 当前块有某个象素点颜色值不相同.也就是有差异. int bw = Math.Min(block.Width, bd1.Width - w); int bh = Math.Min(block.Height, bd1.Height - h); rects.Add(new Rectangle(w, h, bw, bh)); goto E; } } } E: w += block.Width; } h += block.Height; } } } finally { bmp1.UnlockBits(bd1); bmp2.UnlockBits(bd2); } return rects; } #endregion /// <summary> /// 计算左上角的起始坐标,用作偏移量 /// </summary> /// <returns></returns> private List<Point> getPoint(string imgPath) { List<Point> lispo = new List<Point>(); int count = 0; Color co = new Color(); Bitmap bt1 = new Bitmap(imgPath); for (int i = 230; i < bt1.Height; i++) { count=0; for (int j = 220; j < bt1.Width; j++) { co = bt1.GetPixel(j,i); if (co.R < 250 || co.G < 250 || co.B < 250) { count++; if (count > 5) { return lispo; } Point po = new Point(j,i); lispo.Add(po); } } } return lispo; } } }
asp.net 版 图片对比
/// <summary> /// 显示二维码的坐标 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void btnShowPoint_Click(object sender, EventArgs e) { List<Point> po = getPoint(@"e:\cpImage\01.jpg"); label1.Text = po[0].X + "," + po[0].Y; } /// <summary> /// 计算左上角的起始坐标,用作偏移量 /// </summary> /// <returns></returns> private List<Point> getPoint(string imgPath) { List<Point> lispo = new List<Point>(); int count = 0; Color co = new Color(); Bitmap bt1 = new Bitmap(imgPath); for (int i = 230; i < bt1.Height; i++) { count = 0; for (int j = 0; j < bt1.Width; j++) { co = bt1.GetPixel(j, i); if (co.R < 0 || co.G < 250 || co.B < 250) { count++; if (count > 5) { return lispo; } Point po = new Point(j, i); lispo.Add(po); } } } return lispo; } /// <summary> /// 显示对比结果 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void btnShowImg_Click(object sender, EventArgs e) { DataTable dt = enterGrade.GetPaperPositions(); if (dt!=null && dt.Rows.Count>0) { //截图图片中二维码的位置坐标 List<Point> po = getPoint(@"e:\cpImage\IMG_0001.jpg"); //读取数据库存储二维码的位置坐标 string[] Code = dt.Rows[0]["CodePos"].ToString().Split(','); //x轴方向的偏移量 int codeX = Convert.ToInt32(po[0].X) - Convert.ToInt32(Code[0]); //y轴方向的偏移量 int codeY = Convert.ToInt32(po[0].Y) - Convert.ToInt32(Code[1]); for (int i = 0; i < dt.Rows.Count; i++) { //每个 td 之间的间距 int Interval = Convert.ToInt32(dt.Rows[i]["CP_Interval"].ToString()); //主键编号 string PositionGuid = dt.Rows[i]["PositionGuid"].ToString(); //剪切图的长度 int cutLen = Convert.ToInt32(dt.Rows[0]["CPTDwh"].ToString()); //获得测评中好的起始坐标 string[] goodPos = dt.Rows[i]["CP_GoodPos"].ToString().Split(','); //好 x轴坐标 int sqlGoodX = Convert.ToInt32(goodPos[0]); //好 y轴坐标 int sqlGoodY = Convert.ToInt32(goodPos[1]); int goodX = sqlGoodX + codeX; int goodY = sqlGoodY + codeY; for (int j = 1; j <= 4; j++) { Bitmap bt1 = ImageScal(@"e:\cpImage\IMG.jpg", cutLen, cutLen, sqlGoodX, sqlGoodY); Bitmap bt2 = ImageScal(@"e:\cpImage\IMG_0001.jpg", cutLen, cutLen, goodX, goodY); List<Rectangle> rec = Compare(bt1, bt2, new Size(5, 5)); if (rec.Count > 56) { //插入指标得分 enterGrade.ModifyGetScore(PositionGuid,j+""); j = 5; } else { sqlGoodX += 138; //sqlGoodY += 190; goodX += 138; //goodY += 190; } } } } } //截取图片某部分并转换为二值图像 private Bitmap ImageScal(string imagePath, int width, int height, int offsetX, int offsetY) { //获取原始图片 Bitmap originalImage = new Bitmap(imagePath); //新建一个图像 Bitmap newImage =new Bitmap(width, height); //新建一个画板 Graphics grp = Graphics.FromImage(newImage); //清空画布并以透明背景色填充 grp.Clear(Color.Transparent); //画图 grp.DrawImage(originalImage, new Rectangle(0, 0, width, height), new Rectangle(offsetX, offsetY, width, height), GraphicsUnit.Pixel); originalImage.Dispose(); grp.Dispose(); //灰度图像变为二值图像 return Imgbinaryzation(newImage); } /// <summary> /// 图像二值化 /// </summary> private Bitmap Imgbinaryzation(Bitmap newImage) { if (newImage != null) { int threshold = ousu(newImage); Color color = new Color();//定义颜色分量 Color newColor = new Color(); for (int i = 0; i < newImage.Width; i++) { for (int j = 0; j < newImage.Height; j++) { color = newImage.GetPixel(i, j); //取得像素值存入颜色变量中 if (color.R > threshold) newColor = Color.White; else newColor = Color.Black; newImage.SetPixel(i, j, newColor); } } } return newImage; } //迭代计算阀值(即计算转换为黑白的中间区间) private int ousu(Bitmap img) { int thresholdValue = 1; int[] ihist = new int[256]; int i, j, k; int n, n1, n2, gmin, gmax; double m1, m2, sum, csum, fmax, sb; for (i = 0; i < 256; i++) ihist[i] = 0; gmin = 255; gmax = 0; for (i = 1; i < img.Width - 1; i++) { for (j = 1; j < img.Height - 1; j++) { Color c = img.GetPixel(i, j); int cn = ((c.G * 59 + c.R * 30 + c.B * 11) / 100); ihist[cn]++; // if (cn > gmax) gmax = cn; if (cn < gmin) gmin = cn; } } sum = csum = 0.0; n = 0; for (k = 0; k <= 255; k++) { sum += (double)k * (double)ihist[k]; n += ihist[k]; } if (n == 0) { return (60); } fmax = -1.0; n1 = 0; for (k = 0; k < 255; k++) { n1 += ihist[k]; if (n1 == 0) { continue; } n2 = n - n1; if (n2 == 0) { break; } csum += (double)k * ihist[k]; m1 = csum / n1; m2 = (sum - csum) / n2; sb = (double)n1 * (double)n2 * (m1 - m2) * (m1 - m2); /* bbg: note: can be optimized. */ if (sb > fmax) { fmax = sb; thresholdValue = k; } } return thresholdValue; } #region 通过直方图比较图像 /// <summary> /// 比较两张图片是否一致 /// </summary> /// <param name="actualImage">被测图片</param> /// <param name="expectedImage">参照图片</param> /// <returns>如果为真,两张图片一样否则不一样</returns> private bool ImgComp(Bitmap actualImage, Bitmap expectedImage) { //获取转换后的图片的直方图 int[] actualHisogram = GetHisogram(actualImage); int[] expectHisogram = GetHisogram(expectedImage); //比较两个直方图数据的相似性 float result = GetFinalResult(actualHisogram, expectHisogram); //如果相似度为1则表示两张图片是一样的 return (result > 0.999); //其实我们可以看到如果仅仅只是要比较两个直方图是不是一样的,完全可以比较数组中相应的index的元素是不是相等即可 } private int[] GetHisogram(Bitmap bmpImg) { BitmapData data = bmpImg.LockBits(new System.Drawing.Rectangle(0, 0, bmpImg.Width, bmpImg.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int[] histogram = new int[256]; unsafe { byte* ptr = (byte*)data.Scan0; int remain = data.Stride - data.Width * 3; for (int i = 0; i < histogram.Length; i++) histogram[i] = 0; for (int i = 0; i < data.Height; i++) { for (int j = 0; j < data.Width; j++) { int mean = ptr[0] + ptr[1] + ptr[2]; mean /= 3; histogram[mean]++; ptr += 3; } ptr += remain; } } bmpImg.UnlockBits(data); return histogram; } /// <summary> /// 计算两个直方图的相似度 /// </summary> /// <param name="actualHisogram">待测图片的直方图</param> /// <param name="expectedHisogram">参考图片的直方图</param> /// <returns>得到直方图的相似度</returns> private float GetFinalResult(int[] actualHisogram, int[] expectedHisogram) { if (actualHisogram.Length != expectedHisogram.Length) { return 0; } float result = 0; int j = actualHisogram.Length; for (int i = 0; i < j; i++) { result += 1 - GetAbs(actualHisogram[i], expectedHisogram[i]); } return result / j; } /// <summary> /// 计算两个整数相减后的绝对值除以两数中的较大值 /// </summary> /// <param name="firstNum">被减数</param> /// <param name="secondNum">减数</param> /// <returns>计算结果</returns> private float GetAbs(int firstNum, int secondNum) { int abs = Math.Abs(firstNum - secondNum); int maxNum = Math.Max(firstNum, secondNum); return (float)abs / ((maxNum == 0) ? 1 : maxNum); } #endregion #region 通过图像像素颜色比较图像 // /// <summary> ///// 图像颜色 ///// </summary> [StructLayout(LayoutKind.Explicit)] private struct ICColor { [FieldOffset(0)] public byte B; [FieldOffset(1)] public byte G; [FieldOffset(2)] public byte R; } /// <summary> /// //比较两个图像 /// </summary> /// <param name="bmp1"></param> /// <param name="bmp2"></param> /// <param name="block"></param> /// <returns></returns> public static List<Rectangle> Compare(Bitmap bmp1, Bitmap bmp2, Size block) { List<Rectangle> rects = new List<Rectangle>(); PixelFormat pf = PixelFormat.Format24bppRgb; BitmapData bd1 = bmp1.LockBits(new Rectangle(0, 0, bmp1.Width, bmp1.Height), ImageLockMode.ReadOnly, pf); BitmapData bd2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width, bmp2.Height), ImageLockMode.ReadOnly, pf); try { unsafe { int w = 0, h = 0; while (h < bd1.Height && h < bd2.Height) { byte* p1 = (byte*)bd1.Scan0 + h * bd1.Stride; byte* p2 = (byte*)bd2.Scan0 + h * bd2.Stride; w = 0; while (w < bd1.Width && w < bd2.Width) { //按块大小进行扫描 for (int i = 0; i < block.Width; i++) { int wi = w + i; if (wi >= bd1.Width || wi >= bd2.Width) break; for (int j = 0; j < block.Height; j++) { int hj = h + j; if (hj >= bd1.Height || hj >= bd2.Height) break; ICColor* pc1 = (ICColor*)(p1 + wi * 3 + bd1.Stride * j); ICColor* pc2 = (ICColor*)(p2 + wi * 3 + bd2.Stride * j); if (pc1->R != pc2->R || pc1->G != pc2->G || pc1->B != pc2->B) { // 当前块有某个象素点颜色值不相同.也就是有差异. int bw = Math.Min(block.Width, bd1.Width - w); int bh = Math.Min(block.Height, bd1.Height - h); rects.Add(new Rectangle(w, h, bw, bh)); goto E; } } } E: w += block.Width; } h += block.Height; } } } finally { bmp1.UnlockBits(bd1); bmp2.UnlockBits(bd2); } return rects; }