机器学习算法原理与实践(一)、基于感知哈希算法的图像搜索实现
来源:互联网 发布:淘宝美工视频教程网盘 编辑:程序博客网 时间:2024/06/01 11:18
【原创】Liu_LongPo 转载请注明出处
【CSDN】http://blog.csdn.net/llp1992
无意中看见一篇博客,是讲仿造google搜图的,链接如下:
Google 以图搜图 - 相似图片搜索原理 - Java实现
觉得挺好玩的,博主使用Java实现的,于是我用 OpenCv实现了下。
根据看到的博文,里面说到,Google图像搜索的关键技术是“感知压缩算法”(Perceptual hash algorithm),它的作用是对每张图片生成一个“指纹”(fingerprint)字符串,然后比较不同图片的指纹。结果越接近,就说明图片越相似。看到这里我就突然来了兴趣想自己实现!
接下来,简单介绍以下感知哈希算法:实验室邹晓艺师兄已经总结得挺好了,所以,我摘取部分:
基于低频的均值哈希
一张图片就是一个二维信号,它包含了不同频率的成分。如下图所示,亮度变化小的区域是低频成分,它描述大范围的信息。而亮度变化剧烈的区域(比如物体的边缘)就是高频的成分,它描述具体的细节。或者说高频可以提供图片详细的信息,而低频可以提供一个框架。
而一张大的,详细的图片有很高的频率,而小图片缺乏图像细节,所以都是低频的。所以我们平时的下采样,也就是缩小图片的过程,实际上是损失高频信息的过程。如下图:
上述这些东西,我们在DSP课堂上都可以学到。
均值哈希算法主要是利用图片的低频信息,其工作过程如下:
(1)缩小尺寸:去除高频和细节的最快方法是缩小图片,将图片缩小到8x8的尺寸,总共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 函数,也就是生成图像的指纹,代码如下:
// 寻找图像指纹// 参数: 输入图像 img ,图像的指纹数组void 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; } } }}
根据上面的原理就可以理解代码。
接下来是计算汉明距离的函数:
// 计算汉明距离// 参数: 两幅图的指纹数组// 输出: 汉明距离int 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张图片
左边第一张为输入图片,第二张和第三张是匹配出来的相似度比较高的图片,第二张其实就是原来的图片,只是改变了大小尺寸,根据下面的命令行打印出来的汉明距离,第一张跟第二张的汉明距离为0,第一张和第三张的汉明距离为10,相似度还是比较高的
这里就不贴出全部代码了,实现起来不是很难,大家可以自己去试试。
- 机器学习算法原理与实践(一)、基于感知哈希算法的图像搜索实现
- 机器学习算法 原理、实现与实践 —— 感知机与梯度下降
- 机器学习算法 原理、实现与实践 —— 感知机与梯度下降
- 机器学习算法原理与实践(六)、感知机算法
- 感知哈希算法原理与实现
- 图像搜索--感知哈希算法
- 机器学习算法原理与编程实践
- 机器学习算法原理与实践(二)、meanshift算法图解以及在图像聚类、目标跟踪中的应用
- 机器学习算法原理与实践(二)、meanshift算法图解以及在图像聚类、目标跟踪中的应用
- 感知哈希算法--实现图片搜索
- 机器学习算法 原理、实现与实践 —— 距离的度量
- 机器学习算法原理与实践(五)、GMM与K-means的那些事
- Java进阶(五十七)-基于感知哈希算法的pHash图像配准算法
- 基于感知哈希算法的pHash图像配准算法
- 机器学习 算法原理与编程实践 第一章 机器学习的基础
- 机器学习算法原理与实践(四)、AdaBoost算法详解与实战
- 图像匹配之感知哈希(pHash)算法的OpenCV实现
- 用C#写的相似图像搜索---感知哈希算法
- Q2:大数据的加减乘除和公约公倍数
- 覆了天下也罢,始终不过一场繁华
- KVO原理(我是来骗星星的)
- KMP算法小解析
- 基础练习 阶乘计算
- 机器学习算法原理与实践(一)、基于感知哈希算法的图像搜索实现
- 安全框架,权限控制
- Eclipse快捷键及其设置
- unity material之tiling和offset属性
- Altium Designer 规则设置
- C++中指针和引用的区别
- 命名管道的封装类
- POJ2318 TOYS 和POJ2398 Toy Storage题解(点在四边形内)(简单几何)
- 清明上河图