C#實現按鍵精靈的'找圖' '找色' '找字'的功能


背景:遊戲輔助功能一般使用按鍵精靈編寫腳本,按鍵精靈的最大賣點就是可以找到畫面中字,圖,色,這對於模擬用戶鼠標操做相當重要,這能找到道具,找到血量,實現自動打怪,自動補血,自動買賣道具,博主閒來無聊,看到一款按鍵精靈實現的輔助,因而乎想用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上下波動,下面還會有容錯的概念


找圖:html

在一張大圖中截取一個矩形小圖,而後在任意包含該小圖的圖片中找到該小圖的座標位置ide

#region 找圖函數


        /// <summary>工具

        /// 查找圖片,不能鏤空測試

        /// </summary>字體

        /// <param name="subPic"></param>spa

        /// <param name="parPic"></param>code

        /// <param name="searchRect">若是爲empty,則默認查找整個圖像</param>orm

        /// <param name="errorRange">容錯,單個色值範圍內視爲正確0~255</param>htm

        /// <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         }
複製代碼

 

結束語:以上代碼本人實現了找顏色,找圖片,找文字,找數字的全部功能,但願對朋友們能有所幫助。





相關文章
相關標籤/搜索