您的位置:首页 > 其它

相似图像查找

2015-12-31 20:24 316 查看
出来实习第一个做的事相似图像的查找,学了一下谷歌的快捷简便方法,确实很吊的感觉。后来又学习了其他的算法,这里先把谷歌这个方法贴出来,方便以后忘记是查找。当然了都是别人写的,一搜就能搜到



相似图像查找,谷歌相似图像

根据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字符串,然后再进行比较。

/************************************************************************
*  Author : YG
*    Date : 2015/12/14
*   Email : 1851506****@163.com
* function: Show similar images in different windows
************************************************************************/
#include <iostream>
#include <bitset>
#include <string>
#include <stdint.h>
#include <iomanip>
#include <cmath>
#include <fstream>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\core\core.hpp>
#include <opencv2\features2d\features2d.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\nonfree\nonfree.hpp>

using namespace std;
using namespace cv;

#define PI 3.1415926
#define hashLength 64
/*************************************************************************************************
*帮助文档
*************************************************************************************************/
void help()
{
cout << "输入的是待检测的图片:格式推荐jpg~" << endl;
cout << "通过提取图片的特征和库中特zheng比~" << endl;
cout << "特征包括缩减颜色值,灰度均值等,保存带hash编码中~" << endl;
}

/**********************************************************************************************
功能:获取DCT系数****************n:矩阵大小**************quo_t: 系数**********quo_T: 系数转置**
**********************************************************************************************/

void concentrate(const int &n, double **quo_t, double **quo_T)

{
//	cout << "~~~~~~~~~~~1.1" << endl;
//	double sqr_t = 1.0 / sqrt(n+0.0);
//	for (int i = 0; i < n; i++)
//	{
//		quo_t[0][i] = sqr_t;
//		quo_T[i][0] = sqr_t;
////		cout << "~~~~~~~~~~~1.1.1" <<i<< endl;
//	}

double sqr = 1.0 / sqrt(n + 0.0);
for (int i = 0; i < n; i++){
quo_t[0][i] = sqr;
quo_T[i][0] = sqr;
}

for (int i = 1; i < n; i++)
{
//	cout << "~~~~~~~~~~~1.1.1 " << i << endl;
for (int j = 0; j < n; j++)
{
quo_t[i][j] = sqrt(2.0 / n)*cos(i*(j + 0.5)*PI / n);  // 由公式得到
quo_T[j][i] = quo_t[i][j];

}
}
}
/************************************************************************************************
两个矩阵相乘输出新的矩阵
************************************************************************************************/
void mutiply_a_b(double **a, double **b, int n, double **result)

{
double k = 0;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
k = 0;
for (int t = 0; t < n; t++)
{
k += a[i][t] * b[t][j];
}
result[i][j] = k;
}

}

}
/***********************************************************************************************
DCT变化
************************************************************************************************/
void DCT_transform(Mat_<uchar> img, const int &n, double **iMax)

{
//	cout << "~~~~~~~~~~~1.1" << endl;
for (int i = 0; i < n; i++)
{
//		cout << "~~~~~~~~~~~1.1.1 "<<i << endl;
for (int j = 0; j < n; j++)
{
//			cout << "~~~~~~~~~~~1.1.1.1 " << j<<endl;
iMax[i][j] = (double)img[i][j];
}
}
//分配空间
double **quo_t = new double *
;
double **quo_T = new double *
;
double **temp_t = new double *
;
for (int i = 0; i < n; i++)
{
quo_t[i] = new double
;
quo_T[i] = new double
;
temp_t[i] = new double
;
}

concentrate(n, quo_t, quo_T);
mutiply_a_b(quo_t, iMax, n, temp_t);
mutiply_a_b(temp_t, quo_T, n, iMax);

for (int i = 0; i < n; i++)
{
delete[] temp_t[i];
delete[] quo_t[i];
delete[] quo_T[i];
}

delete[] temp_t;
delete[] quo_t;
delete[] quo_T;
}

/************************************************************************************************
计算均值
/************************************************************************************************/

float Caculate_Avarage(double **iMax, const int &size)
{
float sum = 0;
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
sum += iMax[i][j];
}

