相似图像查找
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字符串,然后再进行比较。
相似图像查找,谷歌相似图像
根据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; }比较乱,实现的功能就是一个函数扫描图库,保存二进制,另一个读取文档,比对测试图像,输出相似图,代码写的不好,丢人了
相关文章推荐
- laravel5.1 配置相关
- 创造一款成功HTML5游戏的完整指南
- Codeforces 611C New Year and Domino DP+容斥
- Bootstrap排版
- CentOs6.5中安装和配置vsftp简明教程
- CMake 创建静态库及动态库供函数调用
- Android Studio快捷键
- 编译安装mariadb
- 从Tomcat源码看其类加载实现
- bootstrap插件bootbox参数和自定义弹出框宽度设置
- 使用Uploadify实现上传图片生成缩略图例子,实时显示进度条
- 迷你DVD管理器
- line-height:150% 和 line-height:1.5的区别
- [Object C]_[初级]_[Object C之内存管理]
- 判断101-200之间有多少个非素数或素数
- bash反弹shell
- Sicily 3725.陶陶考试
- 2015年12月英语总结
- iOS UI-标签控制器(UITabBarController)
- 大学要不要谈恋爱