相似图像搜索的哈希算法有三种:
- 均值哈希算法
- 差值哈希算法
- 感知哈希算法
什么是哈希
- 散列函数(或散列算法,又称哈希函数,英语:Hash Function)是一种从任何一种数据中创建小的数字“指纹”的方法。散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。该函数将数据打乱混合,重新创建一个叫做散列值(hash values,hash codes,hash sums,或hashes)的指纹。散列值通常用一个短的随机字母和数字组成的字符串来代表。
- 通过哈希算法得到的任意长度的二进制值映射为较短的固定长度的二进制值,即哈希值。此外,哈希值是一段数据唯一且极其紧凑的数值表示形式,如果通过哈希一段明文得到哈希值,哪怕只更改该段明文中的任意一个字母,随后得到的哈希值都将不同。
- 哈希算法是一个函数,能够把几乎所有的数字文件都转换成一串由数字和字母构成的看似乱码的字符串
哈希函数作为一种加密函数,其拥有两个最重要特点:
- 不可逆性。输入信息得出输出的那个看似乱码的字符串(哈希值)非常容易,但是从输出的字符串反推出输入的结果却是却非常非常难。
- 输出值唯一性和不可预测性。只要输入的信息有一点点区别,那么根据哈希算法得出来的输出值也相差甚远
汉明距离
两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。
均值哈希算法
步骤
- 缩放:图片缩放为8*8,保留结构,除去细节。
- 灰度化:转换为灰度图。
- 求平均值:计算灰度图所有像素的平均值。
- 比较:像素值大于平均值记作1,相反记作0,总共64位。
- 生成hash:将上述步骤生成的1和0按顺序组合起来既是图片的指(hash)。
- 对比指纹:将两幅图的指纹对比,计算汉明距离,即两个64位的hash值有多少位是不一样的,不相同位数越少,图片越相似。
差值哈希算法
差值哈希算法相较于均值哈希算法,前期和后期基本相同,只有中间比较hash有变化。
步骤
- 缩放:图片缩放为8*9,保留结构,除去细节。
- 灰度化:转换为灰度图。
- 比较:像素值大于后一个像素值记作1,相反记作0。本行不与下一行对比,每行9个像素,八个差值,有8行,总共64位
- 生成hash:将上述步骤生成的1和0按顺序组合起来既是图片的指纹(hash)。
- 对比指纹:将两幅图的指纹对比,计算汉明距离,即两个64位的hash值有多少位是不一样的,不相同位数越少,图片越相似。
三种算法的比较:
- aHash:均值哈希。速度比较快,但是有时不太精确。
- pHash:感知哈希。精确度较高,但是速度方面较差一些。
- dHash:差值哈希。精确度较高,且速度也非常快。
感知哈希算法
感知哈希算法,它采用的是DCT(离散余弦变换)的方法。
步骤:
- 缩小图片:32 * 32是一个较好的大小,这样方便DCT计算
- 转化为灰度图:把缩放后的图片转化为灰度图。
- 计算DCT:DCT把图片分离成分率的集合
- 缩小DCT:DCT计算后的矩阵是32 * 32,保留左上角的8 * 8,这些代表图片的最低频率。
- 计算平均值:计算缩小DCT后的所有像素点的平均值。
- 进一步减小DCT:大于平均值记录为1,反之记录为0.
- 得到信息指纹:组合64个信息位,顺序随意保持一致性。
- 最后比对两张图片的指纹,获得汉明距离即可。
代码部分(均值&差值哈希)
# 图像相似度比较哈希算法
import cv2
import numpy as np
# 均值哈希算法
def aHash(img):
# 缩放为8*8
img = cv2.resize(img,(8,8),interpolation=cv2.INTER_CUBIC)
# 转换为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# s为像素和初值为0,hash_str为hash值为''
s = 0
hash_str = ''
# 遍历累加求像素和
for i in range(8):
for j in range(8):
s = s + gray[i,j]
# 求平均灰度
avg = s/64
# 灰度大于平均值为1相反为0生成图片的 hash 值
for i in range(8):
for j in range(8):
if gray[i,j]>avg:
hash_str += '1'
else:
hash_str += '0'
return hash_str
# 插值算法
def dHash(img):
# 缩放为8*9
img = cv2.resize(img,(9,8),interpolation=cv2.INTER_CUBIC)
# 转换灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
hash_str = ''
# 每行前一个像素大于后一个像素为1,相反为0,生成哈希
for i in range(8):
for j in range(8):
if gray[i,j]>gray[i,j+1]:
hash_str += '1'
else:
hash_str += '0'
return hash_str
# Hash值对比
def cmpHash(hash1, hash2):
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
img1 = cv2.imread("sheep1.jpg")
img2 = cv2.imread("sheep2.jpg")
hash1 = aHash(img1)
hash2 = aHash(img2)
print(hash1)
print(hash2)
n = cmpHash(hash1, hash2)
print("均值哈希算法相似度:", n)
hash1 = dHash(img1)
hash2 = dHash(img2)
print(hash1)
print(hash2)
n = cmpHash(hash1, hash2)
print("差值哈希算法相似度:", n)
输出
0100010111110101100111110111111111100010000010001000001010000000
0000000001000000011011000011111011111111110000001111000100110101
均值哈希算法相似度: 33
1000101010111010100001011010010110101001111101011010100110110100
0010001001010100011001110101011101101101110100011001010110010101
差值哈希算法相似度: 29