环境:Python 3.7 + Opencv 4.1.2 + Numpy 1.18.1
一、实验目的
图像的Hash技术是能够快速检索图片的技术之一,它能够快速度量出图像相似度,三种Hash算法都是通过获取图片的Hash值,再比较两张图片Hash值的距离,即对于两个图片的Hash值,各个bit相同的越多,则图片相似度越高。
实验流程:
1.由10张原图,创建90张实验图片
2.分别计算90张图片的aHash,dHash,pHash值
3.分别在三种算法下,从90图中,找出与每张原图最相似的10图
4.分析结果
二、代码模块
1、平均哈希算法(aHash)
首先要对图像进行灰度处理,使用opencv中的COLOR_BGR2GRAY模式,其计算公式为Gray = 0.1140*B + 0.5870*G + 0.2989*R。往后函数一致。
接着计算图像进行灰度处理后图片的所有像素点的平均值;再比较像素灰度值,遍历灰度图片每一个像素,如果大于平均值记录为1,否则为0,最后就能得到信息指纹,组合64个bit位,顺序随意保持一致性即可。
def fun(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 = ''
avg = numpy.mean(gray)
# 灰度大于平均值为1相反为0生成图片的hash值
for i in range(8):
for j in range(8):
if gray[i, j] > avg:
hash_str = hash_str + '1'
else:
hash_str = hash_str + '0'
return hash_str
2、感知哈希算法(pHash)
平均Hash算法过于严格,不够精确,更适合搜索缩略图,为了获得更精确的结果可以选择感知Hash算法,它采用的是DCT(离散余弦变换)来降低频率的方法。
DCT变换类似于离散傅里叶变换,但是只使用实数,相当于一个长度大概是它两倍的离散傅里叶变换,这个离散傅里叶变换是对一个实偶函数进行的(因为一个实偶函数的傅里叶变换仍然是一个实偶函数)。
先缩小图片至3232,便于DCT变换,转化为灰度图后进行DCT变换,保留其左上角集中图片最低频率的88块,计算缩小DCT后的所有像素点的均值,对DCT系数进行编码,大于均值计1,小于计0,得到指纹信息。
def fun(img): # 缩放32*32 img = cv2.resize(img, (32, 32)) # interpolation=cv2.INTER_CUBIC # 转换灰度图 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 将灰度图转为浮点型,再进行dct变换 dct = cv2.dct(numpy.float32(gray)) # 取左上角8*8 dct_roi = dct[0:8, 0:8] hash_str = '' avg = numpy.mean(dct_roi) for i in range(dct_roi.shape[0]): for j in range(dct_roi.shape[1]): if dct_roi[i, j] > avg: hash_str = hash_str + '1' else: hash_str = hash_str + '0' return hash_str
3、差异哈希算法(dHash)
差异Hash算法比前两种算法的速度要快得多,相比aHash,dHash在效率几乎相同的情况下的效果要更好,它是基于渐变实现的。
首先是将图片缩小至9*8,这样每行就有8个差值,转化为灰度图;接着计算差异值,dHash算法工作在相邻像素之间,这样每行9个像素之间产生了8个不同的差异,一共8行,则产生了64个差异值,如果左边的像素比右边的更亮,则记录为1,否则为0。
def fun(img): # 缩放9*8 img = cv2.resize(img, (9, 8), interpolation=cv2.INTER_CUBIC) # 转换灰度图 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) hash_str = '' # 每行左边像素大于右边像素为1,相反为0,生成Hash for i in range(8): for j in range(8): if gray[i, j] > gray[i, j + 1]: hash_str = hash_str + '1' else: hash_str = hash_str + '0' return hash_str
4、相似度
对于两个图片的Hash值,各个bit相同的越多,则图片相似度越高。
n = 0
for m in range(len(x)):
# 不相等则n计数+1,n最终为相似度
if x[m] != y[m]:
n = n + 1
return n
5、计算90图Hash值
读取图片并将图片名称存入列表p,计算图片Hash值存入列表hash_all_2(二进制)与hash_all(十六进制)。两个列表的索引为一一对应关系。
def cmp(x, y):
n = 0
for m in range(len(x)):
# 不相等则n计数+1,n最终为相似度
if x[m] != y[m]:
n = n + 1
return n
p = [] # 90张图
for i in range(10):
p.append('pic/' + str(i) + '.jpg')
p.append('pic/' + str(i) + '.png')
p.append('pic/' + str(i) + '_small.jpg')
p.append('pic/' + str(i) + '_small.png')
p.append('pic/' + str(i) + '_wide.jpg')
p.append('pic/' + str(i) + '_wide.png')
p.append('pic/' + str(i) + 'water.jpg')
p.append('pic/' + str(i) + 'water_small.png')
p.append('pic/' + str(i) + 'water_wide.png')
hash_all_2 = []
hash_all = []
# with open('ahash.txt', 'a') as f1:
for k in range(90):
img = cv2.imread(p[k])
# hash_2 = aHash.fun(img) # Hash算法选择
# hash_2 = pHash.fun(img)
hash_2 = dHash.fun(img)
hash_16 = hex(int(hash_2, 2))[2:].zfill(16)
hash_all_2.append(hash_2)
hash_all.append(hash_16)
# key = p[k] + ' ' + hash_16 + '\n'
# f1.write(key)
# print(k, hash_16, 'save')
6、最相似10图
从原图索引列表中选中原图地址,将原图与90图的Hash值一一对比。由于两图Hash值相似度存在相等重复,故列出Hash值最近的20图,选出靠前10图。
f = xlwt.Workbook() sheet = f.add_sheet('dHash', cell_overwrite_ok=True) ori = [0, 9, 18, 27, 36, 45, 54, 63, 72, 81] # 原图 for i in ori: hash_ori = hash_all_2[i] print(p[i], hash_all[i]) dist = [] # 原图与90图的距离 for j in range(90): hash_cmp = hash_all_2[j] dist.append(cmp(hash_ori, hash_cmp)) print('距离:', dist) temp = [] for k in range(20): # 距离最小的20张图 min_num = dist.index(min(dist)) temp.append(min_num) print(k, p[min_num], 'hash值为', hash_all[min_num], '距离为', dist[min_num]) sheet.write(k + (ori.index(i) * 20), 0, k) sheet.write(k + (ori.index(i) * 20), 1, p[min_num]) sheet.write(k + (ori.index(i) * 20), 2, hash_all[min_num]) sheet.write(k + (ori.index(i) * 20), 3, dist[min_num]) dist[min_num] = 99 f.save('Hash.xls')
三、哈希值对比实验结果
以10为基准,两图距离超过10的判断为两张不一样的图,小于10的为同一张图,每个算法10张原图均作90次比较。
1、平均哈希算法
本实验中,平均哈希算法判断相似图片全部准确,不同图片的相差较大,距离均在20以上。aHash能区别相似图片和差异大的图片。
而在相似图片中,对改图的细节区分不明显,距离一般为0、1。其中3.jpg、7.jpg、8.jpg三张图甚至与其改图的距离全为0。只有2.jpg的多数改图距离在2以上,最高达到7。
2、感知哈希算法
本实验中,感知哈希算法判断相似图片全部准确,多数不同图片的相差较大,在18以上,而1.jpg与2.jpg的距离为12-13,差距较小。dHash能区别相似图片和差异大的图片。
而在相似图片中,对改图细节区分比aHash更明显,未出现原图与其改图距离全0的情况,且大多数情况下,加水印后的改图距离较其他改图距离更大。
3、差异哈希算法
本实验中,差异哈希算法判断相似图片全部准确,不同图片的相差较大,均在20以上,半数在25以上。dHash能区别相似图片和差异大的图片,且在三种算法中效果最好。
在相似图片中,对改图细节区分比aHash更明显,未出现原图与其改图距离全0的情况。
四、后记
起初寻找了Python中有关图像哈希编码的第三方库,通过实验发现ImageHash中的dHash编码中,其1、0的记录与课上所讲相反,有些微差别,且Pillow库与Opencv库的灰度图转换函数也有些许差别,得出的灰度图有微小不同。
俗话说得好:“消除恐惧的最好方法就是面对恐惧,坚持就是胜利。加油。奥力给!”