C#实现按键精灵的'找图' '找色' '找字'的功能

背景:游戏辅助功能通常使用按键精灵编写脚本,按键精灵的最大卖点就是能够找到画面中字,图,色,这对于模拟用户鼠标操作至关重要,这能找到道具,找到血量,实现自动打怪,自动补血,自动买卖道具,博主闲来无聊,看到一款按键精灵实现的辅助,于是乎想用WPF也写一款辅助工具,实现其核心的找图找色等功能。博主测试,对于背景复杂多变的画面,找不变图的成功率达到100%,找带透明的图,比如文字,能达到90%以上。默认您已经知道一个颜色值由argb构成,每个值范围都是0~255。网上发现不少人询问过该问题,几乎没有比较全面的解答,今天本博主自己写了这些功能的代码,C#同学们,以后可以用这几个函数实现你的游戏辅助了哦(⊙o⊙)

 找色:

 

 
  1. /// <summary>

  2. /// 找颜色

  3. /// </summary>

  4. /// <param name="parPic">查找的图片的绝对路径</param>

  5. /// <param name="searchColor">查找的16进制颜色值,如#0C5FAB</param>

  6. /// <param name="searchRect">查找的矩形区域范围内</param>

  7. /// <param name="errorRange">容错</param>

  8. /// <returns></returns>

  9. System.Drawing.Point FindColor(string parPic, string searchColor, System.Drawing.Rectangle searchRect, byte errorRange = 10)

  10. {

  11. var colorX = System.Drawing.ColorTranslator.FromHtml(searchColor);

  12. var parBitmap = new Bitmap(parPic);

  13. var parData = parBitmap.LockBits(new System.Drawing.Rectangle(0, 0, parBitmap.Width, parBitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

  14. var byteArraryPar = new byte[parData.Stride * parData.Height];

  15. Marshal.Copy(parData.Scan0, byteArraryPar, 0, parData.Stride * parData.Height);

  16. if (searchRect.IsEmpty)

  17. {

  18. searchRect = new System.Drawing.Rectangle(0, 0, parBitmap.Width, parBitmap.Height);

  19. }

  20. var searchLeftTop = searchRect.Location;

  21. var searchSize = searchRect.Size;

  22. var iMax = searchLeftTop.Y + searchSize.Height;//行

  23. var jMax = searchLeftTop.X + searchSize.Width;//列

  24. int pointX = -1; int pointY = -1;

  25. for (int m = searchRect.Y; m < iMax; m++)

  26. {

  27. for (int n = searchRect.X; n < jMax; n++)

  28. {

  29. int index = m * parBitmap.Width * 4 + n * 4;

  30. var color = System.Drawing.Color.FromArgb(byteArraryPar[index + 3], byteArraryPar[index + 2], byteArraryPar[index + 1], byteArraryPar[index]);

  31. if (ColorAEqualColorB(color, colorX, errorRange))

  32. {

  33. pointX = n;

  34. pointY = m;

  35. goto END;

  36. }

  37. }

  38. }

  39. END:

  40. parBitmap.UnlockBits(parData);

  41. return new System.Drawing.Point(pointX, pointY);

  42. }

  43. #endregion

  

方法中的容错范围,默认设置为10。R、G、B三者的范围都是0~255,容错为10,就表示每个范围都可以在10上下波动,下面还会有容错的概念

  找图:

在一张大图中截取一个矩形小图,然后在任意包含该小图的图片中找到该小图的坐标位置

 

 

 

 
  1. #region 找图

  2.  
  3. /// <summary>

  4. /// 查找图片,不能镂空

  5. /// </summary>

  6. /// <param name="subPic"></param>

  7. /// <param name="parPic"></param>

  8. /// <param name="searchRect">如果为empty,则默认查找整个图像</param>

  9. /// <param name="errorRange">容错,单个色值范围内视为正确0~255</param>

  10. /// <param name="matchRate">图片匹配度,默认90%</param>

  11. /// <param name="isFindAll">是否查找所有相似的图片</param>

  12. /// <returns>返回查找到的图片的中心点坐标</returns>

  13. List<System.Drawing.Point> FindPicture(string subPic, string parPic, System.Drawing.Rectangle searchRect, byte errorRange, double matchRate = 0.9, bool isFindAll = false)

  14. {

  15. List<System.Drawing.Point> ListPoint = new List<System.Drawing.Point>();

  16. var subBitmap = new Bitmap(subPic);

  17. var parBitmap = new Bitmap(parPic);

  18. int subWidth = subBitmap.Width;

  19. int subHeight = subBitmap.Height;

  20. int parWidth = parBitmap.Width;

  21. int parHeight = parBitmap.Height;

  22. if (searchRect.IsEmpty)

  23. {

  24. searchRect = new System.Drawing.Rectangle(0, 0, parBitmap.Width, parBitmap.Height);

  25. }

  26.  
  27. var searchLeftTop = searchRect.Location;

  28. var searchSize = searchRect.Size;

  29. System.Drawing.Color startPixelColor = subBitmap.GetPixel(0, 0);

  30. var subData = subBitmap.LockBits(new System.Drawing.Rectangle(0, 0, subBitmap.Width, subBitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

  31. var parData = parBitmap.LockBits(new System.Drawing.Rectangle(0, 0, parBitmap.Width, parBitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

  32. var byteArrarySub = new byte[subData.Stride * subData.Height];

  33. var byteArraryPar = new byte[parData.Stride * parData.Height];

  34. Marshal.Copy(subData.Scan0, byteArrarySub, 0, subData.Stride * subData.Height);

  35. Marshal.Copy(parData.Scan0, byteArraryPar, 0, parData.Stride * parData.Height);

  36.  
  37. var iMax = searchLeftTop.Y + searchSize.Height - subData.Height;//行

  38. var jMax = searchLeftTop.X + searchSize.Width - subData.Width;//列

  39.  
  40. int smallOffsetX = 0, smallOffsetY = 0;

  41. int smallStartX = 0, smallStartY = 0;

  42. int pointX = -1; int pointY = -1;

  43. for (int i = searchLeftTop.Y; i < iMax; i++)

  44. {

  45. for (int j = searchLeftTop.X; j < jMax; j++)

  46. {

  47. //大图x,y坐标处的颜色值

  48. int x = j, y = i;

  49. int parIndex = i * parWidth * 4 + j * 4;

  50. var colorBig = System.Drawing.Color.FromArgb(byteArraryPar[parIndex + 3], byteArraryPar[parIndex + 2], byteArraryPar[parIndex + 1], byteArraryPar[parIndex]);

  51. ;

  52. if (ColorAEqualColorB(colorBig, startPixelColor, errorRange))

  53. {

  54. smallStartX = x - smallOffsetX;//待找的图X坐标

  55. smallStartY = y - smallOffsetY;//待找的图Y坐标

  56. int sum = 0;//所有需要比对的有效点

  57. int matchNum = 0;//成功匹配的点

  58. for (int m = 0; m < subHeight; m++)

  59. {

  60. for (int n = 0; n < subWidth; n++)

  61. {

  62. int x1 = n, y1 = m;

  63. int subIndex = m * subWidth * 4 + n * 4;

  64. var color = System.Drawing.Color.FromArgb(byteArrarySub[subIndex + 3], byteArrarySub[subIndex + 2], byteArrarySub[subIndex + 1], byteArrarySub[subIndex]);

  65.  
  66. sum++;

  67. int x2 = smallStartX + x1, y2 = smallStartY + y1;

  68. int parReleativeIndex = y2 * parWidth * 4 + x2 * 4;//比对大图对应的像素点的颜色

  69. var colorPixel = System.Drawing.Color.FromArgb(byteArraryPar[parReleativeIndex + 3], byteArraryPar[parReleativeIndex + 2], byteArraryPar[parReleativeIndex + 1], byteArraryPar[parReleativeIndex]);

  70. if (ColorAEqualColorB(colorPixel, color, errorRange))

  71. {

  72. matchNum++;

  73. }

  74. }

  75. }

  76. if ((double)matchNum / sum >= matchRate)

  77. {

  78. Console.WriteLine((double)matchNum / sum);

  79. pointX = smallStartX + (int)(subWidth / 2.0);

  80. pointY = smallStartY + (int)(subHeight / 2.0);

  81. var point = new System.Drawing.Point(pointX, pointY);

  82. if (!ListContainsPoint(ListPoint, point, 10))

  83. {

  84. ListPoint.Add(point);

  85. }

  86. if (!isFindAll)

  87. {

  88. goto FIND_END;

  89. }

  90. }

  91. }

  92. //小图x1,y1坐标处的颜色值

  93. }

  94. }

  95. FIND_END:

  96. subBitmap.UnlockBits(subData);

  97. parBitmap.UnlockBits(parData);

  98. subBitmap.Dispose();

  99. parBitmap.Dispose();

  100. GC.Collect();

  101. return ListPoint;

  102. }

  103. #endregion

  

 
  1. private void Button_Click_Pic(object sender,RoutedEventArgs e)

  2. {

  3. string subPic = @"E:\2.png";

  4. string parPic = @"E:\1.png";

  5. ;

  6. List<System.Drawing.Point> List =FindPicture(subPic, parPic, System.Drawing.Rectangle.Empty, 100);

  7. foreach(System.Drawing.Point p in List)

  8. {

  9. Console.WriteLine(p.X.ToString());

  10. Console.WriteLine(p.Y.ToString());

  11. }

  12. Console.WriteLine("finish");

  13. }

  

  找字:

找字比较困难了呢,因为文字是一种镂空的图像,不像上述找的是非镂空图像,代码:

定义结构体:

 

 

 

 
  1. struct NumBody

  2. {

  3. public int num;//数字

  4. public int matchNum;//匹配的个数

  5. public int matchSum;

  6. public double matchRate;//匹配度

  7. public System.Drawing.Point point;

  8. public List<System.Drawing.Point> bodyCollectionPoint;//该数字所有像素在大图中的坐标

  9. }

  

 

 

 
  1. #region 找字

  2.  
  3. /// <summary>

  4. /// 找文字,镂空的图片文字

  5. /// </summary>

  6. /// <param name="subPic"></param>

  7. /// <param name="parPic"></param>

  8. /// <param name="searchRect"></param>

  9. /// <param name="errorRange"></param>

  10. /// <param name="matchRate"></param>

  11. /// <param name="isFindAll"></param>

  12. /// <returns></returns>

  13. List<NumBody> FindText(string subPic, string parPic, System.Drawing.Rectangle searchRect, byte errorRange, double matchRate = 0.9, bool isFindAll = false)

  14. {

  15.  
  16. List<NumBody> ListPoint = new List<NumBody>();

  17. var subBitmap = new Bitmap(subPic);

  18. var parBitmap = new Bitmap(parPic);

  19. int subWidth = subBitmap.Width;

  20. int subHeight = subBitmap.Height;

  21. int parWidth = parBitmap.Width;

  22. int parHeight = parBitmap.Height;

  23. var bgColor = subBitmap.GetPixel(0, 0);//背景红色

  24. if (searchRect.IsEmpty)

  25. {

  26. searchRect = new System.Drawing.Rectangle(0, 0, parBitmap.Width, parBitmap.Height);

  27. }

  28. var searchLeftTop = searchRect.Location;

  29. var searchSize = searchRect.Size;

  30. var subData = subBitmap.LockBits(new System.Drawing.Rectangle(0, 0, subBitmap.Width, subBitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

  31. var parData = parBitmap.LockBits(new System.Drawing.Rectangle(0, 0, parBitmap.Width, parBitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

  32. var byteArrarySub = new byte[subData.Stride * subData.Height];

  33. var byteArraryPar = new byte[parData.Stride * parData.Height];

  34. Marshal.Copy(subData.Scan0, byteArrarySub, 0, subData.Stride * subData.Height);

  35. Marshal.Copy(parData.Scan0, byteArraryPar, 0, parData.Stride * parData.Height);

  36. var iMax = searchLeftTop.Y + searchSize.Height - subData.Height;//行

  37. var jMax = searchLeftTop.X + searchSize.Width - subData.Width;//列

  38. System.Drawing.Color startPixelColor = System.Drawing.Color.FromArgb(0, 0, 0);

  39. int smallOffsetX = 0, smallOffsetY = 0;

  40. int smallStartX = 0, smallStartY = 0;

  41. int pointX = -1; int pointY = -1;

  42.  
  43.  
  44. for (int m = 0; m < subHeight; m++)

  45. {

  46. for (int n = 0; n < subWidth; n++)

  47. {

  48. smallOffsetX = n;

  49. smallOffsetY = m;

  50. int subIndex = m * subWidth * 4 + n * 4;

  51. var color = System.Drawing.Color.FromArgb(byteArrarySub[subIndex + 3], byteArrarySub[subIndex + 2], byteArrarySub[subIndex + 1], byteArrarySub[subIndex]);

  52. if (!ColorAEqualColorB(color, bgColor, errorRange))

  53. {

  54. startPixelColor = color;

  55. goto END;

  56. }

  57. }

  58. }

  59.  
  60. END:

  61. for (int i = searchLeftTop.Y; i < iMax; i++)

  62. {

  63. for (int j = searchLeftTop.X; j < jMax; j++)

  64. {

  65. //大图x,y坐标处的颜色值

  66. int x = j, y = i;

  67. int parIndex = i * parWidth * 4 + j * 4;

  68. var colorBig = System.Drawing.Color.FromArgb(byteArraryPar[parIndex + 3], byteArraryPar[parIndex + 2], byteArraryPar[parIndex + 1], byteArraryPar[parIndex]);

  69. ;

  70.  
  71. List<System.Drawing.Point> myListPoint = new List<System.Drawing.Point>();

  72. if (ColorAEqualColorB(colorBig, startPixelColor, errorRange))

  73. {

  74. smallStartX = x - smallOffsetX;//待找的图X坐标

  75. smallStartY = y - smallOffsetY;//待找的图Y坐标

  76. int sum = 0;//所有需要比对的有效点

  77. int matchNum = 0;//成功匹配的点

  78. for (int m = 0; m < subHeight; m++)

  79. {

  80. for (int n = 0; n < subWidth; n++)

  81. {

  82. int x1 = n, y1 = m;

  83. int subIndex = m * subWidth * 4 + n * 4;

  84. var color = System.Drawing.Color.FromArgb(byteArrarySub[subIndex + 3], byteArrarySub[subIndex + 2], byteArrarySub[subIndex + 1], byteArrarySub[subIndex]);

  85. if (color != bgColor)

  86. {

  87. sum++;

  88. int x2 = smallStartX + x1, y2 = smallStartY + y1;

  89. int parReleativeIndex = y2 * parWidth * 4 + x2 * 4;//比对大图对应的像素点的颜色

  90. var colorPixel = System.Drawing.Color.FromArgb(byteArraryPar[parReleativeIndex + 3], byteArraryPar[parReleativeIndex + 2], byteArraryPar[parReleativeIndex + 1], byteArraryPar[parReleativeIndex]);

  91. if (ColorAEqualColorB(colorPixel, color, errorRange))

  92. {

  93. matchNum++;

  94. }

  95. myListPoint.Add(new System.Drawing.Point(x2, y2));

  96. }

  97. }

  98. }

  99.  
  100. double rate = (double)matchNum / sum;

  101. if (rate>= matchRate)

  102. {

  103. Console.WriteLine((double)matchNum / sum);

  104. pointX = smallStartX + (int)(subWidth / 2.0);

  105. pointY = smallStartY + (int)(subHeight / 2.0);

  106. var point = new System.Drawing.Point(pointX, pointY);

  107. if (!ListTextBodyContainsPoint(ListPoint, point, 1))

  108. {

  109. ListPoint.Add(new NumBody() { point = point, matchNum = matchNum,matchSum=sum, matchRate = rate, bodyCollectionPoint = myListPoint });

  110. }

  111. SearchNumbersByMatchNum(ref ListPoint);

  112. if (!isFindAll)

  113. {

  114. goto FIND_END;

  115. }

  116. }

  117. }

  118. //小图x1,y1坐标处的颜色值

  119. }

  120. }

  121. FIND_END:

  122. subBitmap.UnlockBits(subData);

  123. parBitmap.UnlockBits(parData);

  124. subBitmap.Dispose();

  125. parBitmap.Dispose();

  126. GC.Collect();

  127. return ListPoint;

  128. }

  

 

特别注意:有了这个方法还是不能找到你要的文字的。要先处理文字,下面举例:

例如在这张图片上找到朋友的朋字的坐标位置:

 

1:打开你的PS,先将图片放大,看到像素方块为止,然后将朋字的范围圈选住,注意稍微比字圈选的大一点,像这样:

2:按住CTRL+C,然后CTRL+N,出现对话框:(教教大家使用PS^_^)

3:将背景内容选择透明,按确定,再按CTRL+V复制图像

4:将这个图片放大到看到像素为止,将所有非字体的位置全部用铅笔工具涂上同一种颜色,

5:涂完了之后将这张图片保存下来,这张图片就是我们要查找的“朋”字,图片是这样的

6:我们需要的就是第五步的图片和第一张底图,下面见证奇迹的时刻到了。

 

1          string str1 = @"C:\Users\JimmyBright\Desktop\1.png";
2             string str2 = @"C:\Users\JimmyBright\Desktop\2.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         }

复制代码

 

结束语:以上代码本人实现了找颜色,找图片,找文字,找数字的所有功能,希望对朋友们能有所帮助。

  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值