【OpenCV】SIFT原理与源码分析:2.关键点搜索与定位

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

由前一步《DoG尺度空间构造》,我们得到了DoG高斯差分金字塔:


如上图的金字塔,高斯尺度空间金字塔中每组有五层不同尺度图像,相邻两层相减得到四层DoG结果。关键点搜索就在这四层DoG图像上寻找局部极值点。

DoG局部极值点

寻找DoG极值点时,每一个像素点和它所有的相邻点比较,当其大于(或小于)它的图像域和尺度域的所有相邻点时,即为极值点。如下图所示,比较的范围是个3×3的立方体:中间的检测点和它同尺度的8个相邻点,以及和上下相邻尺度对应的9×2个点——共26个点比较,以确保在尺度空间和二维图像空间都检测到极值点。 


在一组中,搜索从每组的第二层开始,以第二层为当前层,第一层和第三层分别作为立方体的的上下层;搜索完成后再以第三层为当前层做同样的搜索。所以每层的点搜索两次。通常我们将组Octaves索引以-1开始,则在比较时牺牲了-1组的第0层和第N组的最高层


高斯金字塔,DoG图像及极值计算的相互关系如上图所示。


关键点精确定位

以上极值点的搜索是在离散空间进行搜索的,由下图可以看到,在离散空间找到的极值点不一定是真正意义上的极值点。可以通过对尺度空间DoG函数进行曲线拟合寻找极值点来减小这种误差。

利用DoG函数在尺度空间的Taylor展开式:

则极值点为:

