从0开始用Python实现图片相似度检测

  • 最近突发奇想,想利用Python做一个轮子工程,就是使用Python来做两个图片相似度的比较,不引用市面上成熟的API,是完完全全的轮子工程(后期估计会有一大堆优化,如果我不懒的话)

主要应用的算法就是感知哈希算法。感知哈希算法的核心就是将图像抽象成一个字符串指纹,然后比较两个字符串指纹之间的汉明距离(Hamming Distance)。但是两张图片的大小都不尽相同,直接拿来比较的可比性也比较低,因此首先需要对图像进行一些预处理

图像预处理

首先要做的第一步就是消除图像中颜色的差异,对于颜色的处理无外乎就是二值化,灰度化那么几种。而这里选择二值化显然保留的数据太少了。所以我们选择灰度化,来去除掉图像的颜色。

何为灰度化?简单的来讲图像分为RGB三原色,每一原色都用256位来表示(0-255),白色是(255,255,255)黑色是(0,0,0)。而灰色则是介于黑色和白色之间,所以应该R=G=B才为灰色。因此就延伸出三种灰度化的方法:一、取RGB值三者中最大的一个max(r,b,g)作为RGB值,二、取RGB三者值的平均的值avg(r,g,b)作为RGB值,三、按照人眼对于色域的习惯对RGB颜色加上权值(R*0.3 + G*0.59 + B*0.11)

总体使用上述三种方法来对图像进行灰度化,灰度化后的结果就是这样。值得一提的是如果你是使用PIL来进行图像载入的话,我建议将PIL对象转化成为numpy对象来进行处理,使用numpy.array来构造numpy数组。至于使用opencv的方法就比较简单了,opencv在Python中默认就是使用numpy数组,所以这个无须担心。

再对图像进行了灰度化以后,我们接下来的工作就是要resize图像,说的简单点就是让图像变小。根据坐标我们能看出来原图的尺寸大概有600+px,计算量之大是可想而知的,另外大图片保存的细节多,如果我们使用大图片来进行处理的话,会导致保留细节过多影响后续的匹配。所以在这里我们使用opencv来将图像进行降采样到8×8。(至于降采样的算法,这里先使用opencv库中已经存在的算法来实现,后期应该会继续造轮子,可以期待一下)降采样后的图像变成了下图,实际上保留的特征并不是特别多,隐隐约约可以看到大致轮廓。

图片变哈希

通过上面的resize我们得到了一个8*8大小的也就是64像素的微缩图片,图片内容如下图。这个时候我们就可以来进行下一步的操作了。因为这64个像素是我们通过opencv相应的resize算法挑选出来的,可以认为是能代表图像的64个像素。接下来的任务就是围绕这64个像素计算出图片的指纹

我们引入一个自定概念,叫像素的强度,也就是明亮程度(接近255,255,255的程度)。做如下规定,如果前一个像素的强度大于后一个像素的强度,就记为Ture(1),如果后一个像素的强度大宇前一个像素的强度就记为False(0)。而假设行与行间像素没有关联,因此第一行就可以被抽象成8个0或1,8行可以被抽象成8*8个0或1,所以我们就有了一个8*8的以0或1构成的矩阵,然后将二维矩阵降维变成一维矩阵。就可以得到1*64个以0或1组成的一维矩阵。

PS:在这里还有另一种处理方法,就是求出目前所有像素的平均强度,然后对每个像素和平均强度想比较得出True或者False

将此一维矩阵变成一个64位的二进制数据,将二进制数据翻译成16进制

这样这个图片的指纹就形成了

哈希对哈希

我们可以借用上面的方法形成另一张图片的指纹,这样两个图片的相似度比较就变成了两个字符串之间相似度的比较了,这个时候我们需要引用汉明距离(Hamming Distance),其实汉明距离的应用访问很广,不单单可以用在比较字符串上

例如上面两个字符串,最后一位不相同,因此两个字符串之间的汉明距离为1。借此方法我们可以比较两个hex的区别(当然是比较二进制的区别),这里实现的方法其实还是蛮多的,我个人比较推荐直接使用位运算符中的异或(^)运算符,然后只需要数一下最后异或的结果中有几个1就可以了,这样就产生了两个哈希值之间的汉明距离,根据汉明距离我们就可以给两个图像的相似程度下一个定义

当然在写出来算法后实际测试的过程中,我发现这个算法的健壮性其实不好,在干扰元素很多或者图像很小的情况下没有很好的表现,因此还需要后期优化。虽然这个是谷歌搜图和百度搜图的基础算法,但是弄成这个样子肯定离百度和谷歌差点还很远,后期还要慢慢优化

具体代码已经发布到GitHub并有可能随时更新

声明:实验用图片均来自互联网,如果您认为侵害了您的合法权利请联系我,予以删除。对于本文章涉及到的基础环境的配置不在本文的讨论范围以内,如果有问题请评论留言或者给我发邮件

About the author

NOBUG.IN

1 comment

By NOBUG.IN

Your sidebar area is currently empty. Hurry up and add some widgets.