图片识别——感知哈希算法 - 高飞网
1336 人阅读

图片识别——感知哈希算法

2017-07-28 02:09:46

    所谓感知哈希算法(Perceptual hash algorithm,PHA),它是用于对多种格式的数据生成一个指纹的算法。当然本文只讨论图片格式。感知哈希不同于密码哈希(如md5云云),它对于相似特征的输入,会有相似的输出;而密码哈希,依赖于雪崩效应,对于非常微小的输入,都会有完全不同的输出。感知哈希算法被广泛应用于网络上的侵权查找,还有数字取证,因为它有能力可以发现两个相似的数据(根据比特位对比)。Perceptual hashing - WikipediaLooks Like It - The Hacker Factor Blog

    相对于AHash算法,pHash是感知算法的增强版,它更为健壮,因为它使用了离散余弦变换(discrete cosine transform,DCT)来降频。

算法步骤

  1. 缩小尺寸。像aHash算法一样,pHash算法也从小图开始。然而图片要比8*8大一些,32 * 32是一个较好的尺寸,这样做并不是因为需要降低高频,而是更方便DCT计算。

  2. 简化色彩。转化为灰度图. 把缩放后的图片转化为256阶的灰度图
  3. 计算DCT。DCT(离散余弦变换)将图像转为频率和标量的集合。
  4. 减少DCT。DCT是32*32的,只需要保留左上角8*8的位置,这块代表了图片的最低频。
  5. 计算平均值。像均值哈希一样,这也要计算DCT值
  6. 进一步减少DCT。这是神秘的一个步骤。将64个hash位,依据它大于或小于上面计算的均值,设置为0或1。这个结果告诉我们的并不是真实的低频,仅仅告诉我们到频率平均值的相对大小。如果图片的总体结构是一致的,那么该结果将不会有多少变化;图片调整不会影响对伽马和颜色直方布的保留。
  7. 构造哈希值。将64位转为64位的整数。次序是次要的,只要所有图片使用一种次序即可。要想看一下指纹长啥样,简单地设置值的集合(如果是1就设置为255,0就设置为-255),然后转换32*32DCT(0是高频)回32*32的图像
    =8a0303f6df3ec8cd
    大概看一眼上面的图,可能看起来像一些随机的斑点……但是仔细看一下,这是个围绕它头部的暗环,另外一条暗线条背景(图片右侧)就像一个暗点。

    与aHash一样,pHash也用汉明距离来比较。

java实现


/**
 * 感知哈希算法/Perceptual hash algorithm/PHA
 * <p>
 * 为了获得更精确的结果可以选择感知哈希算法,它采用的是DCT(离散余弦变换)来降低频率的方法
 * 
 * @author xuyanhua
 * @data Jan 10, 2017 1:09:46 AM
 */
public class PHash {

    /**
     * 步骤组合
     * 
     * @param srcFile
     * @return
     * @throws IOException
     */
    public static long fingerprint(String srcFile) throws IOException {
        BufferedImage image = ImageIO.read(new File(srcFile));
        image = ImageUtil.trim(image);
        /*
         * 1.缩小尺寸. 32 * 32是一个较好的大小,这样方便DCT计算
         */
        BufferedImage image32x32 = ImageUtil.resize(image, 32, 32);
        /*
         * 2.简化色彩,转化为灰度图. 把缩放后的图片转化为256阶的灰度图
         */
        int width = image32x32.getWidth();
        int height = image32x32.getHeight();
        int[] grayPix = new int[width * height];
        int i = 0;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                int rgb = image32x32.getRGB(x, y);
                int r = rgb >> 16 & 0xff;
                int g = rgb >> 8 & 0xff;
                int b = rgb >> 0 & 0xff;
                int gray = (r * 30 + g * 59 + b * 11) / 100;
                grayPix[i++] = gray;
            }
        }
        /*
         * 3.计算DCT(离散余弦变换)并缩小DCT. DCT的结果是32*32大小的矩阵,但我们只要保留左上角的8*8的矩阵,所以只需要设置两层的for循环是从0到7就可以了
         */
        int[] dct = Transformation.DCT(grayPix, 32);
        int sum = 0;
        for (i = 0; i < dct.length; i++) {
            sum += dct[i];
        }
        /*
         * 4.计算平均值
         */
        int avg = sum / dct.length;
        /*
         * 5.比较像素灰度值,遍历灰度图片每一个像素,如果大于平均值记录为1,否则为0.并获取指纹
         */
        long finger = 0;
        for (i = 63; i >= 0; i--) {
            long b = (long) (grayPix[i] > avg ? 1 : 0);
            finger |= b << i;
        }
        return finger;
    }
}


与均值哈希比较

算法优点缺点
均值哈希(aHash)速度快。适用于知道一个缩略图,在大量原图中查找图片内容改变会对结果hash值造成影响,如在图片上加字或水印
pHash对图片的修改比较宽容,如图片改变不超过25%都可以出来相对aHash慢

aHash,本质上是对颜色的比较;pHash由于做了DCT,是对频率的比较。


特别感谢

《Looks Like It》:http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html

《CPP实现》:http://blog.csdn.net/forthcriminson/article/details/8729000

《相似图片搜索原理 - Java实现》:http://blog.csdn.net/luohong722/article/details/7100058

《图像搜索其实也不难》:http://blog.csdn.net/luoweifu/article/details/8220992

《基于感知哈希算法的视觉目标跟踪》:http://blog.csdn.net/zouxy09/article/details/17471401

《Perceptual hashing》:https://en.wikipedia.org/wiki/Perceptual_hashing


另外一种基于渐变的感知哈希算法:差异哈希(Different Hash,dHash)


还没有评论!