return sum / (size*size);
}

/***********************************************************************************************
计算hash
************************************************************************************************/

void hash_Value(double **iMax, const int &size, bitset<hashLength> &phash, const float &averagePix)
{
for (int i = 0; i < size; i++)
{
int pos = i*size;
for (int j = 0; j < size; j++)
{
phash[pos + j] = iMax[i][j] >= averagePix ? 1 : 0;
}
}
}

/************************************************************************************************
计算距离
/************************************************************************************************/

int Distance_of_pic(const string &query, const string &target)
{
int distance = 0;
for (int i = 0; i < hashLength; i++)
distance += (query[i] == target[i] ? 0 : 1);
return distance;
}

/************************************************************************************************
转化成16进制保存
************************************************************************************************/

string bitBat(const bitset<hashLength> &target)
{
string str;
for (int i = 0; i < hashLength; i += 4)
{
string s;
int sum = 0;
sum += target[i] + (target[i + 1] << 1) + (target[i + 2] << 2) + (target[i + 3] << 3);
stringstream ss;
ss << hex << sum;
ss >> s;
str += s;  //转化16进制
}
return str;
}
/************************************************************************************************/
/********************************16进制转化2进制**********************************************/
/************************************************************************************************
string HexToBin(const string &strHex)
{
if (strHex.size() % 2 != 0)
{
return "";
}

string strBin;
strBin.resize(strHex.size() / 2);
for (size_t i = 0; i < strBin.size(); i++)
{
uint8_t cTemp = 0;
for (size_t j = 0; j < 2; j++)
{
char cCur = strHex[2 * i + j];
if (cCur >= '0' && cCur <= '9')
{
cTemp = (cTemp << 4) + (cCur - '0');
}
else if (cCur >= 'a' && cCur <= 'f')
{
cTemp = (cTemp << 4) + (cCur - 'a' + 10);
}
else if (cCur >= 'A' && cCur <= 'F')
{
cTemp = (cTemp << 4) + (cCur - 'A' + 10);
}
else
{
return "";
}
}
strBin[i] = cTemp;
}

return strBin;
}

/************************************************************************************************/
/********************************样本库main函数入口**********************************************/
/************************************************************************************************/

int main()
{
void help();
Mat img = imread("D:\\tutu\\image253.jpg", 1);
namedWindow("原始图像", WINDOW_AUTOSIZE);
if (!img.data)
{
cout << "the image is not exist" << endl;
return 0;
}
imshow("原始图像", img);
int size = 32;  // 图片缩放后大小

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

double **iMax = new double*[size];
for (int i = 0; i < size; i++)
iMax[i] = new double[size];

DCT_transform(img, size, iMax);   // 离散余弦变换

float average_pix = Caculate_Avarage(iMax, 8);
cout <<"the average_pix is:"<< average_pix << endl;

bitset<hashLength> phash;
hash_Value(iMax, 8, phash, average_pix);
cout << "the phash is:" << phash << endl;

string str = bitBat(phash);
cout << "the final str is:" << str << endl;
fstream dataFile0;
dataFile0.open("feature0.txt", ios::out);
dataFile0 << phash << endl;

fstream in("feature.txt");
fstream in1("feature0.txt");
//bitset<hashLength> phash2;
string line,line2;
//vector<string>name;
//char buf[64];//存储的buf。
int i = 1;
fstream dataFile;
dataFile.open("result.txt", ios::out);
getline(in1,line2);

string dri = "D:\\tutu\\";
char filename[100];
char windowname[100];

while (getline(in, line))
{
//cout<<line.c_str()<<endl;
//name.push_back(line);
//for (int i = 0; i < name.size(); ++i)
//{
//	Mat src = imread(name[i].c_str());
//	imshow("00", src);
//}

//string line_bin = HexToBin(line);
cout <<"image"<<i<<"的16进制是:"<< line << endl;

int distance = Distance_of_pic(line, line2);      // 计算汉明距离
cout << "【" << "image" << i << "-" << distance << "】 " << endl;
if (i % 5 == 0)
cout << endl;
if (distance <= 15)
{
dataFile << "image" << i << "-" << distance << " " << endl; //向文件写入数据
}
if (distance <= 17)
{

string pos;
stringstream ss;
ss << i;
ss >> pos;
string name_img = dri + "image" + pos + ".jpg";

//sprintf(filename, "D:/test/%d.jpg", i);// 将图片以数字命名:例如1.jpg 2.jpg等,放入D:/test/文件夹下
sprintf_s(windowname, "image%d.jpg", i);
//pScr = cvLoadImage(filename, 1);//导入图片
//cvNamedWindow(windowname, CV_WINDOW_AUTOSIZE);
//cvShowImage(windowname, pScr);//显示图片
//cvWaitKey(0);

Mat src = imread(name_img, 1);
namedWindow(windowname, WINDOW_AUTOSIZE);
imshow(windowname, src);
waitKey(1);
}
i++;

/*fstream datafile;
datafile.open("result.txt", ios::out);
datafile << "image" << " " << str << endl;
datafile.close();*/
}
dataFile0.close();
dataFile.close();
for (int i = 0; i < size; i++)
delete[]iMax[i];
delete[]iMax;
cout << "the program is over!" << endl;
waitKey(0);

return 0;

}


