相似图片搜索原理一(ahash—c++实现)

ahash,全称叫做average hash,应该是phash(perceptual
hash, 感知哈希)算法的一种。是基于图像内容搜索最简单的一种(search image by image),因此也有很多的局限性。主要用于由图像的缩略图搜原图,对于图像的旋转、平移、对比度和微变形等都无能为力,所以很局限。此次讲解主要分为两个部分,理论部分主要参考是网上的资料,最核心的应该是自己的c++代码实现。

理论部分:

理论部分主要包括以下几个步骤:

<1> 图像缩放—将图像缩放到8*8大小

<2>灰度化—对8*8大小的图像进行灰度化

<3>计算均值—计算这8*8大小图片中64个像素的均值

<4>得到8*8图像的ahash—8*8的像素值中大于均值的则用1表示,小于的用0表示,这样就得到一个64位二进制码作为该图像的ahash值。

<5>计算两幅图像ahash值的汉明距离,距离越小,表明两幅图像越相似;距离越大,表明两幅图像距离越大。

以下来自阮一峰blog的简介:http://www.ruanyifeng.com/blog/2011/07/principle_of_similar_image_search.html?20150415102912

上个月,Google把"相似图片搜索"正式放上了首页。

你可以用一张图片,搜索互联网上所有与它相似的图片。点击搜索框中照相机的图标。

一个对话框会出现。

你输入网片的网址,或者直接上传图片,Google就会找出与其相似的图片。下面这张图片是美国女演员Alyson Hannigan。

上传后,Google返回如下结果:

类似的"相似图片搜索引擎"还有不少,TinEye甚至可以找出照片的拍摄背景。

==========================================================

这种技术的原理是什么?计算机怎么知道两张图片相似呢?

根据Neal Krawetz博士的解释,原理非常简单易懂。我们可以用一个快速算法,就达到基本的效果。

这里的关键技术叫做"感知哈希算法"(Perceptual
hash algorithm),它的作用是对每张图片生成一个"指纹"(fingerprint)字符串,然后比较不同图片的指纹。结果越接近,就说明图片越相似。

下面是一个最简单的实现:

第一步,缩小尺寸。

将图片缩小到8x8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。

第二步,简化色彩。

将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色。

第三步,计算平均值。

计算所有64个像素的灰度平均值。

第四步,比较像素的灰度。

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

第五步,计算哈希值。

将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。


 =8f373714acfcf4d0

得到指纹以后,就可以对比不同的图片,看看64位中有多少位是不一样的。在理论上,这等同于计算"汉明距离"(Hamming
distance)。如果不相同的数据位不超过5,就说明两张图片很相似;如果大于10,就说明这是两张不同的图片。

具体的代码实现,可以参见Wote用python语言写的imgHash.py。代码很短,只有53行。使用的时候,第一个参数是基准图片,第二个参数是用来比较的其他图片所在的目录,返回结果是两张图片之间不相同的数据位数量(汉明距离)。

这种算法的优点是简单快速,不受图片大小缩放的影响,缺点是图片的内容不能变更。如果在图片上加几个文字,它就认不出来了。所以,它的最佳用途是根据缩略图,找出原图。

实际应用中,往往采用更强大的pHash算法和SIFT算法,它们能够识别图片的变形。只要变形程度不超过25%,它们就能匹配原图。这些算法虽然更复杂,但是原理与上面的简便算法是一样的,就是先将图片转化成Hash字符串,然后再进行比较。

 

注意两点(英文原文)

1:The resulting hash won‘t change if the image is scaled or the aspect ratio changes. Increasing or decreasing
the brightness or contrast, or even altering the colors won‘t dramatically change the hash value. And best of all: this is FAST!

就是说当图像被缩放或者分辨率及强度或者对比度甚至颜色改变对得到的hash值都不会有很明显的变化,关键是他非常的快!

2:With pictures,
high frequencies give you detail, while low frequencies show you structure. A large, detailed picture has lots of high frequencies. A very small picture lacks details, so it is all low frequencies.

就是说,像素点的值出现的频率高给你看到的是图片的细节信息,而低频则给你看到的是图片的轮廓结构,一副大的详细的图片会含有很多的高频,而一副小的则缺少细节,
大多都是低频。<翻译的有点烂>

C++代码实现:

网上有人用java代码将其实现了,可以看这里:http://blog.csdn.net/luohong722/article/details/7100058

我这里结合opencv用c++代码将其实现了

<1>图片缩放与灰度化

直接调用opencv的函数就可以了:

Mat img = imread("E:\\algorithmZack\\ImageSearch\\image\\person.jpg", 1);
	if(!img.data){
		cout << "the image is not exist" << endl;
		return 0;
	}
	int size = 8;  // 图片缩放后大小

	resize(img, img, Size(size,size));      // 缩放到8*8
	cvtColor(img, img, COLOR_BGR2GRAY);       // 灰度化

<2>计算灰度化的均值

