【OpenCV】SIFT原理与源码分析:4.关键点描述

来源:互联网 发布:iphone照片导入mac步骤 编辑:程序博客网 时间:2024/05/16 12:39

转自:http://blog.csdn.net/xiaowei_cqu/article/details/8113565


由前一篇《方向赋值》,为找到的关键点即SIFT特征点赋了值,包含位置、尺度和方向的信息。接下来的步骤是关键点描述,即用用一组向量将这个关键点描述出来,这个描述子不但包括关键点,也包括关键点周围对其有贡献的像素点。用来作为目标匹配的依据(所以描述子应该有较高的独特性,以保证匹配率),也可使关键点具有更多的不变特性,如光照变化、3D视点变化等。

SIFT描述子h(x,y,θ)是对关键点附近邻域内高斯图像梯度统计的结果,是一个三维矩阵,但通常用一个矢量来表示。矢量通过对三维矩阵按一定规律排列得到。

描述子采样区域

特征描述子与关键点所在尺度有关,因此对梯度的求取应在特征点对应的高斯图像上进行。将关键点附近划分成d×d个子区域,每个子区域尺寸为mσ个像元(d=4,m=3,σ为尺特征点的尺度值)。考虑到实际计算时需要双线性插值,故计算的图像区域为mσ(d+1),再考虑旋转,则实际计算的图像区域为,如下图所示:

源码

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1.    Point pt(cvRound(ptf.x), cvRound(ptf.y));  
  2. //计算余弦,正弦,CV_PI/180:将角度值转化为幅度值  
  3.    float cos_t = cosf(ori*(float)(CV_PI/180));  
  4.    float sin_t = sinf(ori*(float)(CV_PI/180));  
  5.    float bins_per_rad = n / 360.f;  
  6.    float exp_scale = -1.f/(d * d * 0.5f); //d:SIFT_DESCR_WIDTH 4      
  7.    float hist_width = SIFT_DESCR_SCL_FCTR * scl;  // SIFT_DESCR_SCL_FCTR: 3   
  8.                                                // scl: size*0.5f  
  9. // 计算图像区域半径mσ(d+1)/2*sqrt(2)  
  10. // 1.4142135623730951f 为根号2  
  11.    int radius = cvRound(hist_width * 1.4142135623730951f * (d + 1) * 0.5f);  
  12.    cos_t /= hist_width;  
  13.    sin_t /= hist_width;  



区域坐标轴旋转

为了保证特征矢量具有旋转不变性,要以特征点为中心,在附近邻域内旋转θ角,即旋转为特征点的方向。

旋转后区域内采样点新的坐标为:

源码

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //计算采样区域点坐标旋转  
  2.     for( i = -radius, k = 0; i <= radius; i++ )  
  3.         for( j = -radius; j <= radius; j++ )  
  4.         {  
  5.             /* 
  6.              Calculate sample's histogram array coords rotated relative to ori. 
  7.              Subtract 0.5 so samples that fall e.g. in the center of row 1 (i.e. 
  8.              r_rot = 1.5) have full weight placed in row 1 after interpolation. 
  9.              */  
  10.             float c_rot = j * cos_t - i * sin_t;  
  11.             float r_rot = j * sin_t + i * cos_t;  
  12.             float rbin = r_rot + d/2 - 0.5f;  
  13.             float cbin = c_rot + d/2 - 0.5f;  
  14.             int r = pt.y + i, c = pt.x + j;  
  15.   
  16.             if( rbin > -1 && rbin < d && cbin > -1 && cbin < d &&  
  17.                r > 0 && r < rows - 1 && c > 0 && c < cols - 1 )  
  18.             {  
  19.                 float dx = (float)(img.at<short>(r, c+1) - img.at<short>(r, c-1));  
  20.                 float dy = (float)(img.at<short>(r-1, c) - img.at<short>(r+1, c));  
  21.                 X[k] = dx; Y[k] = dy; RBin[k] = rbin; CBin[k] = cbin;  
  22.                 W[k] = (c_rot * c_rot + r_rot * r_rot)*exp_scale;  
  23.                 k++;  
  24.             }  
  25.         }  


计算采样区域梯度直方图

将旋转后区域划分为d×d个子区域(每个区域间隔为mσ像元),在子区域内计算8个方向的梯度直方图,绘制每个方向梯度方向的累加值,形成一个种子点。
与求主方向不同的是,此时,每个子区域梯度方向直方图将0°~360°划分为8个方向区间,每个区间为45°。即每个种子点有8个方向区间的梯度强度信息。由于存在d×d,即4×4个子区域,所以最终共有4×4×8=128个数据,形成128维SIFT特征矢量。

对特征矢量需要加权处理,加权采用mσd/2的标准高斯函数。为了除去光照变化影响,还有一步归一化处理。

源码

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //计算梯度直方图  
  2.     for( k = 0; k < len; k++ )  
  3.     {  
  4.         float rbin = RBin[k], cbin = CBin[k];  
  5.         float obin = (Ori[k] - ori)*bins_per_rad;  
  6.         float mag = Mag[k]*W[k];  
  7.   
  8.         int r0 = cvFloor( rbin );  
  9.         int c0 = cvFloor( cbin );  
  10.         int o0 = cvFloor( obin );  
  11.         rbin -= r0;  
  12.         cbin -= c0;  
  13.         obin -= o0;  
  14.   
  15.         //n为SIFT_DESCR_HIST_BINS:8,即将360°分为8个区间  
  16.         if( o0 < 0 )  
  17.             o0 += n;  
  18.         if( o0 >= n )  
  19.             o0 -= n;  
  20.           
  21.   
  22.         // histogram update using tri-linear interpolation  
  23.         // 双线性插值  
  24.         float v_r1 = mag*rbin, v_r0 = mag - v_r1;  
  25.         float v_rc11 = v_r1*cbin, v_rc10 = v_r1 - v_rc11;  
  26.         float v_rc01 = v_r0*cbin, v_rc00 = v_r0 - v_rc01;  
  27.         float v_rco111 = v_rc11*obin, v_rco110 = v_rc11 - v_rco111;  
  28.         float v_rco101 = v_rc10*obin, v_rco100 = v_rc10 - v_rco101;  
  29.         float v_rco011 = v_rc01*obin, v_rco010 = v_rc01 - v_rco011;  
  30.         float v_rco001 = v_rc00*obin, v_rco000 = v_rc00 - v_rco001;  
  31.   
  32.         int idx = ((r0+1)*(d+2) + c0+1)*(n+2) + o0;  
  33.         hist[idx] += v_rco000;  
  34.         hist[idx+1] += v_rco001;  
  35.         hist[idx+(n+2)] += v_rco010;  
  36.         hist[idx+(n+3)] += v_rco011;  
  37.         hist[idx+(d+2)*(n+2)] += v_rco100;  
  38.         hist[idx+(d+2)*(n+2)+1] += v_rco101;  
  39.         hist[idx+(d+3)*(n+2)] += v_rco110;  
  40.         hist[idx+(d+3)*(n+2)+1] += v_rco111;  
  41.     }  

关键点描述源码

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. // SIFT关键点特征描述  
  2. // SIFT描述子是关键点领域高斯图像提取统计结果的一种表示  
  3. static void calcSIFTDescriptor( const Mat& img, Point2f ptf, float ori, float scl,  
  4.                                int d, int n, float* dst )  
  5.                              
  6. {  
  7.     Point pt(cvRound(ptf.x), cvRound(ptf.y));  
  8.     //计算余弦,正弦,CV_PI/180:将角度值转化为幅度值  
  9.     float cos_t = cosf(ori*(float)(CV_PI/180));  
  10.     float sin_t = sinf(ori*(float)(CV_PI/180));  
  11.     float bins_per_rad = n / 360.f;  
  12.     float exp_scale = -1.f/(d * d * 0.5f); //d:SIFT_DESCR_WIDTH 4     
  13.     float hist_width = SIFT_DESCR_SCL_FCTR * scl;  // SIFT_DESCR_SCL_FCTR: 3   
  14.                                                    // scl: size*0.5f  
  15.     // 计算图像区域半径mσ(d+1)/2*sqrt(2)  
  16.     // 1.4142135623730951f 为根号2  
  17.     int radius = cvRound(hist_width * 1.4142135623730951f * (d + 1) * 0.5f);  
  18.     cos_t /= hist_width;  
  19.     sin_t /= hist_width;  
  20.   
  21.     int i, j, k, len = (radius*2+1)*(radius*2+1), histlen = (d+2)*(d+2)*(n+2);  
  22.     int rows = img.rows, cols = img.cols;  
  23.   
  24.     AutoBuffer<float> buf(len*6 + histlen);  
  25.     float *X = buf, *Y = X + len, *Mag = Y, *Ori = Mag + len, *W = Ori + len;  
  26.     float *RBin = W + len, *CBin = RBin + len, *hist = CBin + len;  
  27.   
  28.     //初始化直方图  
  29.     for( i = 0; i < d+2; i++ )  
  30.     {  
  31.         for( j = 0; j < d+2; j++ )  
  32.             for( k = 0; k < n+2; k++ )  
  33.                 hist[(i*(d+2) + j)*(n+2) + k] = 0.;  
  34.     }  
  35.   
  36.     //计算采样区域点坐标旋转  
  37.     for( i = -radius, k = 0; i <= radius; i++ )  
  38.         for( j = -radius; j <= radius; j++ )  
  39.         {  
  40.             /* 
  41.              Calculate sample's histogram array coords rotated relative to ori. 
  42.              Subtract 0.5 so samples that fall e.g. in the center of row 1 (i.e. 
  43.              r_rot = 1.5) have full weight placed in row 1 after interpolation. 
  44.              */  
  45.             float c_rot = j * cos_t - i * sin_t;  
  46.             float r_rot = j * sin_t + i * cos_t;  
  47.             float rbin = r_rot + d/2 - 0.5f;  
  48.             float cbin = c_rot + d/2 - 0.5f;  
  49.             int r = pt.y + i, c = pt.x + j;  
  50.   
  51.             if( rbin > -1 && rbin < d && cbin > -1 && cbin < d &&  
  52.                r > 0 && r < rows - 1 && c > 0 && c < cols - 1 )  
  53.             {  
  54.                 float dx = (float)(img.at<short>(r, c+1) - img.at<short>(r, c-1));  
  55.                 float dy = (float)(img.at<short>(r-1, c) - img.at<short>(r+1, c));  
  56.                 X[k] = dx; Y[k] = dy; RBin[k] = rbin; CBin[k] = cbin;  
  57.                 W[k] = (c_rot * c_rot + r_rot * r_rot)*exp_scale;  
  58.                 k++;  
  59.             }  
  60.         }  
  61.   
  62.     len = k;  
  63.     fastAtan2(Y, X, Ori, len, true);  
  64.     magnitude(X, Y, Mag, len);  
  65.     exp(W, W, len);  
  66.   
  67.       
  68.     //计算梯度直方图  
  69.     for( k = 0; k < len; k++ )  
  70.     {  
  71.         float rbin = RBin[k], cbin = CBin[k];  
  72.         float obin = (Ori[k] - ori)*bins_per_rad;  
  73.         float mag = Mag[k]*W[k];  
  74.   
  75.         int r0 = cvFloor( rbin );  
  76.         int c0 = cvFloor( cbin );  
  77.         int o0 = cvFloor( obin );  
  78.         rbin -= r0;  
  79.         cbin -= c0;  
  80.         obin -= o0;  
  81.   
  82.         //n为SIFT_DESCR_HIST_BINS:8,即将360°分为8个区间  
  83.         if( o0 < 0 )  
  84.             o0 += n;  
  85.         if( o0 >= n )  
  86.             o0 -= n;  
  87.           
  88.   
  89.         // histogram update using tri-linear interpolation  
  90.         // 双线性插值  
  91.         float v_r1 = mag*rbin, v_r0 = mag - v_r1;  
  92.         float v_rc11 = v_r1*cbin, v_rc10 = v_r1 - v_rc11;  
  93.         float v_rc01 = v_r0*cbin, v_rc00 = v_r0 - v_rc01;  
  94.         float v_rco111 = v_rc11*obin, v_rco110 = v_rc11 - v_rco111;  
  95.         float v_rco101 = v_rc10*obin, v_rco100 = v_rc10 - v_rco101;  
  96.         float v_rco011 = v_rc01*obin, v_rco010 = v_rc01 - v_rco011;  
  97.         float v_rco001 = v_rc00*obin, v_rco000 = v_rc00 - v_rco001;  
  98.   
  99.         int idx = ((r0+1)*(d+2) + c0+1)*(n+2) + o0;  
  100.         hist[idx] += v_rco000;  
  101.         hist[idx+1] += v_rco001;  
  102.         hist[idx+(n+2)] += v_rco010;  
  103.         hist[idx+(n+3)] += v_rco011;  
  104.         hist[idx+(d+2)*(n+2)] += v_rco100;  
  105.         hist[idx+(d+2)*(n+2)+1] += v_rco101;  
  106.         hist[idx+(d+3)*(n+2)] += v_rco110;  
  107.         hist[idx+(d+3)*(n+2)+1] += v_rco111;  
  108.     }  
  109.   
  110.     // finalize histogram, since the orientation histograms are circular  
  111.     // 最后确定直方图,目标方向直方图是圆的  
  112.     for( i = 0; i < d; i++ )  
  113.         for( j = 0; j < d; j++ )  
  114.         {  
  115.             int idx = ((i+1)*(d+2) + (j+1))*(n+2);  
  116.             hist[idx] += hist[idx+n];  
  117.             hist[idx+1] += hist[idx+n+1];  
  118.             for( k = 0; k < n; k++ )  
  119.                 dst[(i*d + j)*n + k] = hist[idx+k];  
  120.         }  
  121.     // copy histogram to the descriptor,  
  122.     // apply hysteresis thresholding  
  123.     // and scale the result, so that it can be easily converted  
  124.     // to byte array  
  125.     float nrm2 = 0;  
  126.     len = d*d*n;  
  127.     for( k = 0; k < len; k++ )  
  128.         nrm2 += dst[k]*dst[k];  
  129.     float thr = std::sqrt(nrm2)*SIFT_DESCR_MAG_THR;  
  130.     for( i = 0, nrm2 = 0; i < k; i++ )  
  131.     {  
  132.         float val = std::min(dst[i], thr);  
  133.         dst[i] = val;  
  134.         nrm2 += val*val;  
  135.     }  
  136.     nrm2 = SIFT_INT_DESCR_FCTR/std::max(std::sqrt(nrm2), FLT_EPSILON);  
  137.     for( k = 0; k < len; k++ )  
  138.     {  
  139.         dst[k] = saturate_cast<uchar>(dst[k]*nrm2);  
  140.     }  
  141. }  

至此SIFT描述子生成,SIFT算法也基本完成了~

(转载请注明作者和出处:http://blog.csdn.net/xiaowei_cqu 未经允许请勿用于商业用途)

0 0
原创粉丝点击