using namespace std;
using namespace cv;

#define PI 3.1415926
#define hashLength 64
/*************************************************************************************************
/****************************************帮助文档*************************************************/
/************************************************************************************************/
void help()
{
cout << "输入的是待检测的图片:格式推荐jpg~" << endl;
cout << "通过提取图片的特征和库中特zheng比~" << endl;
cout << "特征包括缩减颜色值,灰度均值等,保存带hash编码中~" << endl;
}

/**********************************************************************************************
功能:获取DCT系数****************n:矩阵大小**************quo_t: 系数**********quo_T: 系数转置**
**********************************************************************************************/

void concentrate(const int &n, double **quo_t, double **quo_T)

{
//	cout << "~~~~~~~~~~~1.1" << endl;
//	double sqr_t = 1.0 / sqrt(n+0.0);
//	for (int i = 0; i < n; i++)
//	{
//		quo_t[0][i] = sqr_t;
//		quo_T[i][0] = sqr_t;
////		cout << "~~~~~~~~~~~1.1.1" <<i<< endl;
//	}

double sqr = 1.0 / sqrt(n + 0.0);
for (int i = 0; i < n; i++)
{
quo_t[0][i] = sqr;
quo_T[i][0] = sqr;
}

for (int i = 1; i < n; i++)
{
//	cout << "~~~~~~~~~~~1.1.1 " << i << endl;
for (int j = 0; j < n; j++)
{
quo_t[i][j] = sqrt(2.0 / n)*cos(i*(j + 0.5)*PI / n);  // 由公式得到
quo_T[j][i] = quo_t[i][j];

}
}
}
/************************************************************************************************/
/*************************************两个矩阵相乘输出新的矩阵***********************************/
/************************************************************************************************/
void mutiply_a_b(double **a, double **b, int n, double **result)

{
double k = 0;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
k = 0;
for (int t = 0; t < n; t++)
{
k += a[i][t] * b[t][j];
}
result[i][j] = k;
}

}

}
/************************************************************************************************/
/***************************************DCT变化**************************************************/
/************************************************************************************************/
void DCT_transform(Mat_<uchar> img, const int &n, double **iMax)

{
//	cout << "~~~~~~~~~~~1.1" << endl;
for (int i = 0; i < n; i++)
{
//		cout << "~~~~~~~~~~~1.1.1 "<<i << endl;
for (int j = 0; j < n; j++)
{
//			cout << "~~~~~~~~~~~1.1.1.1 " << j<<endl;
iMax[i][j] = (double)img[i][j];
}
}
//分配空间
double **quo_t = new double *
;
double **quo_T = new double *
;
double **temp_t = new double *
;
for (int i = 0; i < n; i++)
{
quo_t[i] = new double
;
quo_T[i] = new double
;
temp_t[i] = new double
;
}

concentrate(n, quo_t, quo_T);
mutiply_a_b(quo_t, iMax, n, temp_t);
mutiply_a_b(temp_t, quo_T, n, iMax);

for (int i = 0; i < n; i++)
{
delete[] temp_t[i];
delete[] quo_t[i];
delete[] quo_T[i];
}

delete[] temp_t;
delete[] quo_t;
delete[] quo_T;
}

