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

找色:
/// <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

找字:

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

定义结构体:

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 }

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



最新文章

  1. UIAutomator
  2. 【转】C#多线程Lock使用
  3. pthreads多线程数据采集
  4. other 遮罩层
  5. stuff about set multiset map multimap
  6. Android 自定义View修炼-自定义可动画展开收缩View的实现
  7. CSS Hack (各个浏览器兼容的问题)
  8. 了解了解你自己的话zookeeper(从那时起,纠正了一些说法在线)
  9. 学习SQL关联查询
  10. svg滤镜学习
  11. 解读JavaScript原型链
  12. UNIX环境高级编程——Linux系统调用列表
  13. [c/c++] programming之路(27)、union共用体
  14. 网易云通过KCSP认证,云原生技术实力再获认可
  15. Android 方法数超过64k、编译OOM、编译过慢解决方案。
  16. PHP关联查询
  17. JavaScript深入之词法作用域和动态作用域
  18. mobx 入门
  19. Linux 系统下 matplotlib 中文乱码解决办法
  20. [UE4 ]Is Locally Controlled的局限性

热门文章

  1. oracle 存储过程的一个小问题
  2. Spark导论(Spark自学一)
  3. Accessing Report Server using Report Server Web Service - Microsoft SQL Server 2008R2
  4. [Angular] Angular Attribute Decorator - When to use it?
  5. Android SlidingMenu 使用具体解释
  6. J2EE规范
  7. 两列布局(浮动、定位、flex)和三列布局(圣杯、双飞翼、flex)
  8. 倍福TwinCAT(贝福Beckhoff)常见问题(FAQ)-人机界面快速入门 TC3
  9. php 基础入门篇之前言
  10. Python——标准库 Sys模块