Java 怎么检测重复图片?Hash 实战

Java检测重复图片的核心是图像哈希,常用aHash、pHash、dHash三种算法;dHash推荐入门,用BufferedImage缩放为9×8灰度图,比较相邻像素差生成64位二进制指纹,汉明距离≤3–5视为重复。

Java 检测重复图片,核心思路是用图像哈希(Image Hash)把图片转成固定长度的指纹,再比对指纹相似度。不是逐像素比较,而是提取视觉特征后降维,兼顾速度和鲁棒性(对缩放、亮度微调、轻微压缩不敏感)。

选哪种 Hash 算法?

常用且适合 Java 实战的有三种:

  • 平均哈希(aHash):最简单快速,适合大量初筛。缩放为 8×8 灰度图 → 算平均值 → 每个像素转 0/1 → 得 64 位二进制 hash。差异 ≤ 5 位大概率是同一图。
  • 感知哈希(pHash):更抗干扰。缩放为 32×32 → DCT 变换 → 取左上 8×8 低频系数 → 算中值 → 生成 64 位 hash。适合识别缩略图、轻微裁剪或 JPEG 压缩后的重复图。
  • dHash(差异哈希):比 aHash 更稳定。缩放为 9×8 → 计算相邻像素差值 → 转 0/1 → 得 64 位 hash。对平移和局部变化更鲁棒,计算也快。

用 Java 快速实现 dHash(推荐入门)

不需要复杂依赖,JDK 自带 BufferedImage 就够用:

(关键逻辑示意,可直接封装为工具方法)

public static String dHash(BufferedImage img) {
    int width = 9, height = 8;
    BufferedImage resized = resizeGray(img, width, height); // 缩放+灰度
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width - 1; j++) {
            int pixelLeft = resized.getRGB(j, i) & 0xFF;
            int pixelRight = resized.getRGB(j + 1, i) & 0xFF;
            sb.append(pixelLeft > pixelRight ? "1" : "0");
        }
    }
    return sb.toString(); // 返回 64 字符二进制串
}

比对时直接计算汉明距离(异或后数 1 的个数),≤ 3–5 通常视为重复。

实战优化建议

  • 先粗筛再精排:用 aHash 或 dHash 扫全量图库生成 hash 表;查重时只对 hash 相似(如汉明距离
  • 预处理统一化:读图后强制转 RGB 格式、去透明通道(alpha 设为白底),避免同图因 PNG/WEBP 存储差异导致 hash 偏移。
  • 用 Long 替代 String 存 hash:64 位二进制可转为 long,比对用 Long.bitCount(h1 ^ h2),性能提升 5–10 倍。
  • 加文件尺寸/宽高过滤:明显尺寸不同

    的图无需算 hash,前置判断能省下大量 IO 和计算。

基本上就这些。Hash 不是万能——完全翻转、大幅裁剪、加水印的图可能漏判,但对日常去重、相册整理、上传防重,dHash + 汉明阈值已足够好用。