Java實現(xiàn)圖片對比功能
之前用按鍵精靈寫過一些游戲輔助,里面有個函數(shù)叫FindPic,就上在屏幕范圍查找給定的一張圖片,返回查找到的坐標位置。
現(xiàn)在,Java來實現(xiàn)這個函數(shù)類似的功能。
算法描述:
屏幕截圖,得到圖A,(查找的目標圖片為圖B);
遍歷圖A的像素點,根據(jù)圖B的尺寸,得到圖B四個角映射到圖A上的四個點;
得到的四個點與圖B的四個角像素點的值比較。如果四個點一樣,執(zhí)行步驟4;否則,回到步驟2繼續(xù);
進一步對比,將映射范圍內的全部點與圖B全部的點比較。如果全部一樣,則說明圖片已找到;否則,回到步驟2繼續(xù);
這里,像素之間的比較是通過BufferedImage對象獲取每個像素的RGB值來比較的。如下,將BufferedImage轉換為int二維數(shù)組:
/**
* 根據(jù)BufferedImage獲取圖片RGB數(shù)組
* @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值是否相同,是通過異或操作比較的(據(jù)說比==效率更高),如果異或操作后得到的值為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數(shù)據(jù)
int[][] keyImageRGBData; //查找目標圖片RGB數(shù)據(jù)
int[][][] findImgData; //查找結果,目標圖標位于屏幕截圖上的坐標數(shù)據(jù)
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;
}
/**
* 根據(jù)BufferedImage獲取圖片RGB數(shù)組
* @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];
//遍歷屏幕截圖像素點數(shù)據(jù)
for(int y=0; y<scrShotImgHeight-keyImgHeight; y++) {
for(int x=0; x<scrShotImgWidth-keyImgWidth; x++) {
//根據(jù)目標圖的尺寸,得到目標圖四個角映射到屏幕截圖上的四個點,
//判斷截圖上對應的四個點與圖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);
//如果比較結果完全相同,則說明圖片找到,填充查找到的位置坐標數(shù)據(jù)到查找結果數(shù)組。
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;
}
/**
* 輸出查找到的坐標數(shù)據(jù)
*/
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比較映射范圍內全部像素點的時候做個統(tǒng)計,如果90%的點都相同,那就是說精確度是0.9。
另外,可能還要考慮效率問題,不過,我在我的應用場景中并不太在意效率。如果有朋友看到這篇文章,對這個話題有更好的想法,請留言。
相關文章
SpringMVC中@controllerAdvice注解的詳細解釋
剛接觸SpringMVC應該很少會見到這個注解,其實它的作用非常大,下面這篇文章主要給大家介紹了關于SpringMVC中@controllerAdvice注解的相關資料,需要的朋友可以參考下2022-02-02
java 將byte中的有效長度轉換為String的實例代碼
下面小編就為大家?guī)硪黄猨ava 將byte中的有效長度轉換為String的實例代碼。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-11-11
java.lang.Runtime.exec的左膀右臂:流輸入和流讀取詳解
這篇文章主要介紹了java.lang.Runtime.exec的左膀右臂:流輸入和流讀取詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11