/************************************************************************************************/
/********************************计算均值********************************************************/
/************************************************************************************************/

float Caculate_Avarage(double **iMax, const int &size)
{
float sum = 0;
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
sum += iMax[i][j];
}

return sum / (size*size);
}

/************************************************************************************************/
/********************************计算hash********************************************************/
/************************************************************************************************/

void hash_Value(double **iMax, const int &size, bitset<hashLength> &phash, const float &averagePix)
{
for (int i = 0; i < size; i++)
{
int pos = i*size;
for (int j = 0; j < size; j++)
{
phash[pos + j] = iMax[i][j] >= averagePix ? 1 : 0;
}
}
}

/************************************************************************************************/
/********************************计算距离********************************************************/
/************************************************************************************************/

//int Distance_of_pic(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;
//}

/************************************************************************************************/
/********************************转化成16进制保存************************************************/
/************************************************************************************************/

string bitBat(const bitset<hashLength> &target)
{
string str;
for (int i = 0; i < hashLength; i += 4)
{
string s;
int sum = 0;
sum += target[i] + (target[i + 1] << 1) + (target[i + 2] << 2) + (target[i + 3] << 3);
stringstream ss;
ss << hex << sum;
ss >> s;
str += s;  //转化16进制
}
return str;
}
//
//bool surf_feature(const Mat &src)
//{
//	int minHession = 400;
//	SurfFeatureDetector detector(minHession);
//	SurfFeatureDetector(SURF);
//	vector<KeyPoint> keyPoints;
//	detector.detect(src, keyPoints);
//	ofstream ofs;
//	ofs.open("surf.txt", ios::app);
//	if (ofs.fail())
//	{
//		cout << "fail to open surf.txt" << endl;
//		return false;
//	}
//	vector<KeyPoint>feature;
//
//
//	for (int i = 0; i < keyPoints.size(); i++)
//	{
//		feature.push_back(keyPoints[i]);
//
//		stringstream ss;
//
//	}
//	ofs << endl;
//	ofs.close();
//}

/************************************************************************************************/
/********************************样本库main函数入口**********************************************/
/************************************************************************************************/

int main()
{
void help();
string dri = "D:\\tutu\\";

fstream datafile;
datafile.open("feature1.txt", ios::out);
//int flag = 0;

int size = 32;
double **iMax = new double*[size];
for (int i = 0; i < size; i++)
iMax[i] = new double[size];

for (int i = 1; i < 1284; i++)
{
string pos;
stringstream ss;
ss << i;
ss >> pos;
string name_img = dri + "image" + pos + ".jpg";
Mat src = imread(name_img, 1);
//检查是否图片存在,是否遍历完成
if (!src.data)
{
cout << "原始图像 " << name_img << " 不存在~~" << endl;
break;
}
//bool surf_write(&src);
Mat dst;
resize(src, dst, Size(size, size));
cvtColor(dst, dst, COLOR_RGB2GRAY);

//cout << "~~~~~~~~~~~1" << endl;

DCT_transform(dst, size, iMax);
//cout << "~~~~~~~~~~~2" << endl;
float average_pix = Caculate_Avarage(iMax, 8);
//cout << "~~~~~~~~~~~3" << endl;
//cout <<"the average_pix is:"<< average_pix << endl;
bitset<hashLength> phash;
hash_Value(iMax, 8, phash, average_pix);

cout << "the phash is:" << phash << endl;
string str = bitBat(phash);
cout << "the final str is:" << str << endl;

datafile << str << endl;

}
datafile.close();
for (int i = 0; i < size; i++)
delete[]iMax[i];
delete[]iMax;
cout << "the program is over!" << endl;
waitKey(0);

return 0;

}
比较乱,实现的功能就是一个函数扫描图库,保存二进制,另一个读取文档,比对测试图像,输出相似图,代码写的不好,丢人了

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: