相似图像查找

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

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

根据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 *[n];
	double **quo_T = new double *[n];
	double **temp_t = new double *[n];
	for (int i = 0; i < n; i++)
	{
		quo_t[i] = new double[n];
		quo_T[i] = new double[n];
		temp_t[i] = new double[n];
	}

	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 *[n];
	double **quo_T = new double *[n];
	double **temp_t = new double *[n];
	for (int i = 0; i < n; i++)
	{
		quo_t[i] = new double[n];
		quo_T[i] = new double[n];
		temp_t[i] = new double[n];
	}

	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;

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



  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值