现在以图搜图的功能比较火热,很好奇其原理。
简单的搜索学习得知,实现相似图片搜索的关键技术是“感知哈希算法”,作用是对每一张图片按照某种规律生成一个对应的指纹字符串。比较不同图片之间的指纹字符串,结果越接近,图片越相似。
现将问题简化为研究两幅图像的相似度,算法可能其他博客都有介绍了,现给出实现代码(简易版)。简易版指纹字符串的算法思想如下:
1.输入图像
2.灰度化
3.将图像大小归一化到8*8尺寸
4.简化灰度以减少计算量,例如所有的灰度除以5
5.计算平均灰度值avg
6.比较8*8=64个像素与平均灰度值avg的大小,若大则记为1,小则记为0,按一定顺序排列成64位2进制的指纹编码。
7.比较两幅图像的指纹编码,计算相似度。
测试样例:(依次为example1,example2,example3,example4)
测试结果:
example1-example2 : 64.0625%,not similar.
example1-example3 : 71.875%,a little similar.
example1-example4 : 95.3125%,extremely similar.
代码如下,只有一个文件哦(main.cpp):
#include <opencv2/opencv.hpp>
using namespace std;
string ImageHashValue(IplImage* src); //计算图片的指纹信息
double ImageSimilarity(string &str1,string &str2); //根据指纹信息计算两幅图像的相似度
int main()
{
IplImage* image1 = cvLoadImage("example1.jpg",1);
IplImage* image2 = cvLoadImage("example3.jpg",1);
cvShowImage("image1",image1);
cvShowImage("image2",image2);
string imgPrint1 = ImageHashValue(image1);
string imgPrint2 = ImageHashValue(image2);
double similarity = ImageSimilarity(imgPrint1,imgPrint2);
cout<<"The similarity of two images is "<<similarity*100<<"%"<<endl;
if(similarity>=0.9)
cout<<"The two images are extremely similar."<<endl;
else if(similarity>=0.8&&similarity<0.9)
cout<<"The two images are pretty similar."<<endl;
else if(similarity>=0.7&&similarity<0.8)
cout<<"The two images are a little similar."<<endl;
else if(similarity<0.7)
cout<<"The two image are not similar."<<endl;
cout<<endl;
cvWaitKey(0);
}
//计算图片的指纹信息
string ImageHashValue(IplImage* src)
{
string resStr(64,'\0');
IplImage* image = cvCreateImage(cvGetSize(src),src->depth,1);
//step one : 灰度化
if(src->nChannels == 3) cvCvtColor(src,image,CV_BGR2GRAY);
else cvCopy(src,image);
//step two : 缩小尺寸 8*8
IplImage* temp = cvCreateImage(cvSize(8,8),image->depth,1);
cvResize(image,temp);
//step three : 简化色彩
uchar* pData;
for(int i=0; i<temp->height; i++)
{
pData =(uchar* )(temp->imageData+i*temp->widthStep);
for(int j=0; j<temp->width;j++)
pData[j]= pData[j]/4;
}
//step four : 计算平均灰度值
int average = cvAvg(temp).val[0];
//step five : 计算哈希值
int index = 0;
for(int i=0; i<temp->height; i++)
{
pData =(uchar* )(temp->imageData+i*temp->widthStep);
for(int j=0; j<temp->width;j++)
{
if(pData[j]>=average)
resStr[index++]='1';
else
resStr[index++]='0';
}
}
return resStr;
}
//根据指纹信息计算两幅图像的相似度
double ImageSimilarity(string &str1,string &str2)
{
double similarity = 1.0;
for(int i=0;i<64;i++)
{
char c1 = str1[i];
char c2 = str2[i];
if(c1!=c2)
similarity = similarity -1.0/64;
}
return similarity;
}