感知哈希算法之基于低频的均值哈希
来源:互联网 发布:金十数据软件 编辑:程序博客网 时间:2024/06/07 11:23
1.1前言
在Google的首页上有"相似图片搜索"功能。你可以用一张图片,搜索互联网上所有与它相似的图片。点击搜索框中照相机的图标。
一个对话框会出现。
你输入网片的网址,或者直接上传图片,Google就会找出与其相似的图片。下面这张图片是美国女演员Alyson Hannigan。
上传后,Google返回如下结果:
类似的"相似图片搜索引擎"还有不少,TinEye甚至可以找出照片的拍摄背景。
==========================================================
这种技术的原理是什么?计算机怎么知道两张图片相似呢?
根据Neal Krawetz博士的解释,原理非常简单易懂。我们可以用一个快速算法,就达到基本的效果。
这里的关键技术叫做"感知哈希算法"(Perceptual hashalgorithm),它的作用是对每张图片生成一个"指纹"(fingerprint)字符串,然后比较不同图片的指纹。结果越接近,就说明图片越相似。。达到图片比较目的且利用信息指纹比较有三种算法,这些算法都很易懂,下面先介绍基于低频的均值哈希,pHash和差异哈希算法(dHash)后续介绍:
1.2基于低频的均值哈希
下面是一个最简单的一张图片就是一个二维信号,它包含了不同频率的成分。如下图所示,亮度变化小的区域是低频成分,它描述大范围的信息。而亮度变化剧烈的区域(比如物体的边缘)就是高频的成分,它描述具体的细节。或者说高频可以提供图片详细的信息,而低频可以提供一个框架。
而一张大的,详细的图片有很高的频率,而小图片缺乏图像细节,所以都是低频的。所以我们平时的下采样,也就是缩小图片的过程,实际上是损失高频信息的过程。下面5张图依次是原图,放缩至64*64、32*32、16*16、8*8的图。
均值哈希算法主要是利用图片的低频信息,其工作过程如下:
(1) 缩小尺寸:去除高频和细节的最快方法是缩小图片,将图片缩小到8x8的尺寸,总共64个像素。不要保持纵横比,只需将其变成8*8的正方形。这样就可以比较任意大小的图片,这一步的作用是去除图片的细节,只保留结构、明暗等基本信息摒弃不同尺寸、比例带来的图片差异。
(2)简化色彩:将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色。
(3)计算平均值:计算所有64个像素的灰度平均值。
(4)比较像素的灰度:将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。
(5)计算hash值:将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。(我设置的是从左到右,从上到下用二进制保存)。
= = 8f373714acfcf4d0
得到指纹以后,就可以对比不同的图片,看看64位中有多少位是不一样的。在理论上,这等同于计算"汉明距离"(Hamming distance)。如果不相同的数据位不超过5,就说明两张图片很相似;如果大于10,就说明这是两张不同的图片。
具体的代码实现,可以参见Wote用python语言写的imgHash.py。代码很短,只有53行。使用的时候,第一个参数是基准图片,第二个参数是用来比较的其他图片所在的目录,返回结果是两张图片之间不相同的数据位数量(汉明距离)。
这种算法的优点是简单快速,不受图片大小缩放或改变纵横比的影响,增加或减少亮度或对比度,或改变颜色,对hash值都不会太大的影响。缺点是图片的内容不能变更。如果在图片上加几个文字,它就认不出来了。所以,它的最佳用途是根据缩略图,找出原图。
实际应用中,往往采用更强大的pHash算法和SIFT算法,它们能够识别图片的变形。只要变形程度不超过25%,它们就能匹配原图。这些算法虽然更复杂,但是原理与上面的简便算法是一样的,就是先将图片转化成Hash字符串,然后再进行比较。
1.2.1范例
#include "stdafx.h"#include <iostream>#include <opencv2/core/core.hpp>#include <opencv2/highgui/highgui.hpp>#include<opencv2/imgproc/imgproc.hpp> #pragmacomment(lib,"opencv_core2410d.lib") #pragmacomment(lib,"opencv_highgui2410d.lib") #pragmacomment(lib,"opencv_imgproc2410d.lib") using namespace std; int _tmain(int argc, _TCHAR* argv[]){ string strSrcImageName = "swan.jpg"; cv::Mat matSrc, matSrc1, matSrc2; matSrc = cv::imread(strSrcImageName, CV_LOAD_IMAGE_COLOR); CV_Assert(matSrc.channels() == 3); cv::resize(matSrc, matSrc1, cv::Size(357, 419), 0, 0,cv::INTER_NEAREST); //cv::flip(matSrc1, matSrc1, 1); cv::resize(matSrc, matSrc2, cv::Size(2177, 3233), 0, 0,cv::INTER_LANCZOS4); cv::Mat matDst1, matDst2; cv::resize(matSrc1, matDst1, cv::Size(8, 8), 0, 0, cv::INTER_CUBIC); cv::resize(matSrc2, matDst2, cv::Size(8, 8), 0, 0, cv::INTER_CUBIC); cv::cvtColor(matDst1, matDst1, CV_BGR2GRAY); cv::cvtColor(matDst2, matDst2, CV_BGR2GRAY); int iAvg1 = 0, iAvg2 = 0; int arr1[64], arr2[64]; for (int i = 0; i < 8; i++) { uchar* data1 = matDst1.ptr<uchar>(i); uchar* data2 = matDst2.ptr<uchar>(i); int tmp = i * 8; for (int j = 0; j < 8; j++) { int tmp1 = tmp + j; arr1[tmp1] = data1[j] / 4 * 4; arr2[tmp1] = data2[j] / 4 * 4; iAvg1 += arr1[tmp1]; iAvg2 += arr2[tmp1]; } } iAvg1 /= 64; iAvg2 /= 64; for (int i = 0; i < 64; i++) { arr1[i] = (arr1[i] >= iAvg1) ? 1 : 0; arr2[i] = (arr2[i] >= iAvg2) ? 1 : 0; } int iDiffNum = 0; for (int i = 0; i < 64; i++) if (arr1[i] != arr2[i]) ++iDiffNum; cout<<"iDiffNum = "<<iDiffNum<<endl; if (iDiffNum <= 5) cout<<"two images are very similar!"<<endl; else if (iDiffNum > 10) cout<<"they are two different images!"<<endl; else cout<<"two image are somewhat similar!"<<endl; getchar(); return 0;}
1.2.2范例2
//均值Hash算法 string HashValue(Mat &src) { string rst(64,'\0'); Mat img; if(src.channels()==3) cvtColor(src,img,CV_BGR2GRAY); else img=src.clone(); /*第一步,缩小尺寸。 将图片缩小到8x8的尺寸,总共64个像素,去除图片的细节*/ resize(img,img,Size(8,8)); /* 第二步,简化色彩(ColorReduce)。 将缩小后的图片,转为64级灰度。*/ uchar *pData; for(int i=0;i<img.rows;i++) { pData = img.ptr<uchar>(i); for(int j=0;j<img.cols;j++) { pData[j]=pData[j]/4; } } /* 第三步,计算平均值。 计算所有64个像素的灰度平均值。*/ int average = mean(img).val[0]; /* 第四步,比较像素的灰度。 将每个像素的灰度,与平均值进行比较。大于或等于平均值记为1,小于平均值记为0*/ Mat mask= (img>=(uchar)average); /* 第五步,计算哈希值。*/ int index = 0; for(int i=0;i<mask.rows;i++) { pData = mask.ptr<uchar>(i); for(int j=0;j<mask.cols;j++) { if(pData[j]==0) rst[index++]='0'; else rst[index++]='1'; } } return rst; }
1.2.3汉明距离计算
//汉明距离计算 intHanmingDistance(string &str1,string &str2) { if((str1.size()!=64)||(str2.size()!=64)) return -1; int difference = 0; for(int i=0;i<64;i++) { if(str1[i]!=str2[i]) difference++; } return difference; }
- 感知哈希算法之基于低频的均值哈希
- 基于感知哈希算法的视觉目标跟踪
- 基于感知哈希算法的视觉目标跟踪
- 基于感知哈希算法的视觉目标跟踪
- 基于感知哈希算法的视觉目标跟踪
- 基于感知哈希算法的视觉目标跟踪
- 基于感知哈希的视觉目标跟踪算法代码
- 感知哈希算法
- 感知哈希算法
- 图片搜索之感知哈希算法
- Java进阶(五十七)-基于感知哈希算法的pHash图像配准算法
- 基于感知哈希算法的pHash图像配准算法
- 基于感知哈希算法的中药标本相似图片的搜索
- 感知哈希算法的opencv实现
- 三种基于感知哈希算法的相似图像检索技术
- 三种基于感知哈希算法的相似图像检索技术
- 三种基于感知哈希算法的相似图像检索技术
- 采用感知哈希算法基于python-PIL的图像去重
- GridView"操作"部分修改
- block背后的黑魔法
- Javascript基础程序测试题
- Tomcat 并发数优化的方法总结
- 解决Tomcat报错模糊不清问题,进行错误详细跟踪
- 感知哈希算法之基于低频的均值哈希
- ArcGIS Portal中配置自定义符号简要说明
- linux下打补丁和生成补丁
- 深入理解Gradle(一)
- java技术达人成长记<3>WEB架构师成长之路(6年)
- mybatis教程--实现增删改查的入门教程
- Windows禁用自带键盘命令
- cache 使用---mark
- Xshell无法连接虚拟机