• 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毫秒,我就想啊,到底是哪里拖慢了速度呢。。。当然,没有想到。所以这里就抛砖引玉了。。。

    • 总结

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

        最后,欢迎拍砖。

        谢谢支持。

  • 相关阅读:
    java期末复习2
    java期末复习
    Educational Codeforces Round 76 (Rated for Div. 2)
    ICPC南昌时间安排
    codeforces 597 div2 ABCDF
    codeforces 597 div2 ABC
    Vue中provide和inject 用法
    Js打印九九乘法表
    document.documentElement和document.body的区别
    移动端关于横屏问题
  • 原文地址:https://www.cnblogs.com/Supperlitt/p/4810891.html
Copyright © 2020-2023  润新知