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 || 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 
 

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

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

你可能感兴趣的:(Java实现图片对比功能)