이전에 버튼 마법사를 사용하여 일부 게임 도우미를 작성한 적이 있습니다. 화면 범위 내에서 주어진 그림을 검색하고 찾은 좌표 위치를 반환하는 FindPic이라는 기능이 있습니다.
이제 Java는 이 함수와 유사한 기능을 구현합니다.
알고리즘 설명:
스크린샷을 찍어 사진 A를 얻습니다(찾고 있는 대상 사진은 사진 B입니다).
이미지 A의 픽셀을 탐색하고 이미지 B의 크기에 따라 이미지 A의 네 점에 매핑된 이미지 B의 네 모서리를 얻습니다.
획득된 4개의 점은 그림 B의 4개 모서리 픽셀 값과 비교됩니다. 4개의 점이 동일하면 4단계로 이동하고, 그렇지 않으면 2단계로 돌아가 계속합니다.
추가 비교를 위해 매핑 범위 내의 모든 지점을 그림 B의 모든 지점과 비교하십시오. 모두 동일하면 그림을 찾은 것입니다. 그렇지 않으면 2단계로 돌아가서 계속합니다.
여기서 픽셀 간의 비교는 BufferedImage 객체를 통해 각 픽셀의 RGB 값을 구하여 수행됩니다. 다음과 같이 BufferedImage를 int 2차원 배열로 변환합니다.
/** * BufferedImage를 기반으로 이미지 RGB 배열 가져오기 * @param bfImage * @return */ public static int[][] getImageGRB(BufferedImage bfImage) { int width = bfImage.getWidth() int height = bfImage.getHeight( ); int[][] 결과 = 새로운 int[높이][너비]; = 0; h < height; h++) { for (int w = 0; w < width; w++) { // ARGB인 점의 색상 값을 얻으려면 getRGB(w, h)를 사용하십시오. , RGB이므로 ARGB를 RGB, 즉 bufImg.getRGB(w, h) & 0xFFFFFF로 변환해야 합니다. 결과[h][w] = bfImage.getRGB(w, h) & 0xFFFFFF } } 결과 반환;
두 픽셀의 RGB 값이 같은지 비교하는 것은 XOR 연산을 통해 이루어진다(이는 ==보다 효율적이라고 한다). XOR 연산 후 얻은 값이 0이면 RGB 값이 두 픽셀 중 는 동일합니다. 그렇지 않으면 동일하지 않습니다.
알고리즘의 전체 Java 코드는 아래에 첨부되어 있습니다.
package com.jebysun.test.imagefind; import java.awt.Rectangle; import java.awt.Toolkit; 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; //Screenshot BufferedImage keyImage; //대상 이미지 찾기 int scrShotImgWidth; int scrShotImgHeight; //스크린샷 높이 int keyImgWidth; //대상 이미지 너비 찾기 int keyImgHeight; //대상 이미지 높이 찾기 int[][] screenShotImageRGBData; //스크린샷 RGB 데이터 int[][] keyImageRGBData; //대상 이미지 RGB 데이터 찾기 int[][][] //결과 찾기 target 스크린샷에 위치한 아이콘의 좌표 데이터 public ImageFindDemo(String keyImagePath) { screenShotImage = this.getFullScreenShot(); = this.getBfImageFromPath(keyImagePath); screenShotImageRGBData = this.getImageGRB(screenShotImage); keyImageRGBData = this.getImageGRB(keyImage); scrShotImgWidth = screenShotImage.getHeight(); keyImgWidth = keyImage.getWidth(); keyImgHeight = keyImage.getHeight(); //this.findImage() 찾기 시작 } /** * 전체 화면 스크린샷* @return Return BufferedImage */ public BufferedImage getFullScreenShot() { BufferedImage = null ; 정수 너비 = (정수) Toolkit.getDefaultToolkit().getScreenSize().getWidth(); int height = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight(); try { 로봇 로봇 = new Robot(); new Rectangle(0, 0, 너비, 높이)) } catch (AWTException e) { e.printStackTrace(); } return bfImage; } /** * 로컬 파일에서 대상 이미지 읽기 * @param keyImagePath - 이미지의 절대 경로 * @return 로컬 이미지의 BufferedImage 객체 */ public BufferedImage getBfImageFromPath (String keyImagePath) { BufferedImage bfImage = null 시도 { 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(); 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; } } 결과 반환 } /** * 이미지 찾기*/ public void findImage() { findImgData = new int[keyImgHeight][keyImgWidth][ 2]; //스크린샷 픽셀 데이터를 탐색합니다. for(int y=0; y<scrShotImgHeight-keyImgHeight; y++) { for(int x=0; 그림 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) { 부울 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; h][w][1] = x+w; } } } } } } /** * 스크린샷의 대상 이미지 매핑 범위 내의 모든 포인트가 작은 이미지의 포인트와 1:1로 대응되는지 확인합니다. * @param y - 대상 이미지의 왼쪽 상단 픽셀 포인트와 일치시키고 싶은 스크린샷의 y 좌표 * @param x - 왼쪽 상단 픽셀 포인트와 일치시키고 싶은 스크린샷의 x 좌표 대상 이미지의 * @return */ public boolean isMatchAll(int y, int x) { int bigY = 0; int 더 큰X = 0; for(int 더 작은Y=0; 더 작은Y<keyImgHeight; 더 작은Y++) = y+smallerY; for(int 더 작은X=0; 더 작은X<keyImgWidth; 더 작은X++) { 더 큰X = x+smallerX; if(biggerY>=scrShotImgHeight || 더 큰X>=scrShotImgWidth) { return false } xor = keyImageRGBData[smallerY][smallerX; ]^screenShotImageRGBData[biggerY][biggerX]; if(xor!=0) { return false; } } 더 큰X = 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(keyImagePath); 데모.printFindData() } }
이 알고리즘은 정확한 비교이므로 한 픽셀에 차이가 있으면 사진을 찾을 수 없습니다. 물론, 비교 정확도를 지정하고 싶다면 알고리즘의 4단계에서 매핑 범위 내의 모든 픽셀을 비교할 때 통계를 작성하는 아이디어도 있습니다. 즉, 포인트의 90%가 동일하다는 의미입니다. 정확도는 0.9이다.
또한 효율성 문제도 고려될 수 있지만 내 애플리케이션 시나리오에서는 효율성에 크게 신경 쓰지 않습니다. 이 글을 읽고 이 주제에 대해 더 나은 아이디어를 갖고 있는 친구가 있다면 메시지를 남겨주세요.