通过将图像大小调整为 8x8 缩略图,然后获取每个图像中相应像素之间 8 位 RGB 色差的均方误差,我得到了令人满意的结果。
步骤 1. 创建缩略图:
BufferedImage img = ImageIO.read(src);
Image thumbnail = img.getScaledInstance(8, 8, Image.SCALE_AREA_AVERAGING);
检查https://community.oracle.com/docs/DOC-983611以了解为什么我选择较慢SCALE_AREA_AVERAGING
的方法而不是更新、更快的方法。
步骤 2.使用Image
BufferedImage
Java 中将 Image 转换为 BufferedImage的toBufferedImage
方法 将缩略图转换为。将结果放入.List<BufferedImage>
步骤 3. 计算均方误差
此方法采用两个相同大小的缩略图大小的图像并返回差异。零表示图像非常相似:
public static double compare(BufferedImage img1, BufferedImage img2) {
int width1 = img1.getWidth();
int width2 = img2.getWidth();
int height1 = img1.getHeight();
int height2 = img2.getHeight();
if ((width1 != width2) || (height1 != height2)) {
throw new IllegalArgumentException("Error: Images dimensions mismatch");
}
int diff2 = 0;
for (int i = 0; i < height1; i++) {
for (int j = 0; j < width1; j++) {
int rgb1 = img1.getRGB(j, i);
int rgb2 = img2.getRGB(j, i);
int r1 = (rgb1 >> 16) & 0xff;
int g1 = (rgb1 >> 8) & 0xff;
int b1 = (rgb1) & 0xff;
int r2 = (rgb2 >> 16) & 0xff;
int g2 = (rgb2 >> 8) & 0xff;
int b2 = (rgb2) & 0xff;
diff2 += Math.pow(r1 - r2, 2) + Math.pow(g1 - g2, 2) + Math.pow(b1 - b2, 2);
}
}
return diff2 * 1.0 / (height1*width1);
}
步骤 4. 实施搜索
这通过简单地找到具有最小差异的图像来工作。根据您的用例,您可能还需要设置一个阈值,超过该阈值不会返回任何图像。在我的应用程序中,始终向用户显示最佳匹配,因此用户可以决定它是否是正确的图像,因此不需要硬编码阈值。
public BufferedImage findImage(List<BufferedImage> haystack, BufferedImage needle) {
double lastDiff = Double.MAX_VALUE;
BufferedImage winner = null;
for(BufferedImage candidate: haystack) {
double diff = compare(candidate, needle);
if(diff < lastDiff) {
lastDiff = diff;
winner = candidate;
}
}
return winner;
}