当前位置: 代码迷 >> C# >> C# 之银屏找图
  详细解决方案

C# 之银屏找图

热度:427   发布时间:2016-05-05 02:54:10.0
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毫秒,我就想啊,到底是哪里拖慢了速度呢。。。当然,没有想到。所以这里就抛砖引玉了。。。

  • 总结

    博客园的编辑器,每次我都感觉自己不会用,别人写的文章,编辑出来效果杠杠的,为什么我这个不行呢,感觉有点坑。

    最后,欢迎拍砖。

    谢谢支持。

 

3楼Supper_litt
最后又直接拿来了,@xiashengwang 同学提供的一个类,直接调用,感觉不错,只需要在前后处理一下,即可。,,http://www.cnblogs.com/xiashengwang/p/4225848.html,, public static Point ComparePic(Bitmap bigImage, Bitmap smallImage) { LockBitmap bigMap = new LockBitmap(bigImage); LockBitmap smallMap = new LockBitmap(smallImage); try { bigMap.LockBits(); smallMap.LockBits(); for (int i = 0; i lt; bigMap.Width; i++) { for (int j = 0; j lt; bigMap.Height; j++) { Color c1 = bigMap.GetPixel(i, j); Color c2 = smallMap.GetPixel(0, 0); // 颜色相等,且没有超出边界 if (Compare(c1, c2) amp;amp; bigMap.Width gt;= (i + smallMap.Width) amp;amp; bigMap.Height gt;= (j + smallMap.Height)) { bool iscontinue = false; for (int x = 0; x lt; smallMap.Width; x++) { for (int y = 0; y lt; smallMap.Height; y++) { Color c3 = smallMap.GetPixel(x, y); Color c4 = bigMap.GetPixel(i + x, j + y); if (!Compare(c3, c4)) { iscontinue = true; break;
2楼文楚
http://www.cnblogs.com/conmajia/archive/2012/07/12/fast-bitmap-benchmark.html
1楼乐水悠悠
GetPixel函数效率很慢,最好用LockBits函数获得BitmapData,然后通过System.Runtime.InteropServices.Marshal.Copy函数获取byte数组,对比数组会很快
Re: Supper_litt
@乐水悠悠,我试试。谢谢哈。
  相关解决方案