以图搜图(二):Python实现pHash算法

  • 发布时间:2016年4月28日 10:34
  • 作者:杨仕航
  • 分类标签: Python
  • 阅读(30850)
  • 评论(1)

上篇博文主要讲的是用dHash均值哈希感知算法进行以图搜图。《以图搜图(一):Python实现dHash算法》

现有3张图片,用前面的dHash均值哈希感知算法计算哈希值。

1.jpg

2.jpg:1.jpg旋转90度


3.jpg:1.jpg旋转5度


dHash均值哈希感知算法计算结果:

1.jpg:270f078fd1fdffff

2.jpg:f8f0e1f0eaefcfff

3.jpg:e70f058f81f1f1ff

1.jpg和2.jpg(旋转90度)的汉明距离是13;1.jpg和3.jpg(旋转5度)的汉明距离是5。(汉明距离是两个字符串对应位置对比,总共不同的个数)

很明显,旋转了90度汉明距离变得很大。在dHash算法中,它们是不同的。而我们肉眼可以看出其实是一样的。前面说过dHash算法比较较真、比较敏感。若要处理一定程度的变形,得要调整一下这个算法。

pHash算法就是基于dHash算法调整而来的,用第一次计算得到的值进行余弦变换。所以命名为余弦哈希感知算法。它可以识别变形程度在25%以内的图片。


大致原理和处理过程是这样:

把图片缩小到32x32的尺寸,并转为灰度模式。

对图片进行二维余弦变换,得到下图。高亮的数据集中在左上角。

主要特征数据就集中在图片左上角的区域,我们去左上角的8x8区域。对这个区域求均值哈希值(参考上一篇的方法)

像素值 = [

    185.6844940185547, 265.2283630371094, 295.5816650390625, 280.8827209472656, 194.1041259765625, 202.2652130126953, 219.96258544921875, 292.495361328125, 178.82818603515625, 233.83262634277344, 273.0287170410156, 149.4671630859375, 168.61572265625, 196.33615112304688, 203.49485778808594, 212.27850341796875, 132.66966247558594, 129.8767852783203, 158.14573669433594, 130.48318481445312, 183.61569213867188, 163.86473083496094, 130.6190643310547, 97.9937515258789, 62.876373291015625, 139.2428436279297, 104.12135314941406, 84.58297729492188, 65.413818359375, 50.1722297668457, 32.628944396972656, 15.74152660369873, 291.3837890625, 352.196044921875, 362.098388671875, 359.0899353027344, 435.290771484375, 385.5643310546875, 471.4906005859375, 341.85638427734375, 364.1877136230469, 376.7964172363281, 379.27386474609375, 259.00885009765625, 311.5459289550781, 309.8655700683594, 277.6601867675781, 237.609375, 203.56141662597656, 181.1670684814453, 178.15585327148438, 250.11349487304688, 233.9508514404297, 220.26385498046875, 179.6764678955078, 163.84234619140625, 129.07568359375, 149.32899475097656, 126.58440399169922, 104.8794174194336, 85.072265625, 61.521141052246094, 42.12876510620117, 21.413537979125977

]

平均值 = 204.3717007189989


得到这个平均值之后,再和每个像素对比。像素值大于平均值的标记成1,小于或等于平均值的标记成0。组成64个数字的字符串(看起来也是一串二进制的)。


降噪结果 = [

    0, 1, 1, 1, 0, 0, 1, 1,

    0, 1, 1, 0, 0, 0, 0, 1,

    0, 0, 0, 0, 0, 0, 0, 0,

    0, 0, 0, 0, 0, 0, 0, 0,

    1, 1, 1, 1, 1, 1, 1, 1,

    1, 1, 1, 1, 1, 1, 1, 1,

    0, 0, 0, 1, 1, 1, 0, 0,

    0, 0, 0, 0, 0, 0, 0, 0]

64位字符串 = '0111001101100001000000000000000011111111111111110001110000000000'

每4个数字,由2进制转成16进制,得到哈希值 = '73610000ffff1c00'


Python代码如下:

#coding:utf-8
"""
author: Haddy Yang(杨仕航)
date: 2016-04-05
filename: opencv_phash.py
decription: pHash算法(感知哈希算法)实现
            把图片转成一个Hash值
测试环境:python2.7 win7 numpy1.11 OpenCV2.4.12
"""
#numpy是OpenCV必须的科学计算库
#OpenCV在Windows系统安装比较麻烦,下载OpenCV2.4的安装程序,执行会解压得到一个目录。
#打开目录build/python/2.7,复制里面的cv2.pyd文件到C:\Python27\Lib\site-packages中即可
import cv2
import cv2.cv as cv
import numpy as np

from compiler.ast import flatten
import sys

def pHash(imgfile):
	"""get image pHash value"""
	#加载并调整图片为32x32灰度图片
	img=cv2.imread(imgfile, cv2.CV_LOAD_IMAGE_GRAYSCALE)
	img=cv2.resize(img,(32,32),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(8,8)

	#把二维list变成一维list
	img_list=flatten(vis1.tolist()) 

	#计算均值
	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,64,4)])

if __name__ == '__main__':
	if len(sys.argv)!=2:
		print 'Error: args error, sample: opencv_phash 1.jpg'
	else:
		print pHash(sys.argv[1])

用这个算法计算2.jpg和3.jpg的哈希值和与1.jpg对比的汉明距离分别是:

2.jpg:7ffc0000ffffe000,汉明距离是5

3.jpg:7fff0000fffff800,汉明距离是5

很明显,pHash算法得到的汉明距离更加符合我们的要求。

上一篇:Excel筛选公式详解

下一篇:以图搜图(一):Python实现dHash算法

评论列表

schunlee

schunlee

现在⁣⁣imagehash库也可以用,⁣imagehash.average_hash方法。

2019-08-06 11:53 回复

新的评论

清空