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 調用方法
由於效率不敢恭維,沒辦法,又想其他的法子吧,於是乎想到了多線程。。
由於代碼的處理方式,造成了,循環太多,處理的比較的次數很多,運算量大。。
多線程怎麼處理呢,於是想到了,把整個屏幕分成很多塊小圖片,這樣,用小圖片和要查找的圖片進行比較然後得到最後的結果。但是問題來了,如果,圖片正好在中間怎麼辦。於是就把小圖片,朵切割一點,多切割,需要查找的圖片的寬度和高度。
於是寫成了代碼,如下:

![]()
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毫秒,我就想啊,到底是哪裡拖慢了速度呢。。。當然,沒有想到。所以這裡就拋磚引玉了。。。
博客園的編輯器,每次我都感覺自己不會用,別人寫的文章,編輯出來效果槓槓的,為什麼我這個不行呢,感覺有點坑。
最後,歡迎拍磚。
謝謝支持。