找色:
/// <summary> /// 找颜色 /// </summary> /// <param name="parPic">查找的图片的绝对路径</param> /// <param name="searchColor">查找的16进制颜色值,如#0C5FAB</param> /// <param name="searchRect">查找的矩形区域范围内</param> /// <param name="errorRange">容错</param> /// <returns></returns> System.Drawing.Point FindColor(string parPic, string searchColor, System.Drawing.Rectangle searchRect, byte errorRange = 10) { var colorX = System.Drawing.ColorTranslator.FromHtml(searchColor); var parBitmap = new Bitmap(parPic); var parData = parBitmap.LockBits(new System.Drawing.Rectangle(0, 0, parBitmap.Width, parBitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); var byteArraryPar = new byte[parData.Stride * parData.Height]; Marshal.Copy(parData.Scan0, byteArraryPar, 0, parData.Stride * parData.Height); if (searchRect.IsEmpty) { searchRect = new System.Drawing.Rectangle(0, 0, parBitmap.Width, parBitmap.Height); } var searchLeftTop = searchRect.Location; var searchSize = searchRect.Size; var iMax = searchLeftTop.Y + searchSize.Height;//行 var jMax = searchLeftTop.X + searchSize.Width;//列 int pointX = -1; int pointY = -1; for (int m = searchRect.Y; m < iMax; m++) { for (int n = searchRect.X; n < jMax; n++) { int index = m * parBitmap.Width * 4 + n * 4; var color = System.Drawing.Color.FromArgb(byteArraryPar[index + 3], byteArraryPar[index + 2], byteArraryPar[index + 1], byteArraryPar[index]); if (ColorAEqualColorB(color, colorX, errorRange)) { pointX = n; pointY = m; goto END; } } } END: parBitmap.UnlockBits(parData); return new System.Drawing.Point(pointX, pointY); } #endregion
方法中的容错范围,默认设置为10。R、G、B三者的范围都是0~255,容错为10,就表示每个范围都可以在10上下波动,下面还会有容错的概念
找图:
在一张大图中截取一个矩形小图,然后在任意包含该小图的图片中找到该小图的坐标位置
#region 找图 /// <summary> /// 查找图片,不能镂空 /// </summary> /// <param name="subPic"></param> /// <param name="parPic"></param> /// <param name="searchRect">如果为empty,则默认查找整个图像</param> /// <param name="errorRange">容错,单个色值范围内视为正确0~255</param> /// <param name="matchRate">图片匹配度,默认90%</param> /// <param name="isFindAll">是否查找所有相似的图片</param> /// <returns>返回查找到的图片的中心点坐标</returns> List<System.Drawing.Point> FindPicture(string subPic, string parPic, System.Drawing.Rectangle searchRect, byte errorRange, double matchRate = 0.9, bool isFindAll = false) { List<System.Drawing.Point> ListPoint = new List<System.Drawing.Point>(); var subBitmap = new Bitmap(subPic); var parBitmap = new Bitmap(parPic); int subWidth = subBitmap.Width; int subHeight = subBitmap.Height; int parWidth = parBitmap.Width; int parHeight = parBitmap.Height; if (searchRect.IsEmpty) { searchRect = new System.Drawing.Rectangle(0, 0, parBitmap.Width, parBitmap.Height); } var searchLeftTop = searchRect.Location; var searchSize = searchRect.Size; System.Drawing.Color startPixelColor = subBitmap.GetPixel(0, 0); var subData = subBitmap.LockBits(new System.Drawing.Rectangle(0, 0, subBitmap.Width, subBitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); var parData = parBitmap.LockBits(new System.Drawing.Rectangle(0, 0, parBitmap.Width, parBitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); var byteArrarySub = new byte[subData.Stride * subData.Height]; var byteArraryPar = new byte[parData.Stride * parData.Height]; Marshal.Copy(subData.Scan0, byteArrarySub, 0, subData.Stride * subData.Height); Marshal.Copy(parData.Scan0, byteArraryPar, 0, parData.Stride * parData.Height); var iMax = searchLeftTop.Y + searchSize.Height - subData.Height;//行 var jMax = searchLeftTop.X + searchSize.Width - subData.Width;//列 int smallOffsetX = 0, smallOffsetY = 0; int smallStartX = 0, smallStartY = 0; int pointX = -1; int pointY = -1; for (int i = searchLeftTop.Y; i < iMax; i++) { for (int j = searchLeftTop.X; j < jMax; j++) { //大图x,y坐标处的颜色值 int x = j, y = i; int parIndex = i * parWidth * 4 + j * 4; var colorBig = System.Drawing.Color.FromArgb(byteArraryPar[parIndex + 3], byteArraryPar[parIndex + 2], byteArraryPar[parIndex + 1], byteArraryPar[parIndex]); ; if (ColorAEqualColorB(colorBig, startPixelColor, errorRange)) { smallStartX = x - smallOffsetX;//待找的图X坐标 smallStartY = y - smallOffsetY;//待找的图Y坐标 int sum = 0;//所有需要比对的有效点 int matchNum = 0;//成功匹配的点 for (int m = 0; m < subHeight; m++) { for (int n = 0; n < subWidth; n++) { int x1 = n, y1 = m; int subIndex = m * subWidth * 4 + n * 4; var color = System.Drawing.Color.FromArgb(byteArrarySub[subIndex + 3], byteArrarySub[subIndex + 2], byteArrarySub[subIndex + 1], byteArrarySub[subIndex]); sum++; int x2 = smallStartX + x1, y2 = smallStartY + y1; int parReleativeIndex = y2 * parWidth * 4 + x2 * 4;//比对大图对应的像素点的颜色 var colorPixel = System.Drawing.Color.FromArgb(byteArraryPar[parReleativeIndex + 3], byteArraryPar[parReleativeIndex + 2], byteArraryPar[parReleativeIndex + 1], byteArraryPar[parReleativeIndex]); if (ColorAEqualColorB(colorPixel, color, errorRange)) { matchNum++; } } } if ((double)matchNum / sum >= matchRate) { Console.WriteLine((double)matchNum / sum); pointX = smallStartX + (int)(subWidth / 2.0); pointY = smallStartY + (int)(subHeight / 2.0); var point = new System.Drawing.Point(pointX, pointY); if (!ListContainsPoint(ListPoint, point, 10)) { ListPoint.Add(point); } if (!isFindAll) { goto FIND_END; } } } //小图x1,y1坐标处的颜色值 } } FIND_END: subBitmap.UnlockBits(subData); parBitmap.UnlockBits(parData); subBitmap.Dispose(); parBitmap.Dispose(); GC.Collect(); return ListPoint; } #endregion
private void Button_Click_Pic(object sender,RoutedEventArgs e) { string subPic = @"E:2.png"; string parPic = @"E:1.png"; ; List<System.Drawing.Point> List =FindPicture(subPic, parPic, System.Drawing.Rectangle.Empty, 100); foreach(System.Drawing.Point p in List) { Console.WriteLine(p.X.ToString()); Console.WriteLine(p.Y.ToString()); } Console.WriteLine("finish"); }
找字:
找字比较困难了呢,因为文字是一种镂空的图像,不像上述找的是非镂空图像,代码:
定义结构体:
struct NumBody { public int num;//数字 public int matchNum;//匹配的个数 public int matchSum; public double matchRate;//匹配度 public System.Drawing.Point point; public List<System.Drawing.Point> bodyCollectionPoint;//该数字所有像素在大图中的坐标 }
#region 找字 /// <summary> /// 找文字,镂空的图片文字 /// </summary> /// <param name="subPic"></param> /// <param name="parPic"></param> /// <param name="searchRect"></param> /// <param name="errorRange"></param> /// <param name="matchRate"></param> /// <param name="isFindAll"></param> /// <returns></returns> List<NumBody> FindText(string subPic, string parPic, System.Drawing.Rectangle searchRect, byte errorRange, double matchRate = 0.9, bool isFindAll = false) { List<NumBody> ListPoint = new List<NumBody>(); var subBitmap = new Bitmap(subPic); var parBitmap = new Bitmap(parPic); int subWidth = subBitmap.Width; int subHeight = subBitmap.Height; int parWidth = parBitmap.Width; int parHeight = parBitmap.Height; var bgColor = subBitmap.GetPixel(0, 0);//背景红色 if (searchRect.IsEmpty) { searchRect = new System.Drawing.Rectangle(0, 0, parBitmap.Width, parBitmap.Height); } var searchLeftTop = searchRect.Location; var searchSize = searchRect.Size; var subData = subBitmap.LockBits(new System.Drawing.Rectangle(0, 0, subBitmap.Width, subBitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); var parData = parBitmap.LockBits(new System.Drawing.Rectangle(0, 0, parBitmap.Width, parBitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); var byteArrarySub = new byte[subData.Stride * subData.Height]; var byteArraryPar = new byte[parData.Stride * parData.Height]; Marshal.Copy(subData.Scan0, byteArrarySub, 0, subData.Stride * subData.Height); Marshal.Copy(parData.Scan0, byteArraryPar, 0, parData.Stride * parData.Height); var iMax = searchLeftTop.Y + searchSize.Height - subData.Height;//行 var jMax = searchLeftTop.X + searchSize.Width - subData.Width;//列 System.Drawing.Color startPixelColor = System.Drawing.Color.FromArgb(0, 0, 0); int smallOffsetX = 0, smallOffsetY = 0; int smallStartX = 0, smallStartY = 0; int pointX = -1; int pointY = -1; for (int m = 0; m < subHeight; m++) { for (int n = 0; n < subWidth; n++) { smallOffsetX = n; smallOffsetY = m; int subIndex = m * subWidth * 4 + n * 4; var color = System.Drawing.Color.FromArgb(byteArrarySub[subIndex + 3], byteArrarySub[subIndex + 2], byteArrarySub[subIndex + 1], byteArrarySub[subIndex]); if (!ColorAEqualColorB(color, bgColor, errorRange)) { startPixelColor = color; goto END; } } } END: for (int i = searchLeftTop.Y; i < iMax; i++) { for (int j = searchLeftTop.X; j < jMax; j++) { //大图x,y坐标处的颜色值 int x = j, y = i; int parIndex = i * parWidth * 4 + j * 4; var colorBig = System.Drawing.Color.FromArgb(byteArraryPar[parIndex + 3], byteArraryPar[parIndex + 2], byteArraryPar[parIndex + 1], byteArraryPar[parIndex]); ; List<System.Drawing.Point> myListPoint = new List<System.Drawing.Point>(); if (ColorAEqualColorB(colorBig, startPixelColor, errorRange)) { smallStartX = x - smallOffsetX;//待找的图X坐标 smallStartY = y - smallOffsetY;//待找的图Y坐标 int sum = 0;//所有需要比对的有效点 int matchNum = 0;//成功匹配的点 for (int m = 0; m < subHeight; m++) { for (int n = 0; n < subWidth; n++) { int x1 = n, y1 = m; int subIndex = m * subWidth * 4 + n * 4; var color = System.Drawing.Color.FromArgb(byteArrarySub[subIndex + 3], byteArrarySub[subIndex + 2], byteArrarySub[subIndex + 1], byteArrarySub[subIndex]); if (color != bgColor) { sum++; int x2 = smallStartX + x1, y2 = smallStartY + y1; int parReleativeIndex = y2 * parWidth * 4 + x2 * 4;//比对大图对应的像素点的颜色 var colorPixel = System.Drawing.Color.FromArgb(byteArraryPar[parReleativeIndex + 3], byteArraryPar[parReleativeIndex + 2], byteArraryPar[parReleativeIndex + 1], byteArraryPar[parReleativeIndex]); if (ColorAEqualColorB(colorPixel, color, errorRange)) { matchNum++; } myListPoint.Add(new System.Drawing.Point(x2, y2)); } } } double rate = (double)matchNum / sum; if (rate>= matchRate) { Console.WriteLine((double)matchNum / sum); pointX = smallStartX + (int)(subWidth / 2.0); pointY = smallStartY + (int)(subHeight / 2.0); var point = new System.Drawing.Point(pointX, pointY); if (!ListTextBodyContainsPoint(ListPoint, point, 1)) { ListPoint.Add(new NumBody() { point = point, matchNum = matchNum,matchSum=sum, matchRate = rate, bodyCollectionPoint = myListPoint }); } SearchNumbersByMatchNum(ref ListPoint); if (!isFindAll) { goto FIND_END; } } } //小图x1,y1坐标处的颜色值 } } FIND_END: subBitmap.UnlockBits(subData); parBitmap.UnlockBits(parData); subBitmap.Dispose(); parBitmap.Dispose(); GC.Collect(); return ListPoint; }
特别注意:有了这个方法还是不能找到你要的文字的。要先处理文字,下面举例:
例如在这张图片上找到朋友的朋字的坐标位置:
1:打开你的PS,先将图片放大,看到像素方块为止,然后将朋字的范围圈选住,注意稍微比字圈选的大一点,像这样:
2:按住CTRL+C,然后CTRL+N,出现对话框:(教教大家使用PS^_^)
3:将背景内容选择透明,按确定,再按CTRL+V复制图像
4:将这个图片放大到看到像素为止,将所有非字体的位置全部用铅笔工具涂上同一种颜色,
5:涂完了之后将这张图片保存下来,这张图片就是我们要查找的“朋”字,图片是这样的
6:我们需要的就是第五步的图片和第一张底图,下面见证奇迹的时刻到了。
1 string str1 = @"C:UsersJimmyBrightDesktop1.png"; 2 string str2 = @"C:UsersJimmyBrightDesktop2.png"; 3 var xx = FindText(str2,str1,new System.Drawing.Rectangle(0, 0, 400, 600),10);
str1是我们的底图,str2是第五步的那张处理后的文字图片,xx就是我们最后需要的文字的位置坐标,我们运行看看。下面截图运行结果:
显然最后我们查找的文字在图片中的坐标为(224,286),大家可以下载那张图片验证
找数:
你以为找到文字就算完了吗?No,找数字才是最困难的,为什么呢?有人会问,数字难道不也是文字吗,不也可以通过PS处理数字达到查找其位置的目的吗?对的,数字也是文字,我们将需要查找的数字0~9全部PS处理,就能查到它们的位置了。但是有一个问题啊,游戏中用数字表示的地方通常是一连串的数字,这些数字里面包含0~9的任意组合。所以我们需要这样处理:
我们从0~9依次查找指定区域,记录每次查找的结果,没查到的数字不必记录,对查到结果的数字再按照X坐标排序,因为在X坐标越小,数字越靠左边。
还有一个严重的问题,例如38,14,这样的数字会很讨厌,为什么呢,我们会再8当中查找3,在4当中查找到1,这会对我们的数字识别产生重大误差,所以下面我也写了一个方法对这个问题做了处理,代码:
1 #region 查找数字 2 3 /// <summary> 4 /// 在指定区域里面查找数字 5 /// </summary> 6 /// <param name="numDic"></param> 7 /// <param name="parPic"></param> 8 /// <param name="searchRect"></param> 9 /// <param name="errorRange"></param> 10 /// <returns></returns> 11 int FindNumbers(Dictionary<int, string> numDic, string parPic, System.Drawing.Rectangle searchRect, byte errorRange=8, double matchRate = 0.9) 12 { 13 //同一个区域找到多个相同的图片 14 List<NumBody> ListBody = new List<NumBody>(); 15 foreach (var item in numDic) 16 { 17 var listPoint = FindText(item.Value, parPic, searchRect, errorRange, matchRate, true); 18 foreach (var point in listPoint) 19 { 20 ListBody.Add(new NumBody() { num = item.Key,matchNum=point.matchNum,matchSum=point.matchSum, matchRate=point.matchRate, point = point.point, bodyCollectionPoint = point.bodyCollectionPoint }); 21 } 22 } 23 24 SearchNumbersByMatchNum(ref ListBody); 25 var myList = from body in ListBody orderby body.point.X ascending select body; 26 string number = "0"; 27 foreach (var item in myList) 28 { 29 number += item.num; 30 } 31 int num = Int32.Parse(number); 32 return num; 33 } 34 /// <summary> 35 /// 搜索同一个数字的时候,出现重叠的地方,用匹配度去过滤掉匹配度低的 36 /// 比如同样是1,在控制匹配度允许下,一个(83,95)和(84,95)这两个点明显是同一个数字 37 /// 此时谁的匹配度低过滤掉谁 38 /// </summary> 39 /// <param name="ListBody"></param> 40 void SearchNumbersByMatchNum(ref List<NumBody> ListBody) 41 { 42 bool isValid = true; 43 for (int i = 0; i < ListBody.Count; i++) 44 { 45 var body = ListBody[i]; 46 47 for (int j = i; j < ListBody.Count; j++) 48 { 49 50 var bodyX = ListBody[j]; 51 if (!bodyX.Equals(body)) 52 { 53 int sameNum = 0; 54 foreach (var item in body.bodyCollectionPoint) 55 { 56 if (bodyX.bodyCollectionPoint.Contains(item)) 57 { 58 sameNum++; 59 } 60 } 61 if (sameNum >= 1)//有1个以上点重合,表面图像重叠,删除像素点数少的图像 62 { 63 isValid = false; 64 65 //如果某个数字100%匹配,那就不用比较了,这个数字肯定是对的 66 double maxRate = 1; 67 if (bodyX.matchRate >= maxRate) 68 { 69 ListBody.Remove(body); 70 } 71 else if (body.matchRate>=maxRate) 72 { 73 ListBody.Remove(bodyX); 74 } 75 else 76 { 77 if (bodyX.matchNum >= body.matchNum)//图像包含的所有像素个数 78 { 79 ListBody.Remove(body); 80 } 81 else 82 { 83 ListBody.Remove(bodyX); 84 } 85 } 86 SearchNumbersByMatchNum(ref ListBody); 87 } 88 } 89 } 90 } 91 if (isValid) 92 { 93 return; 94 } 95 } 96 97 #endregion
其他方法:
1 bool ColorAEqualColorB(System.Drawing.Color colorA, System.Drawing.Color colorB, byte errorRange = 10) 2 { 3 return colorA.A <= colorB.A + errorRange && colorA.A >= colorB.A - errorRange && 4 colorA.R <= colorB.R + errorRange && colorA.R >= colorB.R - errorRange && 5 colorA.G <= colorB.G + errorRange && colorA.G >= colorB.G - errorRange && 6 colorA.B <= colorB.B + errorRange && colorA.B >= colorB.B - errorRange; 7 8 } 9 bool ListContainsPoint(List<System.Drawing.Point> listPoint, System.Drawing.Point point, double errorRange = 10) 10 { 11 bool isExist = false; 12 foreach (var item in listPoint) 13 { 14 if (item.X <= point.X + errorRange && item.X >= point.X - errorRange && item.Y <= point.Y + errorRange && item.Y >= point.Y - errorRange) 15 { 16 isExist = true; 17 } 18 } 19 return isExist; 20 } 21 bool ListTextBodyContainsPoint(List<NumBody> listPoint, System.Drawing.Point point, double errorRange = 10) 22 { 23 bool isExist = false; 24 foreach (var item in listPoint) 25 { 26 27 if (item.point.X <= point.X + errorRange && item.point.X >= point.X - errorRange && item.point.Y <= point.Y + errorRange && item.point.Y >= point.Y - errorRange) 28 { 29 isExist = true; 30 } 31 } 32 return isExist; 33 }
结束语:以上代码本人实现了找颜色,找图片,找文字,找数字的所有功能,希望对朋友们能有所帮助。
最近找到了以前的代码:
想看源码的去这里
https://github.com/ahuchjm/BtnPress.git