之前在伯乐在线上看过一篇译文,《用Python和OpenCV创建一个图片搜索引擎的完整指南》。我对照了原文,翻译质量不错。原文作者是个外国小哥,文章写的很好,从图像处理的基础概念入手,由浅入深的介绍了利用OpenCV进行图片搜索的方法。在更进一步学习了OpenCV的帮助文档之后,我我觉得他在此文中使用的方法有几处可以加以改进,以便提高搜索质量。对于使用OpenCV不太熟悉的同学,建议移步原文先。
注,小哥也许为了快速直观,采用Python作demo的开发语言,我出于语言的移植性考虑,选择了C++。
1. 卡方检测值的计算
对于两幅图片的直方图(histogram)比较,由于选用了卡方相似度检测(Chi-Square),所以应该使用OpenCV的API, 而非自己实现。
小哥的python代码:
def chi2_distance(self, histA, histB, eps = 1e-10):
# compute the chi-squared distance
d = 0.5 * np.sum([((a - b) ** 2) / (a + b + eps)
for (a, b) in zip(histA, histB)])
# return the chi-squared distance
return d
小哥用的histA, histB, 就是将图片的HSV矩阵转换成的直方图,代码里就是一个矩阵。不过由于opencv提供了一个API: compareHist(),这一切其实可以很简单。个人觉得手动实现大矩阵的运算完全没有必要,容易出错不说,效率也是一个大问题。
double val = compareHist(Mat hist1, Mat hist2, HISTCMP_CHISQR)
目前opencv提供了四种相似读检测方法: Correlation, Chi-Square, Intersection, Bhattacharyya distance。(见
opencv doc)考虑到直方图的数据分布特点,Chi-Square比较合适。而且Chi-Square的检测数值越小说明相似读越高,原图与自身比较结果为0,这也非常直观,方便排序。小哥这个选择,还是无比正确的。
2. 图片不同部分的权重
小哥把每幅图片分成五个部分,中间一个椭圆,加上四角。计算出这五部分的直方图后,分别与原图的五个部分的直方图进行相似度检测。
注:此图出自原文
这个思路真是挺赞的,考虑到拍照时用户视野正中心一般就是目标物,这个方法可以单独对中心视野部分进行比较,然后对五个卡方检测值进行加和。
代码实现中,我倒觉得,既然五个部分中认定中心椭圆优先级最高,那么应该给予不同权重,比如中心椭圆权重最高,其他四角均分。具体数值可自行测试得到。
3. HSV色彩空间的bins选择
计算直方图时,需要将图片投射到HSV色彩空间上表示出来。这里HSV各自的bins选择有些讲究。因为之后生成的矩阵,其尺寸等于H(bins) * S(bins) * V(bins)。如果取的值太大,会严重影响计算性能,取的值太小,又会降低比较的准确性。
小哥原文中取的{H = 8, S = 12, V = 3};opencv manual中关于直方图比较的demo 中,取的{H = 50, S = 60}, 直接忽略色度V。
经过测试,我的建议是:
1. 色度的bins应该保留,不必过大,保持3的倍数即可,因为这样可以对应到 R G B三原色。
2. H 和 S 的bins 可以取大一些,属于图片信息中更加重要的坐标。H = 8, S = 12 肯定太小了一些,不过是否需要50,60这个数量可以通过测试看看效果。
有兴趣的同学可以进一步参考 stackoverflow
小结:
我经过测试,提出了几点对于“利用OpenCV创建图片搜索引擎“的改进。完整的C++代码实现,可参考我的github。
另,这位小哥的网站上专门刊登一些利用OpenCV处理图片的方法和教程,有不少很有意思的帖子,有兴趣的同学不妨看看,不过有些是收费的:)