图片搜索识别

对于图片搜索识别领域,国内除了百度、腾讯这些大公司,其他还真就没几个人对这块有深入的研究。且人家都做的是广域搜索,相对门槛比较高,咱个小屌丝就不研究了。如果贵们公司准备花钱买类似的服务,推荐使用TinEye。虽然TinEye的MatchEngine足够满足我们的需求,但是价格方面有点小贵。从目前产品的情况来说,我们还没有必要花钱去做这个事情。做还是要做的,开源的东西还是很多的。从咱们的产品考虑,对于垂直领域,图片库的范围就小了非常多,效率方面就无须考虑太多。况且咱们技术也不不过硬,识别准确率方面的标准也可以适当降降。

目前的功能需求总结如下:

1.识别来自手机现场拍摄的图片,手机客户端不处理,直接将图片发送到服务器端,服务器返回识别出的图片信息。

2.能够快速识别出完全一样的图片或类似的图片,识别搜索结果返回时间不能大于5秒。

3.可以识别有一定旋转的的图片。

4.可以识别带有拍照背景的图片,但背景不会太复杂,拍照时会要求用户在尽量简单的背景下拍摄图片。

5.可以识别拍摄不完整的图片,但照片必须包含相关图片信息的大部分内容。

6.可以识别有拍照时过多曝光和光线太暗的图片。

7.识别准确率能到达80%

尝试一,phash:

phash(基于感知哈希算法实现)是一种实现功能需求的方法,他的实现原理可以阅读《相似图片搜索的原理》。且Alexander Polakov贡献了他的python扩展py-phash,相对来说就简单多了。

实现图片识别搜索的基本思路:把用户上传的图片,与图片库所有图片一一对比,找到匹配度最高的。

测试代码如下:

import pHash
image1 = "2.jpg"
image2 = "2a.jpg"
hash1 = pHash.imagehash( image1 )
hash2 = pHash.imagehash( image2 )
print "Hamming distance is %d " % pHash.hamming_distance(hash1, hash2)

通过图片hash的哈明距离来判断图片的相似度,如果hamming距离小于5,俩张图片可以认为是相似的。这个阀值可以自己根据需要来设定,可以通过抽样测试的方法来决定阀值的取值。

对于用户拍摄的照片来讲,如何去掉背景图片是个大问题。autocrop在这方面不仅帮助去掉背景,而且能够矫正拍照时的角度扭曲问题。

但是phash的方法最终被pass掉了,也许phash最大的作用就只是根据缩略图找到原图了。任何变形、残缺(一张图片中的一部分去找原图)、色差等都对结果有非常大的差异。

尝试二,直方图:

判断图片的相似层度,归根揭底是要找到俩张图片相似特征。直方图是用来形容图片特征的一种方式,他描述的是一张图片的颜色的全局分布。而且python的Image模块中有histogram方法能直接获得一张图片的直方图。具体实现参考《计算图像相似度》。把图片统一成RGB格式和256大小尺寸,把原图按照64*64拆分成16张子图片,然后依次比对直方图。这种方法理解起来简单而且有效。

直方图的尝试比之前的phash的方法准确率方面要高很多。但是新问题又来了,图库只有几张图片时还好,当图库的图片有400多张的时候。每次识别匹配的时间太长了,速度慢的已经难以忍受(一分多钟)。

怎么办?时间都耗在哪里?

1.每次搜索识别都要计算图库中图片的直方图的时间。

2.上传的图片与图库中图片的直方图的比较时间。

第一个问题好解决,把图库的直方图尽量都存到缓存里。关于图片比较的时间可以粗率计算,如下:

400(图库中图片数)16(每次识别均需比对16次子图片的直方图)256(每次直方图对比需要对比参考256次直方图坐标) = 1638400 次运算

目前来说也只有对比中的子图片可以简化。比如只对比第6张和第9张子图。按照我们垂直领域图片的需求,根据5、6、9、10这四张中间位置的图片就可以完成我们的识别效果。再简化之,6、9组合或者5、10组合都同样能满足识别需求。这样从原来比较16张子图,简化到只比较2张子图。

0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15

经过这样的改进,效率提高了不少,时间缩减到3m以内。已经基本能够满足项目需求,没有意外就用这个办法了。

尝试三,特征点:

对于程序屌丝来说,如果想更深层次去做图像处理方面的研究,opencv再合适不过了。不选择matlab,是因为他收费,当然matlab绝对是个好东西。opencv封装了检测特征点的一些方法,使用者能在运用检测特征点时方便调用。另官方来看,将用opencv2逐渐替换掉要被废弃opencv。SIFT获取特征点的方法如下:

import cv2
detector = cv2.FeatureDetector_create("SIFT")
descriptor = cv2.DescriptorExtractor_create("SIFT")
skp = detector.detect(img)
skp, sd = descriptor.compute(img, skp)

除了SIFT方法还有其他方法,参看官方文档。做图片比较时,俩张图片如果特征点匹配最多,俩张图片就是最相似的。不过对于这个方法没有深入继续测试,因为感觉效率一样还是个问题。如果仅限对比俩张图片的相似度,这个方法还不错。

总结一下:

1.尝试去发现方法要比自己动手去实现更容易。尤其对于我们这个行业,开源的、有价值的东西很多,更要学会复制和利用现有资源和研究成果。

2.学好英语很重要。第一手的技术资料和成果一定不是中文的(有待商榷)。我一直这么认为,这次查资料的过程中遇到了一件好玩的事。对比这俩片文章《相似图片搜索的原理》《LOOKS LIKE IT》,你会发现内容出奇的相似,而且英文资料比中文早俩个月发表。成为纵屌丝膜拜的技术帝的一条捷径 – 把你发现的国外一手资料翻译一下。其实你也有机会被仰望。

3.不要害怕任何技术难题,硬着头皮上就是了。当有一天你发现技术不过如此,当年的大神也不怎么牛逼。那么恭喜你,你已经很牛逼了!