本文實現一種基於像素 RGB 差值統計的圖片相似度比較算法。核心思路:將兩張圖片縮放至相同尺寸,逐像素比對 RGB 三通道值,設定閾值判定相似像素,最終計算相似像素佔比作為相似度指標。提供完整可運行代碼與結果解析。
BufferedImage(x, y)R = (pixel >> 16) & 0xFF 等|R1 - R2|, |G1 - G2|, |B1 - B2|
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
public class ImageSimilarity {
/**
* 將圖片轉換為 RGB 字串矩陣
* @param imagePath 圖片路徑
* @return 二維陣列,每元素為 "R,G,B" 格式字串
*/
public static String[][] getPixelMatrix(String imagePath) {
File file = new File(imagePath);
BufferedImage image;
try {
image = ImageIO.read(file);
} catch (Exception e) {
e.printStackTrace();
return new String[0][0];
}
int width = image.getWidth();
int height = image.getHeight();
String[][] matrix = new String[width][height];
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int pixel = image.getRGB(x, y);
int r = (pixel >> 16) & 0xFF;
int g = (pixel >> 8) & 0xFF;
int b = pixel & 0xFF;
matrix[x][y] = r + "," + g + "," + b;
}
}
return matrix;
}
/**
* 比較兩張圖片相似度
* @param imgPath1 圖片 1 路徑
* @param imgPath2 圖片 2 路徑
*/
public static void compareImages(String imgPath1, String imgPath2) {
String[][] pixels1 = getPixelMatrix(imgPath1);
String[][] pixels2 = getPixelMatrix(imgPath2);
// 尺寸檢查
if (pixels1.length != pixels2.length ||
pixels1[0].length != pixels2[0].length) {
System.out.println("圖片尺寸不一致,無法直接比較。建議先縮放至相同尺寸。");
return;
}
int width = pixels1.length;
int height = pixels1[0].length;
int similar = 0;
int different = 0;
final int THRESHOLD = 5; // RGB 差值閾值
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
String[] rgb1 = pixels1[x][y].split(",");
String[] rgb2 = pixels2[x][y].split(",");
boolean isSimilar = true;
for (int c = 0; c < 3; c++) {
int diff = Math.abs(Integer.parseInt(rgb1[c]) - Integer.parseInt(rgb2[c]));
if (diff >= THRESHOLD) {
isSimilar = false;
break;
}
}
if (isSimilar) similar++;
else different++;
}
}
double similarity = (double) similar / (similar + different) * 100;
System.out.printf("相似像素: %d, 不同像素: %d, 相似度: %.2f%%%n",
similar, different, similarity);
}
public static void main(String[] args) {
// 測試:請替換為實際圖片路徑
compareImages("E:/12.bmp", "E:/1.bmp");
}
}
原始代碼存在以下問題,已在上方修正:
i, j 索引卻雙重嵌套導致越界catch (RuntimeException e) { continue; } 掩蓋真實錯誤| 優化方向 | 實現方式 | 效果 |
|---|---|---|
| 避免字串操作 | 直接用 int[] rgb = new int[3] 存儲或位運算提取 | 大幅降低 GC 壓力,提升 10-50x |
| 圖片預處理縮放 | 使用 Image.getScaledInstance() 統一尺寸 | 支援不同解析度圖片比較 |
| 採樣比較 | 每隔 N 像素採樣,適合大圖快速預估 | 犧牲精度換速度 |
| 使用原始數組 | int[] pixels = image.getRGB(0, 0, w, h, null, 0, w) | 一次性讀取所有像素,避免重複 getRGB 調用 |
| 適用場景 | 不適用場景 |
|---|---|
| 截圖比對、UI 回歸測試 | 不同角度/光照拍攝同一物體 |
| 水印偵測、圖片去重(完全相同) | 縮放、裁剪、旋轉後的圖片 |
| 簡單相似度閾值過濾 | 語義級相似度(如「都是貓」) |
像素差值統計法實現簡單、邏輯清晰,適合尺寸固定、內容高度一致的圖片比對(如自動化測試截圖驗證)。對於複雜場景,建議引入感知哈希或深度學習特徵向量比較(如 ResNet + 余弦相似度)。