C# 之屏幕找图
- 引言
最近,由于工作上的某些原因,又要写类似于外挂的程序,又要用到一个屏幕找图功能,很多程序(eg:按键精灵)都提供了类似的功能,其实在这之前,我也查找过很多类似的C#方法,因为之前有一个试过没有用得起,所以最后就放弃了,知道现在都是使用的自己写的一个,相对来说,除了效率比较慢,没有太大的问题。不过就是由于效率不高,后面又想了其他的一些解决办法。
- 基础+贴代码。
因为是一些图片处理和操作,所以必不可少的会用到C# GDI+的一些基本知识,对于这个网上应该也有很多,大家可以拿来学习和参考。
再者,其实细细想一下,其实应该很简单,为什么呢,因为就是一个一个像素的比较,比较颜色差异,没有差异就通过,有差异,就继续查找,知道找到必须要,且完全匹配就OK。
于是乎有了下面的代码。1.0
// 基础代码和调用代码 (注释基本,略,后面又没有添加,多多包涵)
public class ImageManager
{
public static Point Compare(Bitmap bigImage, Bitmap smallImage)
{
for (int i = ; i < bigImage.Width; i++)
{
for (int j = ; j < bigImage.Height; j++)
{
Color c1 = bigImage.GetPixel(i, j);
Color c2 = smallImage.GetPixel(, ); // 颜色相等,且没有超出边界
if (Compare(c1, c2) && bigImage.Width >= (i + smallImage.Width) && bigImage.Height >= (j + smallImage.Height))
{
bool iscontinue = false;
for (int x = ; x < smallImage.Width; x++)
{
for (int y = ; y < smallImage.Height; y++)
{
Color c3 = smallImage.GetPixel(x, y);
Color c4 = bigImage.GetPixel(i + x, j + y);
if (!Compare(c3, c4))
{
iscontinue = true;
break;
}
} if (iscontinue)
{
break;
}
} if (!iscontinue)
{
return new Point(i, j);
}
}
}
} return new Point(-, -);
} private static bool Compare(Color c1, Color c2)
{
if (c1.A == c2.A && c1.R == c2.R && c1.B == c2.B && c1.G == c2.G)
{
return true;
} return false;
}
}
C# ImageManager 1.0
/// <summary>
/// 得到指定图片顶点
/// </summary>
/// <param name="picName">图片名称</param>
private Point GetPicturePoint(string picName)
{
Bitmap image = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics imgGraphics = Graphics.FromImage(image); //设置截屏区域
imgGraphics.CopyFromScreen(, , , , new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height)); // 然后从截屏图片中查找指定图片
string taskImagePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "image", picName);
Image img = Image.FromFile(taskImagePath); var result = ImageManager.Compare(CloseImg(image), CloseImg(img)); return result;
} private Bitmap CloneImg(Image img)
{
using (MemoryStream mostream = new MemoryStream())
{
Bitmap bmp = new Bitmap(img);
bmp.Save(mostream, System.Drawing.Imaging.ImageFormat.Jpeg);//将图像以指定的格式存入缓存内存流
byte[] bt = new byte[mostream.Length];
mostream.Position = ;//设置流的初始位置
mostream.Read(bt, , Convert.ToInt32(bt.Length)); return bmp;
}
}
ImageManager 调用方法
上面的CloseImg麻烦修改成CloneImg 写错了,多谢网友指出。
由于效率不敢恭维,没办法,又想其他的法子吧,于是乎想到了多线程。。
- 多线程处理,效率没啥子提升感觉。
由于代码的处理方式,造成了,循环太多,处理的比较的次数很多,运算量大。。
多线程怎么处理呢,于是想到了,把整个屏幕分成很多块小图片,这样,用小图片和要查找的图片进行比较然后得到最后的结果。但是问题来了,如果,图片正好在中间怎么办。于是就把小图片,朵切割一点,多切割,需要查找的图片的宽度和高度。
于是写成了代码,如下:
public class ImageManager
{
private static List<Point> result = new List<Point>(); public static event Action<int, Image> DoPic; private static int width = ; private static int height = ; /// <summary>
/// 多线程找图
/// </summary>
/// <param name="bigImage"></param>
/// <param name="smallImage"></param>
/// <returns></returns>
public static Point ThreadCompare(Bitmap bigImage, Bitmap smallImage)
{
result = new List<Point>();
// 先拆分大图成为16个小图片,每个小图片都需要加上smallImage的长宽组成一个新图片
// 需要16个线程来完成。
width = (int)Math.Ceiling(bigImage.Width / 4.0);
height = (int)Math.Ceiling(bigImage.Height / 4.0);
int maxWidth = width + smallImage.Width;
int maxHeight = height + smallImage.Height;
int index = ;
for (int i = ; i < ; i++)
{
for (int j = ; j < ; j++)
{
Bitmap bitMap = null;
if (i == && j == )
{
bitMap = new Bitmap(width, height);
}
else if (j == )
{
bitMap = new Bitmap(maxWidth, height);
}
else if (i == )
{
bitMap = new Bitmap(width, maxWidth);
}
else
{
bitMap = new Bitmap(maxWidth, maxHeight);
} Graphics resultG = Graphics.FromImage(bitMap);
resultG.DrawImage(bigImage, new Rectangle(, , bitMap.Width, bitMap.Height), new Rectangle(i * width, j * height, bitMap.Width, bitMap.Height), GraphicsUnit.Pixel);
resultG.Dispose(); if (DoPic != null)
{
DoPic(index, CloneImg(bitMap));
} ThreadPool.QueueUserWorkItem(new WaitCallback(CompareThread), new object[] { bitMap, CloneImg(smallImage), i, j });
index++;
}
} while (result.Count != )
{
Thread.Sleep();
} var point = new Point(-, -);
if (result.Exists(p => p.X >= ))
{
point = result.Find(a => a.X >= );
} return point;
} public static Point Compare(Bitmap bigImage, Bitmap smallImage)
{
for (int i = ; i < bigImage.Width; i++)
{
for (int j = ; j < bigImage.Height; j++)
{
Color c1 = bigImage.GetPixel(i, j);
Color c2 = smallImage.GetPixel(, ); // 颜色相等,且没有超出边界
if (Compare(c1, c2) && bigImage.Width >= (i + smallImage.Width) && bigImage.Height >= (j + smallImage.Height))
{
bool iscontinue = false;
for (int x = ; x < smallImage.Width; x++)
{
for (int y = ; y < smallImage.Height; y++)
{
Color c3 = smallImage.GetPixel(x, y);
Color c4 = bigImage.GetPixel(i + x, j + y);
if (!Compare(c3, c4))
{
iscontinue = true;
break;
}
} if (iscontinue)
{
break;
}
} if (!iscontinue)
{
return new Point(i, j);
}
}
}
} return new Point(-, -);
} private static void CompareThread(object obj)
{
object[] objs = obj as object[];
Bitmap bigImage = objs[] as Bitmap;
Bitmap smallImage = objs[] as Bitmap;
int indexI = Convert.ToInt32(objs[]);
int indexJ = Convert.ToInt32(objs[]);
bool isbreak = false;
Point p = new Point(-, -);
for (int i = ; i < bigImage.Width; i++)
{
for (int j = ; j < bigImage.Height; j++)
{
Color c1 = bigImage.GetPixel(i, j);
Color c2 = smallImage.GetPixel(, ); // 颜色相等,且没有超出边界
if (Compare(c1, c2) && bigImage.Width >= (i + smallImage.Width) && bigImage.Height >= (j + smallImage.Height))
{
bool iscontinue = false;
for (int x = ; x < smallImage.Width; x++)
{
for (int y = ; y < smallImage.Height; y++)
{
Color c3 = smallImage.GetPixel(x, y);
Color c4 = bigImage.GetPixel(i + x, j + y);
if (!Compare(c3, c4))
{
iscontinue = true;
break;
}
} if (iscontinue)
{
break;
}
} if (!iscontinue)
{
isbreak = true;
p = new Point(i + indexI * width, j + indexJ * height);
break;
}
}
} if (isbreak)
{
break;
}
} result.Add(p);
} private static bool Compare(Color c1, Color c2)
{
if (c1.A == c2.A && c1.R == c2.R && c1.B == c2.B && c1.G == c2.G)
{
return true;
} return false;
} private static Bitmap CloneImg(Image img)
{
using (MemoryStream mostream = new MemoryStream())
{
Bitmap bmp = new Bitmap(img);
bmp.Save(mostream, System.Drawing.Imaging.ImageFormat.Jpeg);//将图像以指定的格式存入缓存内存流
byte[] bt = new byte[mostream.Length];
mostream.Position = ;//设置留的初始位置
mostream.Read(bt, , Convert.ToInt32(bt.Length)); return bmp;
}
}
}
ImageManager 2.0
终于支持多线程了,然后测试了一下,效率略有增加,不过没有太大的感觉。但是用别人的工具,感觉特别快,因为软件上面写的50,60毫秒,我就想啊,到底是哪里拖慢了速度呢。。。当然,没有想到。所以这里就抛砖引玉了。。。
- 总结
博客园的编辑器,每次我都感觉自己不会用,别人写的文章,编辑出来效果杠杠的,为什么我这个不行呢,感觉有点坑。
最后,欢迎拍砖。
谢谢支持。
C# 之屏幕找图的更多相关文章
- Delphi下实现全屏快速找图找色
前言 最近有好几个朋友都在问我找图找色的问题,奇怪?于是乎写了一个专门用于找图找色的单元文件“BitmapData.pas”.在这个单元文件中我实现了从文件中导入位图.屏幕截图.鼠标指针截图.在图片上 ...
- C#实现按键精灵的'找图' '找色' '找字'的功能
http://www.cnblogs.com/JimmyBright/p/4355862.html 背景:游戏辅助功能通常使用按键精灵编写脚本,按键精灵的最大卖点就是能够找到画面中字,图,色,这对于模 ...
- Python实现按键精灵(二)-找图找色
一.实现功能 判断在指定坐标范围内,是否存在相似度大于n的图片,并返回坐标. 二.基本思路 A=你需要寻找的图片 B=截取当前页面中指定范围的图片 利用opencv 判断A在B中的位置, 在该位置截取 ...
- C#实现按键精灵的'找图' '找色' '找字'的功能
背景:游戏辅助功能通常使用按键精灵编写脚本,按键精灵的最大卖点就是能够找到画面中字,图,色,这对于模拟用户鼠标操作至关重要,这能找到道具,找到血量,实现自动打怪,自动补血,自动买卖道具,博主闲来无聊, ...
- python从图片中找图
import aircv as ac def matcha(bb,aa):#从bb查找aa,如果有则返回其坐标位置 yuan=ac.imread(bb) mubi=ac.imread(aa) resu ...
- 第三篇T语言实例开发,图色操作
---恢复内容开始--- 图色的基本操作 1.找颜色色命令的基本操作 坐标点取色:获取指定坐标点的颜色 区域找色:在指定区域里找某一个颜色 模糊找色:在指定区域里找某一个颜色,可以设置相似度 多点找色 ...
- 屏幕分辨率与FPS
屏幕分辨率 刷新率分为垂直刷新率和水平刷新率,一般提到的刷新率通常指垂直刷新率. 垂直刷新率表示屏幕的图象每秒钟重绘多少次,也就是每秒钟屏幕刷新的次数,以Hz(赫兹)为单位. 刷新率越高越好,图象就越 ...
- [转]SAP中找表的方法
http://blog.chinaunix.net/uid-24063584-id-2642334.html 分类: 18种根据屏幕字段查找数据库表数据的技巧 帮助 18种根据屏幕字段查找潜在数据 ...
- 在屏幕上搜索图片并返回图片所在位置的坐标的AutoHotkey脚本源代码(类似大漠插件)
;~ 在屏幕上搜索图片并返回图片所在位置的坐标的AutoHotkey脚本源代码(类似大漠插件) ; https://www.autohotkey.com/boards/viewtopic.php?t ...
随机推荐
- Multiple Contexts have a path of "/xxxx"问题解决
转自:http://blog.csdn.net/ivanzhangqf/article/details/50326533 之前因为ecplise项目保存更新时一直显示等待后台操作完成,以至于一直卡住无 ...
- nohup启动java命令导致dubbo无法注册
没有任何异常,日志和正常注册日志一模一样,但服务就是没注册成功. 解决办法,screen代替nohup命令. screen screen -ls screen -r pid (ctrl + a)+d
- 11款样式新颖的 jQuery/CSS3 网页菜单
今天为大家准备了11款样式风格挺不错的jQuery/CSS3网页菜单,主要包括面包屑菜单.下拉菜单.Tab菜单等,喜欢的朋友赶紧收藏,一起来看看这些菜单. 1.jQuery / CSS3多功能下拉菜单 ...
- 数据库必会必知 之 SQL四种语言:DDL DML DCL TCL
作者:泥瓦匠 今天群里面讨论,DDL 还是 DML,我这种小白还是总结下他们的区别吧. 1. DDL - Data Definition Language 数据库定义语言:定义数据库的结构. 其主要命 ...
- VSFTPD添加用户
VSFTPD的安装网上有很多教程这里就不多说了,这里主要是针对做主机空间服务的朋友在安装好vsftpd后如何为用户增加ftp账号 先来看一看我们一般在*inux系统下面如何增加用户的 #adduser ...
- C2C,B2C,F2C三种电商运营模式的比较
第三方模式(C2C) 销售商模式(B2C) 生产商模式(F2C) 概念及简介 第三方平台提供商模式是电子商务的最原始也是最自然的形式.这种模式一般都是由信息技术开发商负责建立平台,利用平台扩展电子 ...
- [转]使用ant让Android自动打包的build.xml,自动生成签名的apk文件(支持android4.0以上的版本)
在android4.0以后的sdk里那个脚本就失效了,主要是因为 apkbuilder这个程序不见了: 人家sdk升级,我们的脚本也要跟上趟,修改一下喽. 上网一查,大家的文章还停留在我去年的脚本程度 ...
- Google首席软件工程师Joshua Bloch谈如何设计一款优秀的API【附PPT】
编者按]随着近来软件规模的日益庞大,API编程接口的设计变的越来越重要.良好的接口设计可以降低系统各部分之间的相互依赖,提高组成单元的内聚性,降低组成单元间的耦合度,从而提高系统的维护性和稳定性. J ...
- [C#]LDAP验证用户名和密码
测试环境:VS2008, NET Framework 3.5 公司打算改用LDAP来存储用户名和密码,现在用C#测试下如何能拿到LDAP中的用户名,并检测用户密码是否正确.即输入用户名和密码,可以检验 ...
- [转]Android dex分包方案
转载自:https://m.oschina.net/blog/308583 当一个app的功能越来越复杂,代码量越来越多,也许有一天便会突然遇到下列现象: 1. 生成的apk在2.3以前的机器无法安装 ...