基于感知哈希算法的图像搜索实现

无意中看见一篇博客,是讲仿造google搜图的,链接如下:

觉得挺好玩的,博主使用Java实现的,于是我用 OpenCv实现了下。

根据看到的博文,里面说到,Google图像搜索的关键技术是“感知压缩算法”(Perceptual hash algorithm),它的作用是对每张图片生成一个“指纹”(fingerprint)字符串,然后比较不同图片的指纹。结果越接近,就说明图片越相似。看到这里我就突然来了兴趣想自己实现!

接下来,简单介绍以下感知哈希算法:实验室邹晓艺师兄已经总结得挺好了,所以,我摘取部分:

基于低频的均值哈希

一张图片就是一个二维信号,它包含了不同频率的成分。如下图所示,亮度变化小的区域是低频成分,它描述大范围的信息。而亮度变化剧烈的区域(比如物体的边缘)就是高频的成分,它描述具体的细节。或者说高频可以提供图片详细的信息,而低频可以提供一个框架。

而一张大的,详细的图片有很高的频率,而小图片缺乏图像细节,所以都是低频的。所以我们平时的下采样,也就是缩小图片的过程,实际上是损失高频信息的过程。如下图:

上述这些东西,我们在DSP课堂上都可以学到。

均值哈希算法主要是利用图片的低频信息,其工作过程如下:

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

(2)简化色彩:将8*8的小图片转换成灰度图像。

(3)计算平均值:计算所有64个像素的灰度平均值。

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

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

计算一个图片的 hash 指纹就是这么简单,计算出来的hash指纹相对比原来的图片已经丢失了太多的信息了,以至于我们都怀疑这样的指纹是不是真的能够识别出相似的图片。不过,结果当然是不用怀疑的,数学之美与编程之美的结合!

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

因为就像我们看到的,一幅图片被压缩,被转为灰度图,只采集hash指纹,这个过程计算量并不大,而这些指纹就相当于图片的特征。

比较两个图片的相似性,就是先计算这两张图片的hash指纹,也就是64位0或1值,然后计算不同位的个数(汉明距离)。如果这个值为0,则表示这两张图片非常相似,如果汉明距离小于5,则表示有些不同,但比较相近,如果汉明距离大于10则表明完全不同的图片。

实际情况中,我自己写的时候,发现汉明距离小于5的要求太苛刻了,汉明距离接近20的两张图片的相似度还是挺高的。

其实看到这里,你可以根据这个思路自己去实现以下,不用看下面的代码,一直觉得这样才是学习的好方法,看博客拓展思路,自己去实现。

接下来说代码实现:

首先介绍 getImageFinger 函数,也就是生成图像的指纹,代码如下:

getImageFinger(IplImage * img,char *status){int avrpixel= 0;int i,j;CvScalar scalar ;for (i = 0;i<8;i++){for (j = 0;j<8;j++){scalar = cvGet2D(img,i,j);avrpixel += scalar.val[0];}}avrpixel = avrpixel / 64 ;int k = 0;for (i = 0;i<8;i++){for (j = 0;j<8;j++){if (cvGet2D(img,i,j).val[0] > avrpixel){status[k++] = 1;}else{status[k++] = 0;}}}}

根据上面的原理就可以理解代码。

接下来是计算汉明距离的函数:

calHammDist(char * src_img,char * dst_img){int dist = 0;for(int i= 0;i<MAX_PIXEL_NUMBER;i++){if (src_img[i] != dst_img[i]){dist ++;}}return dist;}

搜素图片匹配的过程代码:

// 读取本地某目录下的全部图片进行搜索匹配int show_number = 0;for (int i = 0;i<7;i++){sprintf(FilePath,”F://image//%d.JPG”,i);dst_img = cvLoadImage(FilePath);resz_dst_img = cvCreateImage(cvSize(8,8),IPL_DEPTH_8U,3);cvResize(dst_img,resz_dst_img,1);com_dst_img = cvCreateImage(cvSize(8,8),IPL_DEPTH_8U,1);cvCvtColor(resz_dst_img,com_dst_img,CV_RGB2GRAY);getImageFinger(com_dst_img,status_dst);dis = calHammDist(status_src,status_dst);cout<<” dis :”<<dis<<endl;if (dis<20){showimgindex[show_number++] = i;}}

for 循环中 i 小于7 是因为我只放了7张图片,具体可以自己修改。

效果如下:

这是我本地的要搜索的7张图片

读书破万卷,下笔如有神。

基于感知哈希算法的图像搜索实现

相关文章:

你感兴趣的文章:

标签云: