之前用按键精灵写过一些游戏辅助,里面有个函数叫FindPic,就是在屏幕范围查找给定的一张图片,返回查找到的坐标位置。

  现在,Java来实现这个函数类似的功能。

  算法描述:

  1. 屏幕截图,得到图A,(查找的目标图片为图B);
  2. 遍历图A的像素点,根据图B的尺寸,得到图B四个角映射到图A上的四个点;
  3. 得到的四个点与图B的四个角像素点的值比较。如果四个点一样,执行步骤4;否则,回到步骤2继续;
  4. 进一步对比,将映射范围内的全部点与图B全部的点比较。如果全部一样,则说明图片已找到;否则,回到步骤2继续;

  这里,像素之间的比较是通过BufferedImage对象获取每个像素的RGB值来比较的。如下,将BufferedImage转换为int二维数组:

     /**
* 根据BufferedImage获取图片RGB数组
* @param bfImage
* @return
*/
public static int[][] getImageGRB(BufferedImage bfImage) {
int width = bfImage.getWidth();
int height = bfImage.getHeight();
int[][] result = new int[height][width];
for (int h = 0; h < height; h++) {
for (int w = 0; w < width; w++) {
//使用getRGB(w, h)获取该点的颜色值是ARGB,而在实际应用中使用的是RGB,所以需要将ARGB转化成RGB,即bufImg.getRGB(w, h) & 0xFFFFFF。
result[h][w] = bfImage.getRGB(w, h) & 0xFFFFFF;
}
}
return result;
}

  比较两个像素点的RGB值是否相同,是通过异或操作比较的(据说比==效率更高),如果异或操作后得到的值为0,说明两个像素点的RGB一样,否则不一样。

  下面附上算法完整java代码:

 package com.jebysun.test.imagefind;

 import java.awt.AWTException;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException; import javax.imageio.ImageIO;
/**
* 屏幕上查找指定图片
* @author Jeby Sun
* @date 2014-09-13
* @website http://www.jebysun.com
*/
public class ImageFindDemo { BufferedImage screenShotImage; //屏幕截图
BufferedImage keyImage; //查找目标图片 int scrShotImgWidth; //屏幕截图宽度
int scrShotImgHeight; //屏幕截图高度 int keyImgWidth; //查找目标图片宽度
int keyImgHeight; //查找目标图片高度 int[][] screenShotImageRGBData; //屏幕截图RGB数据
int[][] keyImageRGBData; //查找目标图片RGB数据 int[][][] findImgData; //查找结果,目标图标位于屏幕截图上的坐标数据 public ImageFindDemo(String keyImagePath) {
screenShotImage = this.getFullScreenShot();
keyImage = this.getBfImageFromPath(keyImagePath);
screenShotImageRGBData = this.getImageGRB(screenShotImage);
keyImageRGBData = this.getImageGRB(keyImage);
scrShotImgWidth = screenShotImage.getWidth();
scrShotImgHeight = screenShotImage.getHeight();
keyImgWidth = keyImage.getWidth();
keyImgHeight = keyImage.getHeight(); //开始查找
this.findImage(); } /**
* 全屏截图
* @return 返回BufferedImage
*/
public BufferedImage getFullScreenShot() {
BufferedImage bfImage = null;
int width = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
int height = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
try {
Robot robot = new Robot();
bfImage = robot.createScreenCapture(new Rectangle(0, 0, width, height));
} catch (AWTException e) {
e.printStackTrace();
}
return bfImage;
} /**
* 从本地文件读取目标图片
* @param keyImagePath - 图片绝对路径
* @return 本地图片的BufferedImage对象
*/
public BufferedImage getBfImageFromPath(String keyImagePath) {
BufferedImage bfImage = null;
try {
bfImage = ImageIO.read(new File(keyImagePath));
} catch (IOException e) {
e.printStackTrace();
}
return bfImage;
} /**
* 根据BufferedImage获取图片RGB数组
* @param bfImage
* @return
*/
public int[][] getImageGRB(BufferedImage bfImage) {
int width = bfImage.getWidth();
int height = bfImage.getHeight();
int[][] result = new int[height][width];
for (int h = 0; h < height; h++) {
for (int w = 0; w < width; w++) {
//使用getRGB(w, h)获取该点的颜色值是ARGB,而在实际应用中使用的是RGB,所以需要将ARGB转化成RGB,即bufImg.getRGB(w, h) & 0xFFFFFF。
result[h][w] = bfImage.getRGB(w, h) & 0xFFFFFF;
}
}
return result;
} /**
* 查找图片
*/
public void findImage() {
findImgData = new int[keyImgHeight][keyImgWidth][2];
//遍历屏幕截图像素点数据
for(int y=0; y<scrShotImgHeight-keyImgHeight; y++) {
for(int x=0; x<scrShotImgWidth-keyImgWidth; x++) {
//根据目标图的尺寸,得到目标图四个角映射到屏幕截图上的四个点,
//判断截图上对应的四个点与图B的四个角像素点的值是否相同,
//如果相同就将屏幕截图上映射范围内的所有的点与目标图的所有的点进行比较。
if((keyImageRGBData[0][0]^screenShotImageRGBData[y][x])==0
&& (keyImageRGBData[0][keyImgWidth-1]^screenShotImageRGBData[y][x+keyImgWidth-1])==0
&& (keyImageRGBData[keyImgHeight-1][keyImgWidth-1]^screenShotImageRGBData[y+keyImgHeight-1][x+keyImgWidth-1])==0
&& (keyImageRGBData[keyImgHeight-1][0]^screenShotImageRGBData[y+keyImgHeight-1][x])==0) { boolean isFinded = isMatchAll(y, x);
//如果比较结果完全相同,则说明图片找到,填充查找到的位置坐标数据到查找结果数组。
if(isFinded) {
for(int h=0; h<keyImgHeight; h++) {
for(int w=0; w<keyImgWidth; w++) {
findImgData[h][w][0] = y+h;
findImgData[h][w][1] = x+w;
}
}
return;
}
}
}
}
} /**
* 判断屏幕截图上目标图映射范围内的全部点是否全部和小图的点一一对应。
* @param y - 与目标图左上角像素点想匹配的屏幕截图y坐标
* @param x - 与目标图左上角像素点想匹配的屏幕截图x坐标
* @return
*/
public boolean isMatchAll(int y, int x) {
int biggerY = 0;
int biggerX = 0;
int xor = 0;
for(int smallerY=0; smallerY<keyImgHeight; smallerY++) {
biggerY = y+smallerY;
for(int smallerX=0; smallerX<keyImgWidth; smallerX++) {
biggerX = x+smallerX;
if(biggerY>=scrShotImgHeight || biggerX>=scrShotImgWidth) {
return false;
}
xor = keyImageRGBData[smallerY][smallerX]^screenShotImageRGBData[biggerY][biggerX];
if(xor!=0) {
return false;
}
}
biggerX = x;
}
return true;
} /**
* 输出查找到的坐标数据
*/
private void printFindData() {
for(int y=0; y<keyImgHeight; y++) {
for(int x=0; x<keyImgWidth; x++) {
System.out.print("("+this.findImgData[y][x][0]+", "+this.findImgData[y][x][1]+")");
}
System.out.println();
}
} public static void main(String[] args) {
String keyImagePath = "D:/key.png";
ImageFindDemo demo = new ImageFindDemo(keyImagePath);
demo.printFindData();
} }

  这种算法是精确比较,只要有一个像素点有差异,就会找不到图片。当然,如果想指定一个比较的精确度,我也有个思路,就是在算法步骤4比较映射范围内全部像素点的时候做个统计,如果90%的点都相同,那就是说精确度是0.9。

  另外,可能还要考虑效率问题,不过,我在我的应用场景中并不太在意效率。如果有朋友看到这篇文章,对这个话题有更好的想法,请留言。

Java图片上查找图片算法的更多相关文章

  1. [转]jquery 鼠标放在图片上显示图片的放大镜效果jqzoom_ev-2.3

    本文转自:http://blog.csdn.net/weizengxun/article/details/6768183 鼠标放在图片上显示图片的放大镜效果使用jqzoom实现,本例版本2.3 效果图 ...

  2. 2019-3-16-win10-uwp-鼠标移动到图片上切换图片

    title author date CreateTime categories win10 uwp 鼠标移动到图片上切换图片 lindexi 2019-03-16 14:43:46 +0800 201 ...

  3. java 文件上传(图片上传)

    1.FTP工具类 代码如下: package com.taotao.common.utils; import java.io.File; import java.io.FileInputStream; ...

  4. spring mvc 图片上传,图片压缩、跨域解决、 按天生成文件夹 ,删除,限制为图片代码等相关配置

    spring mvc 图片上传,跨域解决 按天生成文件夹 ,删除,限制为图片代码,等相关配置 fs.root=data/ #fs.root=/home/dev/fs/ #fs.root=D:/fs/ ...

  5. Django中怎么做图片上传--图片展示

    1.首先是html页面的form表单的三大属性,action是提交到哪,method是提交方式,enctype只要有图片上传就要加这个属性 Django框架自带csrf_token ,所以需要在前端页 ...

  6. SpringBoot图片上传(五) 上一篇的新版本,样式修改后的

    简单描述:一次上传N张图片(N可自定义):上传完后图片回显,鼠标放到已经上传的图片上后,显示删除,点击后可以删除图片,鼠标离开后,图片恢复. 效果:一次上传多个图片后的效果 上传成功: 鼠标悬浮到图片 ...

  7. C#设计模式总结 C#设计模式(22)——访问者模式(Vistor Pattern) C#设计模式总结 .NET Core launch.json 简介 利用Bootstrap Paginator插件和knockout.js完成分页功能 图片在线裁剪和图片上传总结 循序渐进学.Net Core Web Api开发系列【2】:利用Swagger调试WebApi

    C#设计模式总结 一. 设计原则 使用设计模式的根本原因是适应变化,提高代码复用率,使软件更具有可维护性和可扩展性.并且,在进行设计的时候,也需要遵循以下几个原则:单一职责原则.开放封闭原则.里氏代替 ...

  8. rails使用bootstrap3-wysiwyg可视化编辑器并实现自定义图片上传插入功能

    之前在rails开发中使用了ckeditor作为可视化编辑器,不过感觉ckeditor过于庞大,有很多不需要的功能,而且图片上传功能不好控制不同用户可以互相删除图片,感觉很不好.于是考虑更改可视化编辑 ...

  9. android 加载自定义图片并在图片上绘图

    来源:毕设 关键词:Bitmap Canvas //毕设中需要自定义室内地图,并且在地图上绘制轨迹 //此处是一个测试Demo,实现图片的加载和记录手指在屏幕上的运动轨迹 图片的载入 使用系统提供的内 ...

随机推荐

  1. 【转】golang中的并行与并发

    原文:http://blog.csdn.net/taohaoge/article/details/27970421 ------------------------------------------ ...

  2. JDBC基本应用

    首先我们来看一下JDBC操作数据的核心: Connection 应用程序与数据库之间的桥梁 数据库驱动程序是构建桥梁的基石和材料 DriverManager类是基石和材料的管理员 Statement ...

  3. 怎样在Swift中使用NSError

    步骤一:声明NSError变量. 一定要加"?",不加或者加"!"都不行.由于使用了optional,所以要用var而不用let. var error: NSE ...

  4. Java对话框总结

    总结起来非常easy: 1,对话框类型:消息.确认,选项.输入 2,选择图标:错误,信息.警告.问题,无或者自己定义 3,选择消息:字符串,图标.自己定义组件或者他们的集合 4.对于确认对话框,选择选 ...

  5. cocos2dX 之数据存储

    今天我们来看cocos2dX里面的数据存储类, CCUserDefault, 如今的游戏基本都会把用户信息保存下来, 以便于再次进入游戏的时候读取, 为了方便起见,有时我们也能够用CCUserDefa ...

  6. 读书笔记:Information Architecture for the World Wide Web, 3rd Edition 北极熊 第一部分 1-3

    Introducing Information Architecture 信息架构简介 Chapter 1 Defining Information Architecture 信息架构的意义(我们盖房 ...

  7. MySQL-子查询,派生表,通用表达式

    MySQL-子查询 MySQL子查询是嵌套在另一个查询中的查询. MySQL子查询还可以嵌套在另一个子查询中. MySQL子查询称为内部查询,而包含子查询的查询称为外部查询. 查询返回在位于美国(US ...

  8. QQ空间说说 视频播放

    http://182.254.8.83/vwecam.gtimg.com/1006_d81d60f3c83844a5ad6a184149d4ccbb.f0.mp4?sha=78A27CF4908AB5 ...

  9. 20170626_oracle_数据库设计

    数据库设计的定义:规划数据库中数据对象以及之间关系的过程. 为什么进行数据库设计? 空间 完整性 程序开发 数据库设计前提知识: 范式: 1NF:第一范式 第一范式的目标是确保每列的原子性 如果每列都 ...

  10. 报表应用系列——图表JFreeChart: 第 4 章 折线图

    双击代码全选 1 2 3 4 5 DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(100 ...