遊戲輔助開發中常見「尋圖」功能:在螢幕截圖中定位目標圖片座標。本文實作純 Java 版 FindPic,採用**四角像素快速過濾 + 全像素精確比對**策略,附完整可執行代碼與效能分析。
Robot.createScreenCapture() 取得全螢幕 BufferedImage(圖 A)。ImageIO.read() 讀取目標圖片檔案(圖 B)。int[][],每像素存 0xRRGGBB(忽略 Alpha)。findImgData[h][w][0/1] (y, x)。public static int[][] toRgbArray(BufferedImage img) {
int w = img.getWidth(), h = img.getHeight();
int[][] rgb = new int[h][w];
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
// getRGB 回傳 ARGB,& 0xFFFFFF 取 RGB
rgb[y][x] = img.getRGB(x, y) & 0xFFFFFF;
}
}
return rgb;
}
// 兩像素相同 ⇔ (a ^ b) == 0
if ((targetRgb[0][0] ^ screenRgb[y][x]) == 0 &&
(targetRgb[0][tw-1] ^ screenRgb[y][x+tw-1]) == 0 &&
(targetRgb[th-1][tw-1] ^ screenRgb[y+th-1][x+tw-1]) == 0 &&
(targetRgb[th-1][0] ^ screenRgb[y+th-1][x]) == 0) {
// 四角通過,進入全像素比對
}
位運算單指令週期,分支預測友好,大幅優於 == 的分支邏輯。
private boolean exactMatch(int screenY, int screenX) {
for (int ty = 0; ty < th; ty++) {
int sy = screenY + ty;
if (sy >= sh) return false;
for (int tx = 0; tx < tw; tx++) {
int sx = screenX + tx;
if (sx >= sw) return false;
if ((targetRgb[ty][tx] ^ screenRgb[sy][sx]) != 0) return false;
}
}
return true; // 完美匹配
}
package com.jebysun.test.imagefind;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class ImageFindDemo {
private BufferedImage screenImg, targetImg;
private int[][] screenRgb, targetRgb;
private int sw, sh, tw, th;
private int[][][] matchCoords; // [ty][tx][0=y, 1=x]
public ImageFindDemo(String targetPath) {
screenImg = captureScreen();
targetImg = loadImage(targetPath);
screenRgb = toRgbArray(screenImg);
targetRgb = toRgbArray(targetImg);
sw = screenImg.getWidth(); sh = screenImg.getHeight();
tw = targetImg.getWidth(); th = targetImg.getHeight();
findImage();
}
private BufferedImage captureScreen() {
try {
Rectangle bounds = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
return new Robot().createScreenCapture(bounds);
} catch (AWTException e) { throw new RuntimeException(e); }
}
private BufferedImage loadImage(String path) {
try { return ImageIO.read(new File(path)); }
catch (IOException e) { throw new RuntimeException("讀取目標圖失敗: " + path, e); }
}
private static int[][] toRgbArray(BufferedImage img) {
int w = img.getWidth(), h = img.getHeight();
int[][] rgb = new int[h][w];
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++)
rgb[y][x] = img.getRGB(x, y) & 0xFFFFFF;
return rgb;
}
private void findImage() {
matchCoords = new int[th][tw][2];
for (int y = 0; y <= sh - th; y++) {
for (int x = 0; x <= sw - tw; x++) {
// 四角快速過濾
if ((targetRgb[0][0] ^ screenRgb[y][x]) != 0) continue;
if ((targetRgb[0][tw-1] ^ screenRgb[y][x+tw-1]) != 0) continue;
if ((targetRgb[th-1][tw-1] ^ screenRgb[y+th-1][x+tw-1]) != 0) continue;
if ((targetRgb[th-1][0] ^ screenRgb[y+th-1][x]) != 0) continue;
// 精確比對
if (exactMatch(y, x)) {
recordMatch(y, x);
return; // 找到第一個即返回
}
}
}
}
private boolean exactMatch(int sy, int sx) {
for (int ty = 0; ty < th; ty++) {
int shy = sy + ty;
for (int tx = 0; tx < tw; tx++) {
if ((targetRgb[ty][tx] ^ screenRgb[shy][sx+tx]) != 0) return false;
}
}
return true;
}
private void recordMatch(int sy, int sx) {
for (int ty = 0; ty < th; ty++)
for (int tx = 0; tx < tw; tx++) {
matchCoords[ty][tx][0] = sy + ty;
matchCoords[ty][tx][1] = sx + tx;
}
}
public void printMatchCoords() {
if (matchCoords == null) { System.out.println("未找到目標圖片"); return; }
for (int y = 0; y < th; y++) {
for (int x = 0; x < tw; x++)
System.out.print("(" + matchCoords[y][x][0] + ", " + matchCoords[y][x][1] + ") ");
System.out.println();
}
}
public static void main(String[] args) {
String target = "D:/key.png"; // 請替換為實際目標圖片路徑
ImageFindDemo demo = new ImageFindDemo(target);
demo.printMatchCoords();
}
}
| 階段 | 複雜度 | 優化點 |
|---|---|---|
| RGB 轉換 | O(SW×SH + TW×TH) | 一次性成本,可接受 |
| 四角過濾 | O((SW-TW)×(SH-TH)) | 極快,位運算,淘汰 99%+ 窗口 |
| 精確比對 | O(匹配候選數 × TW×TH) | 候選極少,通常 0~1 次 |
本文實作了**純 Java、零依賴**的螢幕尋圖功能,核心在於 Robot 截圖 + BufferedImage 像素存取 + **XOR 位運算四角過濾 + 全像素驗證**。代碼結構清晰,易於整合至自動化腳本。對於生產級需求,建議引入 OpenCV (JavaCV) 進行特徵點/模板匹配,獲得旋轉/縮放/光照不變性與更高效能。