pHash图像相似度比较算法汇总

翻译原文地址:http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html

在过去的几个月,我不停地寻求“TinEye 如何工作”的答案,或者说它是如何搜索图片的。

结果是我仍没法知道TinEye图片搜索引擎是如何工作的,他们并没有公开他们所用使用的算法细节。然而,根据它返回的结果,呈现给我的是感知哈希算法的一个变种。

这是有感知的

感知哈希(hash)算法描述了一个有可比较的哈希函数的类。图像特征被用于生成独特的(但不是唯一的)指纹,而这些指纹是可比较的。

感知哈希与像MD5和SHA1这样的加密哈希(散列)函数是不同的概念。加密哈希的hash值是随机的,数据用于生成像随机数种子的散列行为,所以相同的数据会产生相同的结果,不同的数据会产生不同的结果。比较两个SHA1的hash值,实际上只告诉我们两个东西,如果hash值是不同的,则数据也是不同的;如果hash值是相同的,则数据是相似的。(因为可能存在hash冲突,相同的hash值会产生不同的数据)。相比之下,感知哈希是可比较的——给你一种两个数据集之间相似的感觉。

我遇到的每一个感知哈希算法都有一个共同的特征:图片可以被放大或缩小,有不同的纵横比,甚至轻微的着色差异(对比度、亮度等),它们依然能够匹配相似的图片,TinEye也有同样的性能。(但TinEye似乎做了更多,我稍后会去了解)

美丽之道

如何创建感知哈希呢?有一些常见的算法,但没有一个是很复杂的。(我总是很惊讶,为什么如此间单却几乎所有的常见算法都能工作)。最间单的算法之一应该是基于低频的均值哈希。

一张高频率的图片可以提供详细的信息,而低频率的图片只显示一个框架;一张大的,详细的图片有很高的频率,而小图片缺乏图像细节,所以都是低频的。为了演示均值哈希算法如何工作,我将使用我妻子—— Alyson Hannigan的图片。

1.缩小尺寸

去除高频和细节的最快方法是缩小图片,将图片缩小到8x8的尺寸,总共64个像素。不要保持纵横比,只需将其变成8*8的正方形。这样就可以比较任意大小的图片,摒弃不同尺寸、比例带来的图片差异。

2.简化色彩

将8*8的小图片转换成灰度图像,将64个像素的颜色(red,green,blue)转换成一种颜色(黑白灰度)。

3.计算平均值

计算所有64个像素的灰度平均值。

4.比较像素的灰度

将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。

5.计算hash值

将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。(我设置的是从左到右,从上到下用二进制保存)。

如果图片放大或缩小,或改变纵横比,结果值也不会改变。增加或减少亮度或对比度,或改变颜色,对hash值都不会太大的影响。最大的优点:计算速度快!

如果你想比较两张图片,为每张图片构造hash值并且计算不同位的个数。(汉明距离)如果这个值为0,则表示这两张图片非常相似,如果汉明距离小于5,则表示有些不同,但比较相近,如果汉明距离大于10则表明完全不同的图片。

效果更佳的pHash

虽然均值哈希更简单且更快速,但是在比较上更死板、僵硬。它可能产生错误的漏洞,如果有一个伽马校正或颜色直方图被用于到图像。这是因为颜色沿着一个非线性标尺 - 改变其中“平均值”的位置,并因此改变哪些高于/低于平均值的比特数。

一个更健壮的算法叫pHash,(我使用的是自己改进后的算法,但概念是一样的) pHash的做法是将均值的方法发挥到极致。使用离散余弦变换(DCT)降低频率。

1.缩小尺寸

pHash以小图片开始,但图片大于8*8,32*32是最好的。这样做的目的是简化了DCT的计算,而不是减小频率。

2.简化色彩

将图片转化成灰度图像,进一步简化计算量。

3.计算DCT

DCT是把图片分解频率聚集和梯状形,虽然JPEG使用8*8的DCT变换,在这里使用32*32的DCT变换。

4.缩小DCT

虽然DCT的结果是32*32大小的矩阵,但我们只要保留左上角的8*8的矩阵,这部分呈现了图片中的最低频率。

5.计算平均值

如同均值哈希一样,计算DCT的均值,

6.进一步减小DCT

这是最主要的一步,根据8*8的DCT矩阵,设置0或1的64位的hash值,大于等于DCT均值的设为”1”,小于DCT均值的设为“0”。结果并不能告诉我们真实性的低频率,只能粗略地告诉我们相对于平均值频率的相对比例。只要图片的整体结构保持不变,hash结果值就不变。能够避免伽马校正或颜色直方图被调整带来的影响。

7.构造hash值

将64bit设置成64位的长整型,组合的次序并不重要,只要保证所有图片都采用同样次序就行了。将32*32的DCT转换成32*32的图像。

与均值哈希一样,pHash同样可以用汉明距离来进行比较。(只需要比较每一位对应的位置并算计不同的位的个数)

同类中的最佳算法?

自从我做了大量关于数码照片取证和巨幅图片的收集工作之后,我需要一种方法来搜索图片,所以,我用了一些不同的感知哈希算法做一个图片搜索工具,根据我并不很科学但长期使用的经验来看,我发现均值哈希比pHash显著地要快。如果你找一些明确的东西,均值Hash是一个极好的算法,例如,我有一张图片的小缩略图,并且我知道它的大图存在于一个容器的某个地方,均值哈希能算法快速地找到它。然而,如果图片有些修改,如过都添加了一些内容或头部叠加在一起,均值哈希就无法处理,虽然pHash比较慢,但它能很好地容忍一些小的变型(变型度小于25%的图片)。

为了验证该算法的性能,我进行了一些简单的测试,发现非等比例的图像缩放对均值Hash算法的性能有很大影响,如我进行测试的图像时640*480的,当我将其缩放为100*100时,两幅图像之间的汉明距离为28,两幅图像的Hash值相差较大,这说明非等比例的图像缩放会会使得基于均值Hash算法的图像检索出现错误,而pHash算法则在计算汉明距离后为4,这说明pHash算法对尺度的变化的鲁棒性强于均值Hash算法。

其次,如果,你运行的服务器像TinEye这样,你就可以不用每次都计算pHash值,我确信它们肯定之前就把pHash值保存在数据库中,核心的比较系统非常快,所以只需花费一次计算的时间,并且几秒之内能进行成千上百次的比较,非常有实用价值。

改进

有许多感知哈希算法的变形能改进它的识别率,例如,在减小尺寸之前可以被剪裁,通过这种方法,主体部分周围额外的空白区域不会产生不同。也可以对图片进行分割,例如,你有一个人脸识别算法,然后你需要计算每张脸的hash值,

可以跟踪一般性的着色(例如,她的头发比蓝色或绿色更红,而背景比黑色更接近白色)或线的相对位置。

如果你能比较图片,那么你就可以做一些很酷的事情。例如, 你可以在GazoPa搜索引擎拖动图片,和TinEye一样,我并不知道GazoPa工作的细节,然而它似乎用的是感知哈希算法的变形,由于哈希把所有东西降低到最低频率,我三个人物线条画的素描可以和其它的图片进行比较——如匹配含有三个人的照片。

猜你喜欢

转载自blog.csdn.net/mago2015/article/details/81137089
今日推荐