首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java 圖片比對與螢幕尋圖實作:像素級 RGB 比較、四角快速過濾與完整代碼

     編輯:Java 程式設計入門

前言:從按鍵精靈到 Java 原生實現

遊戲輔助開發中常見「尋圖」功能:在螢幕截圖中定位目標圖片座標。本文實作純 Java 版 FindPic,採用**四角像素快速過濾 + 全像素精確比對**策略,附完整可執行代碼與效能分析。

演算法流程

  1. 螢幕截圖:Robot.createScreenCapture() 取得全螢幕 BufferedImage(圖 A)。
  2. 載入目標:ImageIO.read() 讀取目標圖片檔案(圖 B)。
  3. RGB 陣列轉換:將兩圖轉為 int[][],每像素存 0xRRGGBB(忽略 Alpha)。
  4. 滑動窗口遍歷:在圖 A 中以圖 B 寬高為步長滑動。
  5. 四角快速過濾:比對當前窗口四角像素與圖 B 四角,**異或 (XOR) 結果為 0 才進入精確比對**。
  6. 全像素精確比對:窗口內每像素與圖 B 逐一 XOR 比對,全 0 即找到。
  7. 記錄座標:將匹配區域座標存入三維陣列 findImgData[h][w][0/1] (y, x)。

關鍵技術細節

1. BufferedImage → RGB 二維陣列

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

2. XOR 像素比較:比 == 更快

// 兩像素相同 ⇔ (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) {
    // 四角通過,進入全像素比對
}

位運算單指令週期,分支預測友好,大幅優於 == 的分支邏輯。

3. 全像素精確比對

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 次

進階優化

  • 多尺度金字塔:先縮小圖片粗略定位,再在原圖精確確認。
  • 特徵點匹配 (ORB/SIFT):OpenCV Java 綁定,抗旋轉/縮放/光照變化。
  • 相似度閾值:統計匹配像素比例,允許 90%+ 相似度(抗壓縮偽影/微小色差)。
  • 區域限定:僅搜尋遊戲視窗客戶區,縮小搜尋空間。
  • 多執行緒分塊:將螢幕分割交給多核心並行四角過濾。

應用場景與限制

  • ✅ 適用:UI 自動化測試、遊戲固定介面元素識別、桌面應用輔助定位。
  • ❌ 不適用:遊戲 3D 場景、動態縮放/旋轉、高壓縮視頻畫面、需跨解析度匹配。
  • ⚠️ 注意:精確像素比對極其脆弱**,任何抗鋸齒、色彩配置檔、DPI 縮放、壓縮偽影都會導致失敗。

總結

本文實作了**純 Java、零依賴**的螢幕尋圖功能,核心在於 Robot 截圖 + BufferedImage 像素存取 + **XOR 位運算四角過濾 + 全像素驗證**。代碼結構清晰,易於整合至自動化腳本。對於生產級需求,建議引入 OpenCV (JavaCV) 進行特徵點/模板匹配,獲得旋轉/縮放/光照不變性與更高效能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved