JAVA图片相似度判断(2)感知哈希算法

这里用到的关键技术叫做”感知哈希算法”(Perceptual hash algorithm),它的作用是对每张图片生成一个”指纹”(fingerprint)字符串,然后比较不同图片的指纹。结果越接近,就说明图片越相似。
下面是一个最简单的实现:
第一步,缩小尺寸。
将图片缩小到8×8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。
第二步,简化色彩。
将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色。
第三步,计算平均值
计算所有64个像素的灰度平均值。
第四步,比较像素的灰度。
将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。
第五步,计算哈希值。
将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。
得到指纹以后,就可以对比不同的图片,看看64位中有多少位是不一样的。在理论上,这等同于计算”汉明距离”(Hamming distance)。如果不相同的数据位不超过5,就说明两张图片很相似;如果大于10,就说明这是两张不同的图片。
这种算法的优点是简单快速,不受图片大小缩放的影响,缺点是图片的内容不能变更。如果在图片上加几个文字,它就认不出来了。所以,它的最佳用途是根据缩略图,找出原图。
实际应用中,往往采用更强大的pHash算法和SIFT算法,它们能够识别图片的变形。只要变形程度不超过25%,它们就能匹配原图。这些算法虽然更复杂,但是原理与上面的简便算法是一样的,就是先将图片转化成Hash字符串,然后再进行比较。

贴上代码

package com;

import java.awt.image.BufferedImage;
import java.awt.*;

public class ImagePerceptualHashSimilarity {

    public boolean perceptualHashSimilarity(BufferedImage src1, BufferedImage src2) {
        String code1 = this.perceptualHashSimilarity(src1);
        String code2 = this.perceptualHashSimilarity(src2);
        System.out.println(code1);
        System.out.println(code2);
        char[] ch1 = code1.toCharArray();
        char[] ch2 = code2.toCharArray();
        int diffCount = 0;
        for (int i = 0; i < 64; i++) {
            if (ch1[i] != ch2[i]) {
                diffCount++;
            }
        }
        System.out.println(diffCount);
        return diffCount <= 5;
    }

    public String perceptualHashSimilarity(BufferedImage src) {
        int width = 8;
        int height = 8;
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
        Graphics graphics = image.createGraphics();
        graphics.drawImage(src, 0, 0, 8, 8, null);
        int total = 0;
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                int pixel = image.getRGB(j, i);
                int gray = this.gray(pixel);
                total = total + gray;
            }
        }
        StringBuffer res = new StringBuffer();
        int grayAvg = total / (width * height);
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                int pixel = image.getRGB(j, i);
                int gray = this.gray(pixel);
                if (gray >= grayAvg) {
                    res.append("1");
                } else {
                    res.append("0");
                }
            }
        }
        return res.toString();
    }

    private int gray(int rgb) {
        int a = rgb & 0xff000000;//将最高位(24-31)的信息(alpha通道)存储到a变量
        int r = (rgb >> 16) & 0xff;//取出次高位(16-23)红色分量的信息
        int g = (rgb >> 8) & 0xff;//取出中位(8-15)绿色分量的信息
        int b = rgb & 0xff;//取出低位(0-7)蓝色分量的信息
        rgb = (r * 77 + g * 151 + b * 28) >> 8;    // NTSC luma,算出灰度值
        return a | (rgb << 16) | (rgb << 8) | rgb;//将灰度值送入各个颜色分量
    }
}

调用代码

public class Test {
    public static void main(String[]args) throws IOException {
        BufferedImage image1 = ImageIO.read(new File("C:/feihp/work/datatoolkit/jocr/wordspic/timg.jpg"));
        BufferedImage image2 = ImageIO.read(new File("C:/feihp/work/datatoolkit/jocr/wordspic/timg4.jpg"));
        ImagePerceptualHashSimilarity is = new ImagePerceptualHashSimilarity();
        boolean code = is.perceptualHashSimilarity(image1,image2);
        System.out.println(code);
    }
}

 

转载于:https://my.oschina.net/u/220184/blog/1588457

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值