感知哈希算法计算图像相似度
实现图片相似度比较的哈希算法有三种:均值哈希算法,差值哈希算法,感知哈希算法
下文简单介绍感知哈希算法,其他算法等后续文档再述。
感知哈希算法是一个比均值哈希算法更为健壮的一种算法,与均值哈希算法的区别在于感知哈希算法是通过DCT(离散余弦变换)来获取图片的低频信息。
离散余弦变换(DCT)是种图像压缩算法,它将图像从像素域变换到频率域。然后一般图像都存在很多冗余和相关性的,所以转换到频率域之后,只有很少的一部分频率分量的系数才不为0,大部分系数都为0(或者说接近于0)。经过DCT变换后的系数矩阵从左上角到右下角频率越来越高,因此图片的能量主要保留在左上角的低频系数上了。
具体步骤:
(1)缩小尺寸:pHash以小图片开始,但图片大于8x8,32x32是最好的。这样做的目的是简化了DCT的计算,而不是减小频率。
(2)简化色彩:将图片转化成灰度图像,进一步简化计算量。
(3)计算DCT:计算图片的DCT变换,得到32x32的DCT系数矩阵。
(4)缩小DCT:虽然DCT的结果是32x32大小的矩阵,但我们只要保留左上角的8x8的矩阵,这部分呈现了图片中的最低频率。
(5)计算平均值:如同均值哈希一样,计算DCT的均值。
(6)计算hash值:这是最主要的一步,根据8x8的DCT矩阵,设置0或1的64位的hash值,大于等于DCT均值的设为”1”,小于DCT均值的设为“0”。组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。
分析: 结果并不能告诉我们真实性的低频率,只能粗略地告诉我们相对于平均值频率的相对比例。只要图片的整体结构保持不变,hash结果值就不变。能够避免伽马校正或颜色直方图被调整带来的影响。对于变形程度在25%以内的图片也能精准识别。
代码实例:
# -*- coding: utf-8 -*- import cv2 import numpy as np import os import glob dir_data=os.path.dirname(os.path.dirname(__file__)) # Hash值对比 def cmpHash(hash1, hash2,shape=(10, 10)): n = 0 # hash长度不同则返回-1代表传参出错 if len(hash1)!=len(hash2): return -1 # 遍历判断 for i in range(len(hash1)): # 相等则n计数+1,n最终为相似度 if hash1[i] == hash2[i]: n = n + 1 return n/(shape[0]*shape[1]) # 感知哈希算法(pHash) def pHash(img,shape=(10,10)): # 缩放32*32 img = cv2.resize(img, (115, 114)) # , interpolation=cv2.INTER_CUBIC # 转换为灰度图 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 将灰度图转为浮点型,再进行dct变换 dct = cv2.dct(np.float32(gray)) # opencv实现的掩码操作 dct_roi = dct[0:10, 0:10] hash = [] avreage = np.mean(dct_roi) for i in range(dct_roi.shape[0]): for j in range(dct_roi.shape[1]): if dct_roi[i, j] > avreage: hash.append(1) else: hash.append(0) return hash def match_result(): for jpgfile in glob.glob(dir_data+'\POC\img/*.jpg'): file=os.path.basename(jpgfile) img1 = cv2.imread('images/template.jpg') img1 = cv2.resize(img1,(115,114)) img2 = cv2.imread(jpgfile) hash1 = pHash(img1) hash2 = pHash(img2) n = cmpHash(hash1, hash2) print('{}的相似度是:{}'.format(file, n)) if __name__=="__main__": match_result()
以上代码运行结果如下: