博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#实现按键精灵的'找图' '找色' '找字'的功能
阅读量:5888 次
发布时间:2019-06-19

本文共 21724 字,大约阅读时间需要 72 分钟。

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

  找色:

1         ///  2         /// 找颜色 3         ///  4         /// 查找的图片的绝对路径 5         /// 查找的16进制颜色值,如#0C5FAB 6         /// 查找的矩形区域范围内 7         /// 容错 8         /// 
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         ///   4         /// 查找图片,不能镂空  5         ///   6         ///   7         ///   8         /// 如果为empty,则默认查找整个图像  9         /// 容错,单个色值范围内视为正确0~255 10         /// 图片匹配度,默认90% 11         /// 是否查找所有相似的图片 12         /// 
返回查找到的图片的中心点坐标
13 List
FindPicture(string subPic, string parPic, System.Drawing.Rectangle searchRect, byte errorRange, double matchRate = 0.9, bool isFindAll = false) 14 { 15 List
ListPoint = new List
(); 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         struct NumBody2         {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
bodyCollectionPoint;//该数字所有像素在大图中的坐标9 }

 

1    #region 找字  2   3         ///   4         /// 找文字,镂空的图片文字  5         ///   6         ///   7         ///   8         ///   9         ///  10         ///  11         ///  12         /// 
13 List
FindText(string subPic, string parPic, System.Drawing.Rectangle searchRect, byte errorRange, double matchRate = 0.9, bool isFindAll = false) 14 { 15 16 List
ListPoint = new List
(); 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
myListPoint = new List
(); 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         ///  4         /// 在指定区域里面查找数字 5         ///  6         ///  7         ///  8         ///  9         /// 10         /// 
11 int FindNumbers(Dictionary
numDic, string parPic, System.Drawing.Rectangle searchRect, byte errorRange=8, double matchRate = 0.9)12 {13 //同一个区域找到多个相同的图片14 List
ListBody = new List
();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 ///
35 /// 搜索同一个数字的时候,出现重叠的地方,用匹配度去过滤掉匹配度低的36 /// 比如同样是1,在控制匹配度允许下,一个(83,95)和(84,95)这两个点明显是同一个数字37 /// 此时谁的匹配度低过滤掉谁38 /// 39 ///
40 void SearchNumbersByMatchNum(ref List
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 else76 {77 if (bodyX.matchNum >= body.matchNum)//图像包含的所有像素个数78 {79 ListBody.Remove(body);80 }81 else82 {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
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
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 }

 

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

 

转载地址:http://shrix.baihongyu.com/

你可能感兴趣的文章
安装cobbler
查看>>
第3章 方法的重载及参数传递
查看>>
多选下拉相互切换
查看>>
SSH服务详解
查看>>
小程序--时间处理(显示几分钟前,,几小时前,,几天前...)
查看>>
23种设计模式介绍(三)---- 行为型模式
查看>>
项目owner看这里,MaxCompute全表扫描新功能,给你“失误”的机会
查看>>
2018-07-16笔记(tomcat 配置)
查看>>
用框架思维解读生活目标
查看>>
selinux
查看>>
ci完整集成
查看>>
深度学习目标检测(object detection)系列(二) SPP-Net
查看>>
Python类、模块、包的概念及区别
查看>>
FreeMarker笔记 第四章 其它
查看>>
Oracle 11g 新特性简介(一)
查看>>
详解Oracle的几种分页查询语句
查看>>
从零部署RHEV3.3红帽虚拟化-2 (用kvm虚拟机安装RHEL6.4)
查看>>
Varnish 3.X详解
查看>>
javascript继承方式详解
查看>>
lnmp环境安装sh脚本
查看>>