1.前言
目前“以图搜图”的引擎越来越多,可参考博文:
“以图搜图”的引擎,之前很好奇他们是如何进行检索的,偶然间看到了一篇博客,上面说Google和Tineye主要利用的算法是感知哈希算法(Perceptual hash algorithm),它的作用是对每张图片生成一个"指纹"(fingerprint)字符串,然后比较不同图片的指纹。结果越接近,就说明图片越相似,里面介绍的原理也比较简单,正好目前也在做图像检索方面的课题,就用OpenCV实现了一下,供大家参考,本篇博文主要介绍如何通过OpenCV实现均值Hash和pHash算法,基本原理和流程会在代码的注释中详细说明。
2.均值Hash算法
[cpp] view plain copy
1. //均值Hash算法
2. string HashValue(Mat &src)
3. {
4. '\0');
5. Mat img;
6. if(src.channels()==3)
7. cvtColor(src,img,CV_BGR2GRAY);
8. else
9. img=src.clone();
10. /*第一步,缩小尺寸。
11. 将图片缩小到8x8的尺寸,总共64个像素,去除图片的细节*/
12.
13. resize(img,img,Size(8,8));
14. /* 第二步,简化色彩(Color Reduce)。
15. 将缩小后的图片,转为64级灰度。*/
16.
17. uchar *pData;
18. for(int i=0;i<img.rows;i++)
19. {
20. pData = img.ptr<uchar>(i);
21. for(int j=0;j<img.cols;j++)
22. {
23. pData[j]=pData[j]/4; }
24. }
25.
26. /* 第三步,计算平均值。
27. 计算所有64个像素的灰度平均值。*/
28. int average = mean(img).val[0];
29.
30. /* 第四步,比较像素的灰度。
31. 将每个像素的灰度,与平均值进行比较。大于或等于平均值记为1,小于平均值记为0*/
32. Mat mask= (img>=(uchar)average);
33.
34. /* 第五步,计算哈希值。*/
35. int index = 0;
36. for(int i=0;i<mask.rows;i++)
37. {
38. pData = mask.ptr<uchar>(i);
39. for(int j=0;j<mask.cols;j++)
40. {
41. if(pData[j]==0)
42. '0';
43. else
44. '1';
45. }
46. }
47. return rst;
48. }
49.
3. pHash算法
[cpp] view plain copy
1. //pHash算法
2. string pHashValue(Mat &src)
3. {
4. Mat img ,dst;
5. '\0');
6. double dIdex[64];
7. double mean = 0.0;
8. int k = 0;
9. if(src.channels()==3)
10. {
11. cvtColor(src,src,CV_BGR2GRAY);
12. double>(src);
13. }
14. else
15. {
16. double>(src);
17. }
18.
19. /* 第一步,缩放尺寸*/
20. resize(img, img, Size(8,8));
21.
22. /* 第二步,离散余弦变换,DCT系数求取*/
23. dct(img, dst);
24.
25. /* 第三步,求取DCT系数均值(左上角8*8区块的DCT系数)*/
26. for (int i = 0; i < 8; ++i) {
27. for (int j = 0; j < 8; ++j)
28. {
29. double>(i, j);
30. double>(i, j)/64;
31. ++k;
32. }
33. }
34.
35. /* 第四步,计算哈希值。*/
36. for (int i =0;i<64;++i)
37. {
38. if (dIdex[i]>=mean)
39. {
40. '1';
41. }
42. else
43. {
44. '0';
45. }
46. }
47. return rst;
48. }
4.汉明距离计算
通过上面两段代码就可以计算出图像的Hash值,检索的时候一般采用汉明距离来进行判断两幅图像的相似性,一般情况下认为汉明距离小于5,就可以认为两幅图像时相似的。汉明具体计算实现:
[cpp] view plain copy
1. //汉明距离计算
2. int HanmingDistance(string &str1,string &str2)
3. {
4. if((str1.size()!=64)||(str2.size()!=64))
5. return -1;
6. int difference = 0;
7. for(int i=0;i<64;i++)
8. {
9. if(str1[i]!=str2[i])
10. difference++;
11. }
12. return difference;
13. }
14.
5.算法性能测试
为了验证该算法的性能,我进行了一些简单的测试,发现非等比例的图像缩放对均值Hash算法的性能有很大影响,如我进行测试的图像时640*480的,当我将其缩放为100*100时,两幅图像之间的汉明距离为28,两幅图像的Hash值相差较大,这说明非等比例的图像缩放会会使得基于均值Hash算法的图像检索出现错误,而pHash算法则在计算汉明距离后为4,这说明pHash算法对尺度的变化的鲁棒性强于均值Hash算法。
接下来我又对其对旋转的鲁棒性进行了一定的测试,测试图像如下所示
img1 img2
img3 img4
均值Hash算法测试结果:
pHash算法测试结果:
从测试结果中可以看出无论是均值Hash算法还是pHash算法,对旋转都不具有鲁棒性,只是pHash算法相对来说好一些, 一个真正的可商用的“以图搜图”引擎, 仍然需要对其进行改进,类似于原文中说的一样,如果不对其进行改进,目前只能由于以缩略图查找原图的情况。