// 计算8*8图像的平均灰度
float calcAverage(Mat_<uchar> image, const int &size){
	float sum = 0;
	for(int i = 0 ; i < size; i++){
		for(int j = 0; j < size; j++){
			sum += image(i, j);
		}
	}
	return sum/(size*size);
}

<3>得到8*8图像的ahash

/* 计算hash值
	image:8*8的灰度图像
	size: 图像大小  8*8
	ahahs:存放64位hash值
	averagePix: 灰度值的平均值
*/
void fingerPrint(Mat_<uchar> image, const int &size, bitset<hashLength> &ahash, const float &averagePix){
	for(int i = 0; i < size; i++){
		int pos = i * size;
		for(int j = 0; j < size; j++){
			ahash[pos+j] = image(i, j) >= averagePix ? 1:0;
		}
	}
}

<4>计算汉明距离

/*计算汉明距离*/
int hammingDistance(const bitset<hashLength> &query, const bitset<hashLength> &target){
	int distance = 0;
	for(int i = 0; i < hashLength; i++){
		distance += (query[i] == target[i] ? 0 : 1);
	}
	return distance;
}

完整源代码:

#include <iostream>
#include <bitset>
#include <string>
#include <iomanip>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\core\core.hpp>

using namespace std;
using namespace cv;

#define hashLength 64

// 计算8*8图像的平均灰度
float calcAverage(Mat_<uchar> image, const int &size){
	float sum = 0;
	for(int i = 0 ; i < size; i++){
		for(int j = 0; j < size; j++){
			sum += image(i, j);
		}
	}
	return sum/(size*size);
}

/* 计算hash值
	image:8*8的灰度图像
	size: 图像大小  8*8
	ahahs:存放64位hash值
	averagePix: 灰度值的平均值
*/
void fingerPrint(Mat_<uchar> image, const int &size, bitset<hashLength> &ahash, const float &averagePix){
	for(int i = 0; i < size; i++){
		int pos = i * size;
		for(int j = 0; j < size; j++){
			ahash[pos+j] = image(i, j) >= averagePix ? 1:0;
		}
	}
}

/*计算汉明距离*/
int hammingDistance(const bitset<hashLength> &query, const bitset<hashLength> &target){
	int distance = 0;
	for(int i = 0; i < hashLength; i++){
		distance += (query[i] == target[i] ? 0 : 1);
	}
	return distance;
}

string bitTohex(const bitset<hashLength> &target){
	string str;
	for(int i = 0; i < hashLength; i=i+4){
		int sum = 0;
		string s;
		sum += target[i] + (target[i+1]<<1) + (target[i+2]<<2) + (target[i+3]<<3);
		stringstream ss;
		ss << hex <<sum;    // 以十六进制保存
		ss >> s;
		str += s;
	}
	return str;
}

int main(){
	Mat img = imread("E:\\algorithmZack\\ImageSearch\\image\\person.jpg", 1);
	if(!img.data){
		cout << "the image is not exist" << endl;
		return 0;
	}
	int size = 8;  // 图片缩放后大小

	resize(img, img, Size(size,size));      // 缩放到8*8
	cvtColor(img, img, COLOR_BGR2GRAY);       // 灰度化
	float averagePix = calcAverage(img, size);    // 计算灰度化的均值
	//cout << averagePix << endl;
	bitset<hashLength> ahash;
	fingerPrint(img, size, ahash, averagePix);    // 得到均值hash
	//cout << ahash << endl;
	cout << bitTohex(ahash) << endl;

	string img_dir = "E:\\algorithmZack\\ImageSearch\\image\\";
	for(int i = 1; i <= 8; i++){
		string pos;
		stringstream ss;
		ss << i;
		ss >> pos;
		string img_name = img_dir + "person" + pos +".jpg";
		Mat target = imread(img_name, 1);
		if(!target.data){
			cout << "the target image" << img_name << " is not exist" << endl;
			continue;
		}
		resize(target, target, Size(size,size));
		cvtColor(target, target, COLOR_BGR2GRAY);
		float averagePix2 = calcAverage(target, size);
		bitset<hashLength> ahash2;
		fingerPrint(target, size, ahash2, averagePix2);

		//cout << averagePix2 << endl;
		int distance = hammingDistance(ahash, ahash2);      // 计算汉明距离
		cout <<"【" << i <<"-" <<  distance << "】 ";
	}
	cout << endl;
	return 0;

}

测试的图片为:

结果为:

其中【i-j】, i代表personi, j代表personi与person的汉明距离。

参考文献:

1:http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html英文原始资料

2:http://www.ruanyifeng.com/blog/2011/07/principle_of_similar_image_search.html?20150415102912阮一峰blog

3:http://blog.csdn.net/luohong722/article/details/7100058java实现版本

时间: 2024-10-12 03:21:52

相似图片搜索原理一(ahash—c++实现)的相关文章

相似图片搜索原理二(phash—c++实现)

