"感知哈希算法"(Perceptual hash algorithm),它的作用是对每张图片生成一个"指纹"(fingerprint)字符串,然后比较不同图片的指纹。结果越接近,就说明图片越相似。
一般步骤:
- 缩小图片:32 * 32是一个较好的大小,这样方便DCT计算
- 转化为灰度图:把缩放后的图片转化为256阶的灰度图。(具体算法见平均哈希算法步骤)
- 计算DCT:DCT把图片分离成分率的集合
- 缩小DCT:DCT计算后的矩阵是32 * 32,保留左上角的8 * 8,这些代表的图片的最低频率
- 计算平均值:计算缩小DCT后的所有像素点的平均值。
- 进一步减小DCT:大于平均值记录为1,反之记录为0.
- 得到信息指纹:组合64个信息位,顺序随意保持一致性。
- 最后比对两张图片的指纹,获得汉明距离即可。
实现代码:
import cv2
import numpy as np
from itertools import chain
class PHash(object):
@staticmethod
def pHash(img_name):
"""
get image pHash value
"""
# 加载并调整图片为32x32灰度图片
img = cv2.imread(img_name, 0)
img = cv2.resize(img, (64, 64), interpolation=cv2.INTER_CUBIC)
# 创建二维列表
h, w = img.shape[:2]
vis0 = np.zeros((h, w), np.float32)
vis0[:h, :w] = img # 填充数据
# 二维Dct变换
vis1 = cv2.dct(cv2.dct(vis0))
# cv.SaveImage('a.jpg',cv.fromarray(vis0)) #保存图片
vis1.resize((32, 32), refcheck=False)
# 把二维list变成一维list
img_list = list(chain.from_iterable(vis1))
# 计算均值
avg = sum(img_list) * 1. / len(img_list)
avg_list = ['0' if i < avg else '1' for i in img_list]
# 得到哈希值
return ''.join(['%x' % int(''.join(avg_list[x:x + 4]), 2) for x in range(0, 32*32, 4)])
@staticmethod
def hammingDist(s1, s2):
"""
计算两张图片的汉明距离
"""
assert len(s1) == len(s2)
return sum([ch1 != ch2 for ch1, ch2 in zip(s1, s2)])
if __name__ == '__main__':
HASH1 = PHash.pHash('001.jpg')
HASH2 = PHash.pHash('002.jpg')
distance = PHash.hammingDist(HASH1, HASH2)
print(distance)
out_score = 1 - distance * 1. / (32 * 32 / 4)
print(out_score)