感知哈希算法
以图搜图的关键技术叫做”感知哈希算法”(Perceptual hash algorithm),它的作用是对每张图片生成一个”指纹”(fingerprint)字符串,然后比较不同图片的指纹。结果越接近,就说明图片越相似。
感知哈希算法包括均值哈希(aHash)、感知哈希(pHash)和dHash(差异值哈希)。aHash速度较快,但精确度较低;pHash则反其道而行之,精确度较高但速度较慢;dHash兼顾二者,精确度较高且速度较快。
之后我们会一一实现。
实现过程
- 缩小尺寸。将图片缩小到8×8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。
- 简化色彩。将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色。
- 计算平均值。计算所有64个像素的灰度平均值。
- 比较像素的灰度。将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。
- 计算哈希值。将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。
- 将64位哈希值序列4个4个分割,转为十六进制。
- 计算汉明距离,进行比较。
得到指纹以后,就可以对比不同的图片,看看64位中有多少位是不一样的。在理论上,这等同于计算”汉明距离”(Hamming distance),汉明距离指的是,在信息论中,两个等长字符串之间的汉明距离是两个字符串对应位置的不同字符的个数。
我们一般选择将64位哈希值序列4个4个分割,转为十六进制再进行比较。
通常情况下,如果不相同的数据位不超过5,就说明两张图片很相似;如果大于10,就说明这是两张不同的图片。
python代码
from PIL import Image
# 得到十六进制的哈希字符串
def Get_hash(image_path):
im = Image.open(image_path)
im = im.resize((8, 8), Image.ANTIALIAS).convert('L') # 将图片缩小到8x8,并改成灰度模式
avg = sum(list(im.getdata())) / 64.0 # 得到像素平均值
str = ''.join(map(lambda i: '0' if i < avg else '1', im.getdata())) # 得到哈希字符串
str = ''.join(map(lambda x: '%x' % int(str[x: x + 4], 2), range(0, 64, 4))) # %x:转换无符号十六进制
return str
# 得到汉明距离
def Get_Hamming(str1, str2):
Hamming = 0
for i in range(16):
if str1[i] != str2[i]:
Hamming += 1
return Hamming
def run():
str1 = Get_hash(r'D:\苍井空1.png')
str2 = Get_hash(r'D:\苍井空2.png')
Hamming = Get_Hamming(str1, str2)
print(str1)
print(str2)
print(Hamming)
run()
缺点
这种算法的优点是简单快速,不受图片大小缩放的影响,缺点是图片的内容不能变更。如果在图片上加几个文字,旋转变形一下,它的哈希值差异都会很大。所以,它的最佳用途是根据缩略图,找出原图。