返回> 网站首页 

[转载]phash相似图片搜索原理

yoours2015-09-24 13:16:15 阅读 2009

简介一边听听音乐,一边写写文章。

出处:http://blog.csdn.net/lu597203933/article/details/45798293


感知哈希算法(perceptual hash, phash),它主要也是用缩略图搜原图并能达到较好点的效果。


理论部分:

理论部分主要包括以下几个步骤:

<1> 图像缩放将图像缩放到32*32大小

<2>灰度化32*32大小的图像进行灰度化

<3>离散余弦变换(DCT)—对32*32大小图像进行DCT

<4>计算均值32*32大小图片前面8*8大小图片处理并计算这64个像素的均值

<4>得到8*8图像的phash—8*8的像素值中大于均值的则用1表示,小于的用0表示,这样就得到一个64位二进制码作为该图像的phash值。

<5>计算两幅图像ahash值的汉明距离,距离越小,表明两幅图像越相似;距离越大,表明两幅图像距离越大。

这样做能够避免伽马校正或者颜色直方图调整带来的影响。

更详细的理论可以参看:

1:http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html

2:http://blog.csdn.net/luoweifu/article/details/8220992包括java代码实现

 

下面我给出自己的c++代码实现:

<1>图像灰度化与缩放

[cpp]
  1.        Mat img = imread("E:\\algorithmZack\\ImageSearch\\image\\person.jpg", 1);  
  2. if(!img.data){  
  3.     cout << "the image is not exist" << endl;  
  4.     return 0;  
  5. }  
  6. int size = 32;  // 图片缩放后大小  
  7.   
  8. resize(img, img, Size(size,size));      // 缩放到32*32  
  9. cvtColor(img, img, COLOR_BGR2GRAY);       // 灰度化  

<2>DCT变换

[cpp]
  1. /* 
  2.     功能:获取DCT系数 
  3.     n:矩阵大小 
  4.     quotient: 系数 
  5.     quotientT: 系数转置 
  6. */  
  7. void coefficient(const int &n, double **quotient, double **quotientT){  
  8.     double sqr = 1.0/sqrt(n+0.0);  
  9.     for(int i = 0; i < n; i++){  
  10.         quotient[0][i] = sqr;  
  11.         quotientT[i][0] =  sqr;  
  12.     }  
  13.   
  14.     for(int i = 1; i < n; i++){  
  15.         for(int j = 0; j < n; j++){  
  16.             quotient[i][j] = sqrt(2.0/n)*cos(i*(j+0.5)*PI/n);  // 由公式得到  
  17.             quotientT[j][i] = quotient[i][j];  
  18.         }  
  19.     }  
  20.   
  21. }  
  22. /* 
  23.     功能:两矩阵相乘 
  24.     A和B:源输入矩阵 
  25.     result:输出矩阵 
  26. */  
  27. void matrixMultiply(double **A, double **B, int n, double **result){    
  28.     double t = 0;  
  29.     for(int i = 0; i < n; i++){  
  30.         for(int j = 0; j < n; j++){  
  31.             t = 0;  
  32.             for(int k = 0; k < n; k++)  
  33.                 t += A[i][k]*B[k][j];     
  34.             result[i][j] = t;  
  35.         }  
  36.     }  
  37. }  
  38.   
  39.   
  40. void DCT(Mat_<uchar> image, const int &n, double **iMatrix){  
  41.     for(int i = 0; i < n; i++){  
  42.         for(int j = 0; j < n; j++){  
  43.             iMatrix[i][j] = (double)image(i,j);  
  44.         }  
  45.     }  
  46.   
  47.     // 为系数分配空间  
  48.     double **quotient = new double*[n];  
  49.     double **quotientT = new double*[n];  
  50.     double **tmp = new double*[n];  
  51.     for(int i = 0; i < n; i++){  
  52.         quotient[i] = new double[n];  
  53.         quotientT[i] = new double[n];   
  54.         tmp[i] = new double[n];  
  55.     }  
  56.     // 计算系数矩阵  
  57.     coefficient(n, quotient, quotientT);  
  58.     matrixMultiply(quotient, iMatrix, n, tmp);  // 由公式成绩结果  
  59.     matrixMultiply(tmp, quotientT, n, iMatrix);  
  60.   
  61.     for(int i = 0; i < n; i++){  
  62.         delete []tmp[i];  
  63.         delete []quotient[i];  
  64.         delete []quotientT[i];  
  65.     }  
  66.     delete []tmp;  
  67.     delete []quotient;  
  68.     delete []quotientT;  
  69. }  

<3>计算均值

