最近,由於工作上的某些原因,又要寫類似於外掛的程序,又要用到一個屏幕找圖功能,很多程序(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毫秒,我就想啊,到底是哪裡拖慢了速度呢。。。當然,沒有想到。所以這裡就拋磚引玉了。。。
博客園的編輯器,每次我都感覺自己不會用,別人寫的文章,編輯出來效果槓槓的,為什麼我這個不行呢,感覺有點坑。
最後,歡迎拍磚。
謝謝支持。