“感知哈希算法”(Perceptual hash algorithm),它的作用是对每张图片生成一个“指纹” 字符串,然后比较不同图片的指纹。结果越接近,就说明图片越相似。
(1)缩小尺寸:去除高频和细节的最快方法是缩小图片,将图片缩小到8x8的尺寸,总共64个像素。不要保持纵横比,只需将其变成8*8的正方形。这样就可以比较任意大小的图片,摒弃不同尺寸、比例带来的图片差异。
(2)简化色彩:将8*8的小图片转换成灰度图像。
(3)计算平均值:计算所有64个像素的灰度平均值。
(4)比较像素的灰度:将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。
(5)计算hash值:将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。
比较两个图片的相似性,就是先计算这两张图片的hash指纹,也就是64位0或1值,然后计算不同位的个数(汉明距离)。如果这个值为0,则表示这两张图片非常相似,如果汉明距离小于5,则表示有些不同,但比较相近,如果汉明距离大于10则表明完全不同的图片。
1 // 递归获得文件夹下面所有的图片路径 2 void getFolderDayFile(CString pathStr, vector<CString>& strFileVector) 3 { 4 CString myDataPath, fdPath; 5 myDataPath = pathStr + _T("\\*.*"); 6 CString strTmp; 7 CFileFind find; 8 bool bf = find.FindFile(myDataPath); 9 10 while(bf) { 11 bf = find.FindNextFile(); 12 if(!find.IsDots()) 13 { 14 fdPath = find.GetFilePath(); 15 if(find.IsDirectory()) 16 getFolderDayFile(fdPath, strFileVector); // 是文件夹,递归,继续查询 17 else 18 { 19 strTmp = fdPath.Right(4); // 是文件,判断是否是*.jpg文件 20 strTmp.MakeLower(); 21 if(strTmp == ".jpg") 22 strFileVector.push_back(fdPath); 23 } 24 } 25 } 26 find.Close(); 27 } 28 29 30 // 哈希指纹获取 31 vector<int> Perceptual_hash(IplImage *image) 32 { 33 IplImage * temp = cvCreateImage(cvSize(8,8),8,3); 34 cvResize(image, temp, CV_INTER_CUBIC); // 8*8大小--缩小尺寸 35 IplImage * grayImg = cvCreateImage(cvSize(8,8),8,1); 36 cvCvtColor(temp,grayImg,CV_BGR2GRAY); // 转灰度--简化色彩 37 38 char *data = (char *)grayImg->imageData; // 新图象数据信息 39 int wp = grayImg->widthStep; 40 41 int average = 0; 42 for(int i = 0;i < 8;i++) 43 for(int j = 0;j < 8;j++) 44 average += data[i * wp + 3 * j]; 45 average /= 64; 46 vector<int> VImg; 47 for(int m = 0;m < 8;m++) 48 for(int n = 0;n < 8;n++) 49 if(data[m * wp + 3 * n] >= average) 50 VImg.push_back(1); 51 else 52 VImg.push_back(0); 53 return VImg; 54 } 55 56 57 // 通过哈希指纹搜索图片 58 void Hash_search(IplImage *image, CString path) 59 { 60 vector<int> hash_image = Perceptual_hash(image); 61 vector<CString> strFileVector; 62 getFolderDayFile(path, strFileVector); 63 printf("%d\n", strFileVector.size()); 64 65 for(int i = 0;i < strFileVector.size();i++) 66 { 67 IplImage * src = cvLoadImage(strFileVector[i]); 68 vector<int> hash_src = Perceptual_hash(src); 69 int com = 0; 70 for(int j = 0;j < 64;j++) 71 if(hash_src[j] != hash_image[j]) 72 com++; 73 if(com <= 5) { // 相似度量 74 CString str; 75 str.Format("D:\\101_Image\\%d.jpg",i); 76 cvSaveImage(str, src); 77 } 78 cvReleaseImage(&src); 79 } 80 }
搜索得到的图像(距离太小,只搜索到原图像):
另附一个直方图搜索:
1 // 通过直方图搜索图像 2 void Histogram_search(IplImage *image, CString path) 3 { 4 CvHistogram * image_hist = Histogram_Image(image); 5 vector<CString> strFileVector; 6 getFolderDayFile(path, strFileVector); 7 printf("%d\n", strFileVector.size()); 8 for(int i = 0;i < strFileVector.size();i++) { 9 IplImage * src = cvLoadImage(strFileVector[i]); 10 CvHistogram * src_hist = Histogram_Image(src); 11 // 直方图相似--Bhattacharyya距离 12 double com = cvCompareHist(image_hist, src_hist, CV_COMP_BHATTACHARYYA); 13 if(com < 0.1) { // 相似度量 14 CString str; 15 str.Format("D:\\101_Image\\%d.jpg",i); 16 cvSaveImage(str, src); 17 } 18 cvReleaseImage(&src); 19 } 20 }
2021-06-04