C# 之屏幕找圖


  • 引言

    最近,由於工作上的某些原因,又要寫類似於外掛的程序,又要用到一個屏幕找圖功能,很多程序(eg:按鍵精靈)都提供了類似的功能,其實在這之前,我也查找過很多類似的C#方法,因為之前有一個試過沒有用得起,所以最后就放棄了,知道現在都是使用的自己寫的一個,相對來說,除了效率比較慢,沒有太大的問題。不過就是由於效率不高,后面又想了其他的一些解決辦法。

  • 基礎+貼代碼。

    因為是一些圖片處理和操作,所以必不可少的會用到C# GDI+的一些基本知識,對於這個網上應該也有很多,大家可以拿來學習和參考。

    再者,其實細細想一下,其實應該很簡單,為什么呢,因為就是一個一個像素的比較,比較顏色差異,沒有差異就通過,有差異,就繼續查找,知道找到必須要,且完全匹配就OK。

    於是乎有了下面的代碼。1.0

  // 基礎代碼和調用代碼 (注釋基本,略,后面又沒有添加,多多包涵)

 1 public class ImageManager
 2     {
 3         public static Point Compare(Bitmap bigImage, Bitmap smallImage)
 4         {
 5             for (int i = 0; i < bigImage.Width; i++)
 6             {
 7                 for (int j = 0; j < bigImage.Height; j++)
 8                 {
 9                     Color c1 = bigImage.GetPixel(i, j);
10                     Color c2 = smallImage.GetPixel(0, 0);
11 
12                     // 顏色相等,且沒有超出邊界
13                     if (Compare(c1, c2) && bigImage.Width >= (i + smallImage.Width) && bigImage.Height >= (j + smallImage.Height))
14                     {
15                         bool iscontinue = false;
16                         for (int x = 0; x < smallImage.Width; x++)
17                         {
18                             for (int y = 0; y < smallImage.Height; y++)
19                             {
20                                 Color c3 = smallImage.GetPixel(x, y);
21                                 Color c4 = bigImage.GetPixel(i + x, j + y);
22                                 if (!Compare(c3, c4))
23                                 {
24                                     iscontinue = true;
25                                     break;
26                                 }
27                             }
28 
29                             if (iscontinue)
30                             {
31                                 break;
32                             }
33                         }
34 
35                         if (!iscontinue)
36                         {
37                             return new Point(i, j);
38                         }
39                     }
40                 }
41             }
42 
43             return new Point(-1, -1);
44         }
45 
46         private static bool Compare(Color c1, Color c2)
47         {
48             if (c1.A == c2.A && c1.R == c2.R && c1.B == c2.B && c1.G == c2.G)
49             {
50                 return true;
51             }
52 
53             return false;
54         }
55     }
C# ImageManager 1.0
 1     /// <summary>
 2         /// 得到指定圖片頂點
 3         /// </summary>
 4         /// <param name="picName">圖片名稱</param>
 5         private Point GetPicturePoint(string picName)
 6         {
 7             Bitmap image = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
 8             Graphics imgGraphics = Graphics.FromImage(image);
 9 
10             //設置截屏區域 
11             imgGraphics.CopyFromScreen(0, 0, 0, 0, new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height));
12 
13             // 然后從截屏圖片中查找指定圖片
14             string taskImagePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "image", picName);
15             Image img = Image.FromFile(taskImagePath);
16 
17             var result = ImageManager.Compare(CloseImg(image), CloseImg(img));
18 
19             return result;
20         }
21 
22         private Bitmap CloneImg(Image img)
23         {
24             using (MemoryStream mostream = new MemoryStream())
25             {
26                 Bitmap bmp = new Bitmap(img);
27                 bmp.Save(mostream, System.Drawing.Imaging.ImageFormat.Jpeg);//將圖像以指定的格式存入緩存內存流
28                 byte[] bt = new byte[mostream.Length];
29                 mostream.Position = 0;//設置流的初始位置
30                 mostream.Read(bt, 0, Convert.ToInt32(bt.Length));
31 
32                 return bmp;
33             }
34         }
ImageManager 調用方法

  上面的CloseImg麻煩修改成CloneImg 寫錯了,多謝網友指出。

    由於效率不敢恭維,沒辦法,又想其他的法子吧,於是乎想到了多線程。。

  • 多線程處理,效率沒啥子提升感覺。

    由於代碼的處理方式,造成了,循環太多,處理的比較的次數很多,運算量大。。

    多線程怎么處理呢,於是想到了,把整個屏幕分成很多塊小圖片,這樣,用小圖片和要查找的圖片進行比較然后得到最后的結果。但是問題來了,如果,圖片正好在中間怎么辦。於是就把小圖片,朵切割一點,多切割,需要查找的圖片的寬度和高度。

    於是寫成了代碼,如下:

  1 public class ImageManager
  2     {
  3         private static List<Point> result = new List<Point>();
  4 
  5         public static event Action<int, Image> DoPic;
  6 
  7         private static int width = 0;
  8 
  9         private static int height = 0;
 10 
 11         /// <summary>
 12         /// 多線程找圖
 13         /// </summary>
 14         /// <param name="bigImage"></param>
 15         /// <param name="smallImage"></param>
 16         /// <returns></returns>
 17         public static Point ThreadCompare(Bitmap bigImage, Bitmap smallImage)
 18         {
 19             result = new List<Point>();
 20             // 先拆分大圖成為16個小圖片,每個小圖片都需要加上smallImage的長寬組成一個新圖片
 21             // 需要16個線程來完成。
 22             width = (int)Math.Ceiling(bigImage.Width / 4.0);
 23             height = (int)Math.Ceiling(bigImage.Height / 4.0);
 24             int maxWidth = width + smallImage.Width;
 25             int maxHeight = height + smallImage.Height;
 26             int index = 0;
 27             for (int i = 0; i < 4; i++)
 28             {
 29                 for (int j = 0; j < 4; j++)
 30                 {
 31                     Bitmap bitMap = null;
 32                     if (i == 3 && j == 3)
 33                     {
 34                         bitMap = new Bitmap(width, height);
 35                     }
 36                     else if (j == 3)
 37                     {
 38                         bitMap = new Bitmap(maxWidth, height);
 39                     }
 40                     else if (i == 3)
 41                     {
 42                         bitMap = new Bitmap(width, maxWidth);
 43                     }
 44                     else
 45                     {
 46                         bitMap = new Bitmap(maxWidth, maxHeight);
 47                     }
 48 
 49                     Graphics resultG = Graphics.FromImage(bitMap);
 50                     resultG.DrawImage(bigImage, new Rectangle(0, 0, bitMap.Width, bitMap.Height), new Rectangle(i * width, j * height, bitMap.Width, bitMap.Height), GraphicsUnit.Pixel);
 51                     resultG.Dispose();
 52 
 53                     if (DoPic != null)
 54                     {
 55                         DoPic(index, CloneImg(bitMap));
 56                     }
 57 
 58                     ThreadPool.QueueUserWorkItem(new WaitCallback(CompareThread), new object[] { bitMap, CloneImg(smallImage), i, j });
 59                     index++;
 60                 }
 61             }
 62 
 63             while (result.Count != 16)
 64             {
 65                 Thread.Sleep(50);
 66             }
 67 
 68             var point = new Point(-1, -1);
 69             if (result.Exists(p => p.X >= 0))
 70             {
 71                 point = result.Find(a => a.X >= 0);
 72             }
 73 
 74             return point;
 75         }
 76 
 77         public static Point Compare(Bitmap bigImage, Bitmap smallImage)
 78         {
 79             for (int i = 0; i < bigImage.Width; i++)
 80             {
 81                 for (int j = 0; j < bigImage.Height; j++)
 82                 {
 83                     Color c1 = bigImage.GetPixel(i, j);
 84                     Color c2 = smallImage.GetPixel(0, 0);
 85 
 86                     // 顏色相等,且沒有超出邊界
 87                     if (Compare(c1, c2) && bigImage.Width >= (i + smallImage.Width) && bigImage.Height >= (j + smallImage.Height))
 88                     {
 89                         bool iscontinue = false;
 90                         for (int x = 0; x < smallImage.Width; x++)
 91                         {
 92                             for (int y = 0; y < smallImage.Height; y++)
 93                             {
 94                                 Color c3 = smallImage.GetPixel(x, y);
 95                                 Color c4 = bigImage.GetPixel(i + x, j + y);
 96                                 if (!Compare(c3, c4))
 97                                 {
 98                                     iscontinue = true;
 99                                     break;
100                                 }
101                             }
102 
103                             if (iscontinue)
104                             {
105                                 break;
106                             }
107                         }
108 
109                         if (!iscontinue)
110                         {
111                             return new Point(i, j);
112                         }
113                     }
114                 }
115             }
116 
117             return new Point(-1, -1);
118         }
119 
120         private static void CompareThread(object obj)
121         {
122             object[] objs = obj as object[];
123             Bitmap bigImage = objs[0] as Bitmap;
124             Bitmap smallImage = objs[1] as Bitmap;
125             int indexI = Convert.ToInt32(objs[2]);
126             int indexJ = Convert.ToInt32(objs[3]);
127             bool isbreak = false;
128             Point p = new Point(-1, -1);
129             for (int i = 0; i < bigImage.Width; i++)
130             {
131                 for (int j = 0; j < bigImage.Height; j++)
132                 {
133                     Color c1 = bigImage.GetPixel(i, j);
134                     Color c2 = smallImage.GetPixel(0, 0);
135 
136                     // 顏色相等,且沒有超出邊界
137                     if (Compare(c1, c2) && bigImage.Width >= (i + smallImage.Width) && bigImage.Height >= (j + smallImage.Height))
138                     {
139                         bool iscontinue = false;
140                         for (int x = 0; x < smallImage.Width; x++)
141                         {
142                             for (int y = 0; y < smallImage.Height; y++)
143                             {
144                                 Color c3 = smallImage.GetPixel(x, y);
145                                 Color c4 = bigImage.GetPixel(i + x, j + y);
146                                 if (!Compare(c3, c4))
147                                 {
148                                     iscontinue = true;
149                                     break;
150                                 }
151                             }
152 
153                             if (iscontinue)
154                             {
155                                 break;
156                             }
157                         }
158 
159                         if (!iscontinue)
160                         {
161                             isbreak = true;
162                             p = new Point(i + indexI * width, j + indexJ * height);
163                             break;
164                         }
165                     }
166                 }
167 
168                 if (isbreak)
169                 {
170                     break;
171                 }
172             }
173 
174             result.Add(p);
175         }
176 
177         private static bool Compare(Color c1, Color c2)
178         {
179             if (c1.A == c2.A && c1.R == c2.R && c1.B == c2.B && c1.G == c2.G)
180             {
181                 return true;
182             }
183 
184             return false;
185         }
186 
187         private static Bitmap CloneImg(Image img)
188         {
189             using (MemoryStream mostream = new MemoryStream())
190             {
191                 Bitmap bmp = new Bitmap(img);
192                 bmp.Save(mostream, System.Drawing.Imaging.ImageFormat.Jpeg);//將圖像以指定的格式存入緩存內存流
193                 byte[] bt = new byte[mostream.Length];
194                 mostream.Position = 0;//設置留的初始位置
195                 mostream.Read(bt, 0, Convert.ToInt32(bt.Length));
196 
197                 return bmp;
198             }
199         }
200     }
ImageManager 2.0

    終於支持多線程了,然后測試了一下,效率略有增加,不過沒有太大的感覺。但是用別人的工具,感覺特別快,因為軟件上面寫的50,60毫秒,我就想啊,到底是哪里拖慢了速度呢。。。當然,沒有想到。所以這里就拋磚引玉了。。。

  • 總結

    博客園的編輯器,每次我都感覺自己不會用,別人寫的文章,編輯出來效果杠杠的,為什么我這個不行呢,感覺有點坑。

    最后,歡迎拍磚。

    謝謝支持。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM