Java实现图片对比功能

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

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

  算法描述:

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

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

(0)

相关推荐

  • JAVA比较两张图片相似度的方法

    本文实例讲述了JAVA比较两张图片相似度的方法.分享给大家供大家参考.具体如下: 摘要: importjava.awt.image.BufferedImage; importjava.io.File; importjavax.imageio.ImageIO; /***比较两张图片的相似度*@authorGuihua**/publicclassBMPLoader{//改变成二进制码 publicstaticString[][]getPX(Stringargs){int[]rgb=newint[3]

  • java将图片至暗的实现方法

    之前也写过一个代码给一张图片然后把图片变暗,今天我们换一种思路,或者是是另外的一种方式将图片至暗,当然方法也是很简单的,但是对于菜鸟的我在这个地方停留了一天半的时间,将图片至暗 现在我们要将这样的一张图片 变成为 虽然说变暗之后确实没有之间亮的好看,但是不管了,反正那么漂亮的美女和我的关系我不太大,如果说硬是有关系的话,那应该是在梦中了,好了我们直接上代码 package com.epoint.wdg.test; import java.awt.Color; import java.awt.im

  • Java实现图片对比功能

    之前用按键精灵写过一些游戏辅助,里面有个函数叫FindPic,就上在屏幕范围查找给定的一张图片,返回查找到的坐标位置. 现在,Java来实现这个函数类似的功能. 算法描述: 屏幕截图,得到图A,(查找的目标图片为图B): 遍历图A的像素点,根据图B的尺寸,得到图B四个角映射到图A上的四个点: 得到的四个点与图B的四个角像素点的值比较.如果四个点一样,执行步骤4:否则,回到步骤2继续: 进一步对比,将映射范围内的全部点与图B全部的点比较.如果全部一样,则说明图片已找到:否则,回到步骤2继续: 这里

  • Java实现图片切割功能

    本文实例为大家分享了Java实现图片切割功能的具体代码,供大家参考,具体内容如下 工具类 package com.xudaolong.Utils; import javax.imageio.IIOImage; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.ImageWriter; import javax.imageio.stream.ImageInputStream; i

  • Java实现图片裁剪功能的示例详解

    目录 前言 Maven依赖 代码 验证一下 前言 本文提供将图片按照自定义尺寸进行裁剪的Java工具类,一如既往的实用主义. Maven依赖 <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>30.1.1-jre</version> </dependency> <dependen

  • java实现的图片裁剪功能示例

    本文实例讲述了java实现的图片裁剪功能.分享给大家供大家参考,具体如下: PicCut.java: package Tsets; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.Iterator; import javax

  • java web中图片验证码功能的简单实现方法

    用户在注册网站信息的时候基本上都要数据验证码验证.那么图片验证码功能该如何实现呢? 大概步骤是: 1.在内存中创建缓存图片 2.设置背景色 3.画边框 4.写字母 5.绘制干扰信息 6.图片输出 废话不多说,直接上代码 package com.lsgjzhuwei.servlet.response; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.image.Buffer

  • Java Struts图片上传至指定文件夹并显示图片功能

    继上一次利用Servlet实现图片上传,这次利用基于MVC的Struts框架,封装了Servlet并简化了JSP页面跳转. JSP上传页面 上传一定要为form加上enctype="multipart/form-data",表示提交的数据时二进制的 并且必须是method="post" <%@ page language="java" contentType="text/html; charset=utf-8" page

  • Java PhantomJs完成html图片输出功能

    借助phantomJs来实现将html网页输出为图片 I. 背景 如何在小程序里面生成一张图,分享到朋友圈呢?目前前端貌似没有太好的解决方法,所以只能猥琐的由后端来支持掉,那么可以怎么玩? 生成图片比较简单 简单的场景,可以直接用jdk来支持掉,一般来讲也没有太复杂的逻辑 之前写过一个图片合成的逻辑,利用awt实现: 图片合成 通用.复杂的模板 简单的可以直接支持,但复杂一点的,让后端来支持,无疑比较恶心,在github上也搜索了一些渲染html的开源库,不知道是姿势不对还是咋的,没有太满意的结

  • Java实现图片上传至服务器功能(FTP协议)

    本文为大家分享了java实现图片上传至服务器功能的具体代码,供大家参考,具体内容如下 本案例实现图片上传功能分为两个步骤,分别为 (1)APP用base64加密将图片内容上传至服务器(http协议),在临时目录中先存储好图片: (2)将服务器临时存储的图片用FTP协议上传至另一台专门用做存储图片的服务器: /** * ftp 文件操作服务实现类 * */ @Service public class FtpFileServiceImpl implements IFtpFileService { /

  • Spring Security 图片验证码功能的实例代码

    验证码逻辑 以前在项目中也做过验证码,生成验证码的代码网上有很多,也有一些第三方的jar包也可以生成漂亮的验证码.验证码逻辑很简单,就是在登录页放一个image标签,src指向一个controller,这个Controller返回把生成的图片以输出流返回给页面,生成图片的同时把图片上的文本放在session,登录的时候带过来输入的验证码,从session中取出,两者对比.这位老师讲的用Spring Security集成验证码,大体思路和我说的一样,但更加规范和通用些. spring securi

  • Java实现图片上传到服务器并把上传的图片读取出来

    在很多的网站都可以实现上传头像,可以选择自己喜欢的图片做头像,从本地上传,下次登录时可以直接显示出已经上传的头像,那么这个是如何实现的呢? 下面说一下我的实现过程(只是个人实现思路,实际网站怎么实现的不太清楚) 实现的思路: 工具:MySQL,eclipse 首先,在MySQL中创建了两个表,一个t_user表,用来存放用户名,密码等个人信息, 一个t_touxiang表,用来存放上传的图片在服务器中的存放路径,以及图片名字和用户ID, T_touxiang表中的用户ID对应了t_user中的i

随机推荐