[cpp]
  1. // 计算8*8图像的平均灰度  
  2. float calcAverage(double **iMatrix, const int &size){  
  3.     float sum = 0;  
  4.     for(int i = 0 ; i < size; i++){  
  5.         for(int j = 0; j < size; j++){  
  6.             sum += iMatrix[i][j];  
  7.         }  
  8.     }  
  9.     return sum/(size*size);  
  10. }  

<4>计算汉明距离

[cpp]
  1. /* 计算hash值 
  2.     image:8*8的灰度图像 
  3.     size: 图像大小  8*8 
  4.     phash:存放64位hash值 
  5.     averagePix: 灰度值的平均值 
  6. */  
  7. void fingerPrint(double **iMatrix, const int &size, bitset<hashLength> &phash, const float &averagePix){  
  8.     for(int i = 0; i < size; i++){  
  9.         int pos = i * size;  
  10.         for(int j = 0; j < size; j++){  
  11.             phash[pos+j] = iMatrix[i][j] >= averagePix ? 1:0;  
  12.         }  
  13.     }  
  14. }  


完整源代码:

[cpp]
  1. #include <iostream>  
  2. #include <bitset>  
  3. #include <string>  
  4. #include <iomanip>  
  5. #include <cmath>  
  6. #include <opencv2\highgui\highgui.hpp>  
  7. #include <opencv2\imgproc\imgproc.hpp>  
  8. #include <opencv2\core\core.hpp>  
  9.   
  10. using namespace std;  
  11. using namespace cv;  
  12.   
  13. #define PI 3.1415926  
  14. #define hashLength 64  
  15.   
  16. /* 
  17.     功能:获取DCT系数 
  18.     n:矩阵大小 
  19.     quotient: 系数 
  20.     quotientT: 系数转置 
  21. */  
  22. void coefficient(const int &n, double **quotient, double **quotientT){  
  23.     double sqr = 1.0/sqrt(n+0.0);  
  24.     for(int i = 0; i < n; i++){  
  25.         quotient[0][i] = sqr;  
  26.         quotientT[i][0] =  sqr;  
  27.     }  
  28.   
  29.     for(int i = 1; i < n; i++){  
  30.         for(int j = 0; j < n; j++){  
  31.             quotient[i][j] = sqrt(2.0/n)*cos(i*(j+0.5)*PI/n);  // 由公式得到  
  32.             quotientT[j][i] = quotient[i][j];  
  33.         }  
  34.     }  
  35.   
  36. }  
  37. /* 
  38.     功能:两矩阵相乘 
  39.     A和B:源输入矩阵 
  40.     result:输出矩阵 
  41. */  
  42. void matrixMultiply(double **A, double **B, int n, double **result){    
  43.     double t = 0;  
  44.     for(int i = 0; i < n; i++){  
  45.         for(int j = 0; j < n; j++){  
  46.             t = 0;  
  47.             for(int k = 0; k < n; k++)  
  48.                 t += A[i][k]*B[k][j];     
  49.             result[i][j] = t;  
  50.         }  
  51.     }  
  52. }  
  53.   
  54.   
  55. void DCT(Mat_<uchar> image, const int &n, double **iMatrix){  
  56.     for(int i = 0; i < n; i++){  
  57.         for(int j = 0; j < n; j++){  
  58.             iMatrix[i][j] = (double)image(i,j);  
  59.         }  
  60.     }  
  61.   
  62.     // 为系数分配空间  
  63.     double **quotient = new double*[n];  
  64.     double **quotientT = new double*[n];  
  65.     double **tmp = new double*[n];  
  66.     for(int i = 0; i < n; i++){  
  67.         quotient[i] = new double[n];  
  68.         quotientT[i] = new double[n];   
  69.         tmp[i] = new double[n];  
  70.     }  
  71.     // 计算系数矩阵  
  72.     coefficient(n, quotient, quotientT);  
  73.     matrixMultiply(quotient, iMatrix, n, tmp);  // 由公式成绩结果  
  74.     matrixMultiply(tmp, quotientT, n, iMatrix);  
  75.   
  76.     for(int i = 0; i < n; i++){  
  77.         delete []tmp[i];  
  78.         delete []quotient[i];  
  79.         delete []quotientT[i];  
  80.     }  
  81.     delete []tmp;  
  82.     delete []quotient;  
  83.     delete []quotientT;  
  84. }  
  85.   
  86. // 计算8*8图像的平均灰度  
  87. float calcAverage(double **iMatrix, const int &size){  
  88.     float sum = 0;  
  89.     for(int i = 0 ; i < size; i++){  
  90.         for(int j = 0; j < size; j++){  
  91.             sum += iMatrix[i][j];  
  92.         }  
  93.     }  
  94.     return sum/(size*size);  
  95. }  
  96.   
  97. /* 计算hash值 
  98.     image:8*8的灰度图像 
  99.     size: 图像大小  8*8 
  100.     phash:存放64位hash值 
  101.     averagePix: 灰度值的平均值 
  102. */  
  103. void fingerPrint(double **iMatrix, const int &size, bitset<hashLength> &phash, const float &averagePix){  
  104.     for(int i = 0; i < size; i++){  
  105.         int pos = i * size;  
  106.         for(int j = 0; j < size; j++){  
  107.             phash[pos+j] = iMatrix[i][j] >= averagePix ? 1:0;  
  108.         }  
  109.     }  
  110. }  
  111.   
  112. /*计算汉明距离*/  
  113. int hammingDistance(const bitset<hashLength> &query, const bitset<hashLength> &target){  
  114.     int distance = 0;  
  115.     for(int i = 0; i < hashLength; i++){  
  116.         distance += (query[i] == target[i] ? 0 : 1);  
  117.     }  
  118.     return distance;  
  119. }  
  120.   
  121. string bitTohex(const bitset<hashLength> &target){  
  122.     string str;  
  123.     for(int i = 0; i < hashLength; i=i+4){  
  124.         int sum = 0;  
  125.         string s;  
  126.         sum += target[i] + (target[i+1]<<1) + (target[i+2]<<2) + (target[i+3]<<3);  
  127.         stringstream ss;  
  128.         ss << hex <<sum;    // 以十六进制保存  
  129.         ss >> s;  
  130.         str += s;  
  131.     }  
  132.     return str;  
  133. }  
  134.   
  135.   
  136.   
  137.   
  138.   
  139. int main(){  
  140.     Mat img = imread("E:\\algorithmZack\\ImageSearch\\image\\person.jpg", 1);  
  141.     if(!img.data){  
  142.         cout << "the image is not exist" << endl;  
  143.         return 0;  
  144.     }  
  145.     int size = 32;  // 图片缩放后大小  
  146.   
  147.     resize(img, img, Size(size,size));      // 缩放到32*32  
  148.     cvtColor(img, img, COLOR_BGR2GRAY);       // 灰度化  
  149.   
  150.     double **iMatrix = new double*[size];  
  151.     for(int i = 0; i < size; i++)  
  152.         iMatrix[i] = new double[size];  
  153.     DCT(img, size, iMatrix);   // 离散余弦变换  
  154.     float averagePix = calcAverage(iMatrix, 8);  
  155.     cout << averagePix << endl;  
  156.     bitset<hashLength> phash;  
  157.     fingerPrint(iMatrix, 8, phash, averagePix);  
  158.   
  159.     //cout << phash << endl;  
  160.     string str = bitTohex(phash);  
  161.     cout << str << endl;  
  162.     /*namedWindow("img"); 
  163.     imshow("img", img); 
  164.     waitKey(0);*/  
  165.       
  166.   
  167.     string img_dir = "E:\\algorithmZack\\ImageSearch\\image\\";  
  168.     for(int i = 1; i <= 11; i++){  
  169.         string pos;  
  170.         stringstream ss;  
  171.         ss << i;  
  172.         ss >> pos;  
  173.         string img_name = img_dir + "person" + pos +".jpg";   
  174.         Mat target = imread(img_name, 1);  
  175.         if(!target.data){  
  176.             cout << "the target image" << img_name << " is not exist" << endl;  
  177.             continue;  
  178.         }  
  179.         resize(target, target, Size(size,size));  
  180.         cvtColor(target, target, COLOR_BGR2GRAY);  
  181.         DCT(target, size, iMatrix);  
  182.   
  183.         float averagePix2 = calcAverage(iMatrix, 8);  
  184.         bitset<hashLength> phash2;  
  185.         fingerPrint(iMatrix, 8, phash2, averagePix2);  
  186.   
  187.         //cout << averagePix2 << endl;  
  188.         int distance = hammingDistance(phash, phash2);      // 计算汉明距离  
  189.         cout <<"【" << i <<"-" <<  distance << "】 ";  
  190.     }  
  191.     cout << endl;  
  192.     for(int i = 0; i < size; i++)  
  193.         delete []iMatrix[i];  
  194.     delete []iMatrix;  
  195.   
  196.     return 0;  
  197. }  

测试图片为:


结果为:


其中【i-j】, i代表personi j代表personiperson的汉明距离。并由结果可见phash对于图片的旋转肯定是无能为力的。

参考文献:

1:http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html  英文原始资料

2:http://blog.csdn.net/luoweifu/article/details/8220992  包括java代码实现

3.   http://blog.csdn.net/luoweifu/article/details/8214959

微信小程序扫码登陆

文章评论

2009人参与,0条评论