前段时间介绍过相似图片搜索原理一(ahash) http://blog.csdn.net/lu597203933/article/details/45101859,它是基于内容检索最简单的一种:这里介绍它的增强版本感知哈希算法(perceptual hash, phash).它主要也是用缩略图搜原图并能达到较好点的效果. 理论部分: 理论部分主要包括以下几个步骤: <1> 图像缩放-将图像缩放到32*32大小 <2>灰度化-对32*32大小的图像进行灰度化 <3>离散余

Google 以图搜图 - 相似图片搜索原理 - Java实现

前阵子在阮一峰的博客上看到了这篇<相似图片搜索原理>博客,就有一种冲动要将这些原理实现出来了. Google "相似图片搜索":你可以用一张图片,搜索互联网上所有与它相似的图片. 打开Google图片搜索页面: 点击使用上传一张angelababy原图: 点击搜索后,Google将会找出与之相似的图片,图片相似度越高就越排在前面.如: 这种技术的原理是什么?计算机怎么知道两张图片相似呢? 根据Neal Krawetz博士的解释,实现相似图片搜素的关键技术叫做"感知

相似图片搜索原理三(颜色直方图—c++实现)

图像的颜色直方图可以用于图像检索,适应有相同色彩,并且可以有平移.缩放.旋转不变性的图像检索,当然了这三大特点不如sift或者surf稳定性强,此外最大的局限就是如果形状内容一样,但色彩不一,结果是搜不到的.不过它在某些情况下达到较好的结果. 颜色直方图两种计算方式: 彩色图像的颜色直方图,这里可以有两种处理方式,得到的效果应该差不多. 首先第一种就是对像素的每个通道都进行划分,每个通道的最大像素值为255,可以等分8.16或者64等分,这样每个通道的范围就是0~15(以16等分为例,当然等分越

相似图片搜索的原理

http://www.ruanyifeng.com/blog/2011/07/principle_of_similar_image_search.html http://www.ruanyifeng.com/blog/2013/03/similar_image_search_part_ii.html 相似图片搜索的原理 作者: 阮一峰 日期: 2011年7月21日 上个月,Google把"相似图片搜索"正式放上了首页. 你可以用一张图片,搜索互联网上所有与它相似的图片.点击搜索框中照相

相似图片搜索的原理(转)

http://www.ruanyifeng.com/blog/2011/07/principle_of_similar_image_search.html 作者: 阮一峰 日期: 2011年7月21日 上个月,Google把"相似图片搜索"正式放上了首页. 你可以用一张图片,搜索互联网上所有与它相似的图片.点击搜索框中照相机的图标. 一个对话框会出现. 你输入网片的网址,或者直接上传图片,Google就会找出与其相似的图片.下面这张图片是美国女演员Alyson Hannigan. 上传

相似图片搜索的原理(二)

二年前,我写了<相似图片搜索的原理>,介绍了一种最简单的实现方法. 昨天,我在isnowfy的网站看到,还有其他两种方法也很简单,这里做一些笔记. 一.颜色分布法 每张图片都可以生成颜色分布的直方图(color histogram).如果两张图片的直方图很接近,就可以认为它们很相似. 任何一种颜色都是由红绿蓝三原色(RGB)构成的,所以上图共有4张直方图(三原色直方图 + 最后合成的直方图). 如果每种原色都可以取256个值,那么整个颜色空间共有1600万种颜色(256的三次方).针对这160

图片搜索的原理

http://www.zhihu.com/question/19726630 Google 图片搜索的原理是什么? 1 条评论 分享 按投票排序按时间排序 18 个回答 389赞同反对,不会显示你的姓名 知乎用户,安全行业 XsXs.知乎用户.知乎用户 等人赞同 针对这个问题,请教了算法组的同事,他分享了基本的思路: 对于这种图像搜索的算法,一般是三个步骤: 1. 将目标图片进行特征提取,描述图像的算法很多,用的比较多的是:SIFT描述子,指纹算法函数,bundling features算法,h

相似图片搜索的三种哈希算法

想必大家都用google或baidu的识图功能,上面就是我搜索冠希哥一幅图片的结果,达到图片比较目的且利用信息指纹比较有三种算法,这些算法都很易懂,下面分别介绍一下: 一.平均哈希算法(aHash) 此算法是基于比较灰度图每个像素与平均值来实现的,最适用于缩略图,放大图搜索. 步骤: 1.缩放图片:为了保留结构去掉细节,去除大小.横纵比的差异,把图片统一缩放到8*8,共64个像素的图片. 2.转化为灰度图:把缩放后的图片转化为256阶的灰度图. 附上灰度图相关算法(R = red, G = gr

百度Ueditor编辑器取消多图上传对话框中的图片搜索

百度Ueditor确实是一个非常强悍的编辑器,功能强大!但是实际开发需求复杂,总会有各种不符合要求的,比如想要取消多图上传的“图片搜索”选项卡(这个图片搜索真心难用)! 以ueditor 1.4.3为例: 1.打开ueditor1.4.3\dialogs\image\image.html 2.删除第25行 <span class="tab" data-content-id="search"><var id="lang_tab_search