以前、ボタン ウィザードを使用していくつかのゲーム アシスタントを作成しました。画面範囲内で指定された画像を検索し、見つかった座標位置を返す FindPic という関数があります。
現在、Java はこの関数と同様の機能を実装しています。
アルゴリズムの説明:
スクリーンショットを撮り、画像 A を取得します (探しているターゲット画像は画像 B)。
画像 A のピクセルをトラバースし、画像 B のサイズに従って、画像 A 上の 4 つの点にマッピングされた画像 B の 4 隅を取得します。
得られた 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[][] 結果 = new int[高さ][幅]; = 0; h < height; h++) { for (int w = 0; w < width; w++) { //実際のアプリケーションでは、getRGB(w, h) を使用してポイントのカラー値を取得します。 、これは RGB なので、ARGB を RGB、つまり bufImg.getRGB(w, h) & 0xFFFFFF に変換する必要があります。 result[h][w] = bfImage.getRGB(w, h) & } } 結果を返します。
2 つのピクセルの RGB 値が同じかどうかの比較は、XOR 演算によって行われます (== よりも効率的と言われています)。XOR 演算後に得られた値が 0 の場合、それは RGB 値が同じであることを意味します。 2 つのピクセルのうちの 1 つは同じですが、それ以外の場合は同じではありません。
アルゴリズムの完全な Java コードを以下に添付します。
パッケージ com.jebysun.test.imagefind; インポート java.awt.Rectangle; インポート java.awt.image.BufferedImage; .File; import java.io.IOException; import javax.imageio.ImageIO; /** * 画面上の指定された画像を検索します* Jeby Sun * @date 2014-09-13 * @website http://www.jebysun.com */ public class ImageFindDemo { BufferedImage screenShotImage //ターゲット画像を検索 int scrShotImgWidth; int scrShotImgHeight; //スクリーンショットの高さ int keyImgWidth //ターゲット画像の幅を検索します。 keyImgHeight; //ターゲット画像の高さを検索 int[][] screenShotImageRGBData; //ターゲット画像の RGB データを検索 int[][][] findImgData; target スクリーンショット上にあるアイコンの座標データ public ImageFindDemo(String keyImagePath) { screenShotImage = this.getFullScreenShot(); = this.getBfImageFromPath(keyImagePath); screenShotImageRGBData = this.getImageGRB(keyImage); scrShotImgWidth = screenShotImage.getWidth(); keyImgWidth = keyImage.getWidth(); keyImgHeight = keyImage.getHeight(); // this.findImage() の検索を開始します } /** * フルスクリーンのスクリーンショット * @return Return BufferedImage */ public BufferedImage getFullScreenShot() { BufferedImage bfImage = null ; int 幅 = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth(); int height = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight(); try { ロボット robot = new Robot(); new Rectangle(0, 0, width, height)); } catch (AWTException e) { e.printStackTrace(); } return bfImage; } /** * ローカル ファイルからターゲット イメージを読み取ります * @param keyImagePath - 画像の絶対パス * @return ローカル イメージの BufferedImage オブジェクト */ public BufferedImage getBfImageFromPath (文字列 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();高さ = bfImage.getHeight(); int[][] 結果 = new int[高さ][幅]; for (int h = 0; h < 高さ; h++) { for (int w = 0; w < 幅; 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]; // スクリーンショットのピクセル データをトラバースします (int y=0; y<scrShotImgHeight-keyImgHeight; y++) { for(int x=0; 図Bの4つの点と4隅のピクセルの値は同じですか? //同じであれば、スクリーンショット上のマッピング範囲内のすべての点を比較しますターゲット画像内のすべての点。 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 betterX = 0; int xor = 0; for(int SmallY=0; SmallY<keyImgHeight; SmallY++) = y+小さいY; for(int小さいX=0;小さいX<keyImgWidth;小さいX++) {大きいX = x+小さいX; if(大きいY>=scrShotImgHeight ||大きいX>=scrShotImgWidth) { return false; } xor = keyImageRGBData[小さいY] ]^screenShotImageRGBData[大きいY][大きいX]; if(xor!=0) { return false; } } biggestX = 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]+")"); println(); } } public static void main(String[] args) { String keyImagePath = "D:/key.png"; ImageFindDemo(keyImagePath);
このアルゴリズムは正確な比較であり、1 つのピクセルに違いがある限り、画像は検出されません。もちろん、比較精度を指定したい場合は、アルゴリズムのステップ 4 でマッピング範囲内のすべてのピクセルを比較するときに統計を作成するというアイデアもあります。点の 90% が同じである場合、それは意味します。精度は0.9です。
さらに、効率の問題も考慮される可能性がありますが、私のアプリケーション シナリオでは効率はあまり気にしません。この記事を読んだ友人がいて、このトピックについてより良いアイデアを持っている場合は、メッセージを残してください。