程序中还除去了极值小于0.04的点。如下所示:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // Detects features at extrema in DoG scale space.  Bad features are discarded  
  2. // based on contrast and ratio of principal curvatures.  
  3. // 在DoG尺度空间寻特征点(极值点)  
  4. void SIFT::findScaleSpaceExtrema( const vector<Mat>& gauss_pyr, const vector<Mat>& dog_pyr,  
  5.                                   vector<KeyPoint>& keypoints ) const  
  6. {  
  7.     int nOctaves = (int)gauss_pyr.size()/(nOctaveLayers + 3);  
  8.       
  9.     // The contrast threshold used to filter out weak features in semi-uniform  
  10.     // (low-contrast) regions. The larger the threshold, the less features are produced by the detector.  
  11.     // 过滤掉弱特征的阈值 contrastThreshold默认为0.04  
  12.     int threshold = cvFloor(0.5 * contrastThreshold / nOctaveLayers * 255 * SIFT_FIXPT_SCALE);  
  13.     const int n = SIFT_ORI_HIST_BINS; //36  
  14.     float hist[n];  
  15.     KeyPoint kpt;  
  16.   
  17.     keypoints.clear();  
  18.   
  19.     forint o = 0; o < nOctaves; o++ )  
  20.         forint i = 1; i <= nOctaveLayers; i++ )  
  21.         {  
  22.             int idx = o*(nOctaveLayers+2)+i;  
  23.             const Mat& img = dog_pyr[idx];  
  24.             const Mat& prev = dog_pyr[idx-1];  
  25.             const Mat& next = dog_pyr[idx+1];  
  26.             int step = (int)img.step1();  
  27.             int rows = img.rows, cols = img.cols;  
  28.   
  29.             forint r = SIFT_IMG_BORDER; r < rows-SIFT_IMG_BORDER; r++)  
  30.             {  
  31.                 const short* currptr = img.ptr<short>(r);  
  32.                 const short* prevptr = prev.ptr<short>(r);  
  33.                 const short* nextptr = next.ptr<short>(r);  
  34.   
  35.                 forint c = SIFT_IMG_BORDER; c < cols-SIFT_IMG_BORDER; c++)  
  36.                 {  
  37.                     int val = currptr[c];  
  38.   
  39.                     // find local extrema with pixel accuracy  
  40.                     // 寻找局部极值点,DoG中每个点与其所在的立方体周围的26个点比较  
  41.                     // if (val比所有都大 或者 val比所有都小)  
  42.                     if( std::abs(val) > threshold &&  
  43.                        ((val > 0 && val >= currptr[c-1] && val >= currptr[c+1] &&  
  44.                          val >= currptr[c-step-1] && val >= currptr[c-step] &&   
  45.                          val >= currptr[c-step+1] && val >= currptr[c+step-1] &&   
  46.                          val >= currptr[c+step] && val >= currptr[c+step+1] &&  
  47.                          val >= nextptr[c] && val >= nextptr[c-1] &&   
  48.                          val >= nextptr[c+1] && val >= nextptr[c-step-1] &&   
  49.                          val >= nextptr[c-step] && val >= nextptr[c-step+1] &&   
  50.                          val >= nextptr[c+step-1] && val >= nextptr[c+step] &&   
  51.                          val >= nextptr[c+step+1] && val >= prevptr[c] &&   
  52.                          val >= prevptr[c-1] && val >= prevptr[c+1] &&  
  53.                          val >= prevptr[c-step-1] && val >= prevptr[c-step] &&   
  54.                          val >= prevptr[c-step+1] && val >= prevptr[c+step-1] &&   
  55.                          val >= prevptr[c+step] && val >= prevptr[c+step+1]) ||  
  56.                         (val < 0 && val <= currptr[c-1] && val <= currptr[c+1] &&  
  57.                          val <= currptr[c-step-1] && val <= currptr[c-step] &&   
  58.                          val <= currptr[c-step+1] && val <= currptr[c+step-1] &&   
  59.                          val <= currptr[c+step] && val <= currptr[c+step+1] &&  
  60.                          val <= nextptr[c] && val <= nextptr[c-1] &&   
  61.                          val <= nextptr[c+1] && val <= nextptr[c-step-1] &&   
  62.                          val <= nextptr[c-step] && val <= nextptr[c-step+1] &&   
  63.                          val <= nextptr[c+step-1] && val <= nextptr[c+step] &&   
  64.                          val <= nextptr[c+step+1] && val <= prevptr[c] &&   
  65.                          val <= prevptr[c-1] && val <= prevptr[c+1] &&  
  66.                          val <= prevptr[c-step-1] && val <= prevptr[c-step] &&   
  67.                          val <= prevptr[c-step+1] && val <= prevptr[c+step-1] &&   
  68.                          val <= prevptr[c+step] && val <= prevptr[c+step+1])))  
  69.                     {  
  70.                         int r1 = r, c1 = c, layer = i;  
  71.                           
  72.                         // 关键点精确定位  
  73.                         if( !adjustLocalExtrema(dog_pyr, kpt, o, layer, r1, c1,  
  74.                                                 nOctaveLayers, (float)contrastThreshold,  
  75.                                                 (float)edgeThreshold, (float)sigma) )  
  76.                             continue;  
  77.                           
  78.                         float scl_octv = kpt.size*0.5f/(1 << o);  
  79.                         // 计算梯度直方图  
  80.                         float omax = calcOrientationHist(  
  81.                             gauss_pyr[o*(nOctaveLayers+3) + layer],  
  82.                             Point(c1, r1),  
  83.                             cvRound(SIFT_ORI_RADIUS * scl_octv),  
  84.                             SIFT_ORI_SIG_FCTR * scl_octv,  
  85.                             hist, n);  
  86.                         float mag_thr = (float)(omax * SIFT_ORI_PEAK_RATIO);  
  87.                         forint j = 0; j < n; j++ )  
  88.                         {  
  89.                             int l = j > 0 ? j - 1 : n - 1;  
  90.                             int r2 = j < n-1 ? j + 1 : 0;  
  91.   
  92.                             if( hist[j] > hist[l]  &&  hist[j] > hist[r2]  &&  hist[j] >= mag_thr )  
  93.                             {  
  94.                                 float bin = j + 0.5f * (hist[l]-hist[r2]) /   
  95.                                 (hist[l] - 2*hist[j] + hist[r2]);  
  96.                                 bin = bin < 0 ? n + bin : bin >= n ? bin - n : bin;  
  97.                                 kpt.angle = (float)((360.f/n) * bin);  
  98.                                 keypoints.push_back(kpt);  
  99.                             }  
  100.                         }  
  101.                     }  
  102.                 }  
  103.             }  
  104.         }  
  105. }  


删除边缘效应

除了DoG响应较低的点,还有一些响应较强的点也不是稳定的特征点。DoG对图像中的边缘有较强的响应值,所以落在图像边缘的点也不是稳定的特征点。
一个平坦的DoG响应峰值在横跨边缘的地方有较大的主曲率,而在垂直边缘的地方有较小的主曲率。主曲率可以通过2×2的Hessian矩阵H求出:

D值可以通过求临近点差分得到。H的特征值与D的主曲率成正比,具体可参见Harris角点检测算法。
为了避免求具体的值,我们可以通过H将特征值的比例表示出来。令 为最大特征值, 为最小特征值,那么:


Tr(H)表示矩阵H的迹,Det(H)表示H的行列式。
表示最大特征值与最小特征值的比值,则有:

上式与两个特征值的比例有关。随着主曲率比值的增加, 也会增加。我们只需要去掉比率大于一定值的特征点。Lowe论文中去掉r=10的点。
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // Interpolates a scale-space extremum's location and scale to subpixel  
  2. // accuracy to form an image feature.  Rejects features with low contrast.  
  3. // Based on Section 4 of Lowe's paper.  
  4. // 特征点精确定位  
  5. static bool adjustLocalExtrema( const vector<Mat>& dog_pyr, KeyPoint& kpt, int octv,  
  6.                                 int& layer, int& r, int& c, int nOctaveLayers,  
  7.                                 float contrastThreshold, float edgeThreshold, float sigma )  
  8. {  
  9.     const float img_scale = 1.f/(255*SIFT_FIXPT_SCALE);  
  10.     const float deriv_scale = img_scale*0.5f;  
  11.     const float second_deriv_scale = img_scale;  
  12.     const float cross_deriv_scale = img_scale*0.25f;  
  13.   
  14.     float xi=0, xr=0, xc=0, contr;  
  15.     int i = 0;  
  16.   
  17.     //三维子像元插值  
  18.     for( ; i < SIFT_MAX_INTERP_STEPS; i++ )  
  19.     {  
  20.         int idx = octv*(nOctaveLayers+2) + layer;  
  21.         const Mat& img = dog_pyr[idx];  
  22.         const Mat& prev = dog_pyr[idx-1];  
  23.         const Mat& next = dog_pyr[idx+1];  
  24.   
  25.         Vec3f dD((img.at<short>(r, c+1) - img.at<short>(r, c-1))*deriv_scale,  
  26.                  (img.at<short>(r+1, c) - img.at<short>(r-1, c))*deriv_scale,  
  27.                  (next.at<short>(r, c) - prev.at<short>(r, c))*deriv_scale);  
  28.   
  29.         float v2 = (float)img.at<short>(r, c)*2;  
  30.         float dxx = (img.at<short>(r, c+1) +   
  31.                 img.at<short>(r, c-1) - v2)*second_deriv_scale;  
  32.         float dyy = (img.at<short>(r+1, c) +   
  33.                 img.at<short>(r-1, c) - v2)*second_deriv_scale;  
  34.         float dss = (next.at<short>(r, c) +   
  35.                 prev.at<short>(r, c) - v2)*second_deriv_scale;  
  36.         float dxy = (img.at<short>(r+1, c+1) -   
  37.                 img.at<short>(r+1, c-1) - img.at<short>(r-1, c+1) +   
  38.                 img.at<short>(r-1, c-1))*cross_deriv_scale;  
  39.         float dxs = (next.at<short>(r, c+1) -   
  40.                 next.at<short>(r, c-1) - prev.at<short>(r, c+1) +   
  41.                 prev.at<short>(r, c-1))*cross_deriv_scale;  
  42.         float dys = (next.at<short>(r+1, c) -   
  43.                 next.at<short>(r-1, c) - prev.at<short>(r+1, c) +   
  44.                 prev.at<short>(r-1, c))*cross_deriv_scale;  
  45.   
  46.         Matx33f H(dxx, dxy, dxs,  
  47.                   dxy, dyy, dys,  
  48.                   dxs, dys, dss);  
  49.   
  50.         Vec3f X = H.solve(dD, DECOMP_LU);  
  51.   
  52.         xi = -X[2];  
  53.         xr = -X[1];  
  54.         xc = -X[0];  
  55.   
  56.         if( std::abs( xi ) < 0.5f  &&  std::abs( xr ) < 0.5f  &&  std::abs( xc ) < 0.5f )  
  57.             break;  
  58.   
  59.         //将找到的极值点对应成像素(整数)  
  60.         c += cvRound( xc );  
  61.         r += cvRound( xr );  
  62.         layer += cvRound( xi );  
  63.   
  64.         if( layer < 1 || layer > nOctaveLayers ||  
  65.            c < SIFT_IMG_BORDER || c >= img.cols - SIFT_IMG_BORDER  ||  
  66.            r < SIFT_IMG_BORDER || r >= img.rows - SIFT_IMG_BORDER )  
  67.             return false;  
  68.     }  
  69.   
  70.     /* ensure convergence of interpolation */  
  71.     // SIFT_MAX_INTERP_STEPS:插值最大步数,避免插值不收敛,程序中默认为5  
  72.     if( i >= SIFT_MAX_INTERP_STEPS )  
  73.         return false;  
  74.   
  75.     {  
  76.         int idx = octv*(nOctaveLayers+2) + layer;  
  77.         const Mat& img = dog_pyr[idx];  
  78.         const Mat& prev = dog_pyr[idx-1];  
  79.         const Mat& next = dog_pyr[idx+1];  
  80.         Matx31f dD((img.at<short>(r, c+1) - img.at<short>(r, c-1))*deriv_scale,  
  81.                    (img.at<short>(r+1, c) - img.at<short>(r-1, c))*deriv_scale,  
  82.                    (next.at<short>(r, c) - prev.at<short>(r, c))*deriv_scale);  
  83.         float t = dD.dot(Matx31f(xc, xr, xi));  
  84.   
  85.         contr = img.at<short>(r, c)*img_scale + t * 0.5f;  
  86.         if( std::abs( contr ) * nOctaveLayers < contrastThreshold )  
  87.             return false;  
  88.   
  89.         /* principal curvatures are computed using the trace and det of Hessian */  
  90.        //利用Hessian矩阵的迹和行列式计算主曲率的比值  
  91.        float v2 = img.at<short>(r, c)*2.f;  
  92.         float dxx = (img.at<short>(r, c+1) +   
  93.                 img.at<short>(r, c-1) - v2)*second_deriv_scale;  
  94.         float dyy = (img.at<short>(r+1, c) +   
  95.                 img.at<short>(r-1, c) - v2)*second_deriv_scale;  
  96.         float dxy = (img.at<short>(r+1, c+1) -   
  97.                 img.at<short>(r+1, c-1) - img.at<short>(r-1, c+1) +   
  98.                 img.at<short>(r-1, c-1)) * cross_deriv_scale;  
  99.         float tr = dxx + dyy;  
  100.         float det = dxx * dyy - dxy * dxy;  
  101.   
  102.         //这里edgeThreshold可以在调用SIFT()时输入;  
  103.         //其实代码中定义了 static const float SIFT_CURV_THR = 10.f 可以直接使用  
  104.         if( det <= 0 || tr*tr*edgeThreshold >= (edgeThreshold + 1)*(edgeThreshold + 1)*det )  
  105.             return false;  
  106.     }  
  107.   
  108.     kpt.pt.x = (c + xc) * (1 << octv);  
  109.     kpt.pt.y = (r + xr) * (1 << octv);  
  110.     kpt.octave = octv + (layer << 8) + (cvRound((xi + 0.5)*255) << 16);  
  111.     kpt.size = sigma*powf(2.f, (layer + xi) / nOctaveLayers)*(1 << octv)*2;  
  112.   
  113.     return true;  
  114. }  

至此,SIFT第二步就完成了。


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


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值