【OpenCV】SIFT原理与源码分析:方向赋值

《SIFT原理与源码分析》系列文章索引:http://blog.csdn.net/xiaowei_cqu/article/details/8069548


由前一篇《 关键点搜索与定位》,我们已经找到了关键点。为了实现图像旋转不变性,需要根据检测到的关键点局部图像结构为特征点方向赋值。也就是在 findScaleSpaceExtrema()函数里看到的alcOrientationHist()语句:
[cpp]  view plain copy
  1. // 计算梯度直方图  
  2. float omax = calcOrientationHist(gauss_pyr[o*(nOctaveLayers+3) + layer],  
  3.                                                 Point(c1, r1),  
  4.                                                 cvRound(SIFT_ORI_RADIUS * scl_octv),  
  5.                                                 SIFT_ORI_SIG_FCTR * scl_octv,  
  6.                                                 hist, n);  

我们使用图像的梯度直方图法求关键点局部结构的稳定方向。

梯度方向和幅值

在前文中,精确定位关键点后也找到改特征点的尺度值σ,根据这一尺度值,得到最接近这一尺度值的高斯图像:


使用有限差分,计算以关键点为中心,以3×1.5σ为半径的区域内图像梯度的幅角和幅值,公式如下:

梯度直方图

在完成关键点邻域内高斯图像梯度计算后,使用直方图统计邻域内像素对应的梯度方向和幅值。

有关直方图的基础知识可以参考《数字图像直方图》,可以看做是离散点的概率表示形式。此处方向直方图的核心是统计以关键点为原点,一定区域内的图像像素点对关键点方向生成所作的贡献

梯度方向直方图的横轴是梯度方向角,纵轴是剃度方向角对应的梯度幅值累加值。梯度方向直方图将0°~360°的范围分为36个柱,每10°为一个柱。下图是从高斯图像上求取梯度,再由梯度得到梯度方向直方图的例图。

在计算直方图时,每个加入直方图的采样点都使用圆形高斯函数函数进行了加权处理,也就是进行高斯平滑。这主要是因为SIFT算法只考虑了尺度和旋转不变形,没有考虑仿射不变性。通过高斯平滑,可以使关键点附近的梯度幅值有较大权重,从而部分弥补没考虑仿射不变形产生的特征点不稳定。

通常离散的梯度直方图要进行插值拟合处理,以求取更精确的方向角度值。(这和《关键点搜索与定位》中插值的思路是一样的)。

关键点方向

直方图峰值代表该关键点处邻域内图像梯度的主方向,也就是该关键点的主方向。在梯度方向直方图中,当存在另一个相当于主峰值    80%能量的峰值时,则将这个方向认为是该关键点的辅方向。所以一个关键点可能检测得到多个方向,这可以增强匹配的鲁棒性。Lowe的论文指出大概有15%关键点具有多方向,但这些点对匹配的稳定性至为关键。

获得图像关键点主方向后,每个关键点有三个信息(x,y,σ,θ):位置、尺度、方向。由此我们可以确定一个SIFT特征区域。通常使用一个带箭头的圆或直接使用箭头表示SIFT区域的三个值:中心表示特征点位置,半径表示关键点尺度(r=2.5σ),箭头表示主方向。具有多个方向的关键点可以复制成多份,然后将方向值分别赋给复制后的关键点。如下图:


源码

[cpp]  view plain copy
  1. // Computes a gradient orientation histogram at a specified pixel  
  2. // 计算特定点的梯度方向直方图  
  3. static float calcOrientationHist( const Mat& img, Point pt, int radius,  
  4.                                   float sigma, float* hist, int n )  
  5. {  
  6.     //len:2r+1也就是以r为半径的圆(正方形)像素个数  
  7.     int i, j, k, len = (radius*2+1)*(radius*2+1);  
  8.   
  9.     float expf_scale = -1.f/(2.f * sigma * sigma);  
  10.     AutoBuffer<float> buf(len*4 + n+4);  
  11.     float *X = buf, *Y = X + len, *Mag = X, *Ori = Y + len, *W = Ori + len;  
  12.     float* temphist = W + len + 2;  
  13.   
  14.     for( i = 0; i < n; i++ )  
  15.         temphist[i] = 0.f;  
  16.   
  17.     // 图像梯度直方图统计的像素范围  
  18.     for( i = -radius, k = 0; i <= radius; i++ )  
  19.     {  
  20.         int y = pt.y + i;  
  21.         if( y <= 0 || y >= img.rows - 1 )  
  22.             continue;  
  23.         for( j = -radius; j <= radius; j++ )  
  24.         {  
  25.             int x = pt.x + j;  
  26.             if( x <= 0 || x >= img.cols - 1 )  
  27.                 continue;  
  28.   
  29.             float dx = (float)(img.at<short>(y, x+1) - img.at<short>(y, x-1));  
  30.             float dy = (float)(img.at<short>(y-1, x) - img.at<short>(y+1, x));  
  31.   
  32.             X[k] = dx; Y[k] = dy; W[k] = (i*i + j*j)*expf_scale;  
  33.             k++;  
  34.         }  
  35.     }  
  36.   
  37.     len = k;  
  38.   
  39.     // compute gradient values, orientations and the weights over the pixel neighborhood  
  40.     // 计算梯度、幅角和幅值  
  41.     exp(W, W, len);   
  42.     fastAtan2(Y, X, Ori, len, true);   
  43.     magnitude(X, Y, Mag, len); //幅角  
  44.       
  45.     // 计算直方图的每个bin  
  46.     for( k = 0; k < len; k++ )  
  47.     {  
  48.         int bin = cvRound((n/360.f)*Ori[k]);  
  49.         if( bin >= n )  
  50.             bin -= n;  
  51.         if( bin < 0 )  
  52.             bin += n;  
  53.         temphist[bin] += W[k]*Mag[k];  
  54.     }  
  55.   
  56.     // smooth the histogram  
  57.     // 高斯平滑  
  58.     temphist[-1] = temphist[n-1];  
  59.     temphist[-2] = temphist[n-2];  
  60.     temphist[n] = temphist[0];  
  61.     temphist[n+1] = temphist[1];  
  62.     for( i = 0; i < n; i++ )  
  63.     {  
  64.         hist[i] = (temphist[i-2] + temphist[i+2])*(1.f/16.f) +  
  65.             (temphist[i-1] + temphist[i+1])*(4.f/16.f) +  
  66.             temphist[i]*(6.f/16.f);  
  67.     }  
  68.       
  69.     // 得到主方向  
  70.     float maxval = hist[0];  
  71.     for( i = 1; i < n; i++ )  
  72.         maxval = std::max(maxval, hist[i]);  
  73.   
  74.     return maxval;  
  75. }  

这一步比较简单~参见《 SIFT原理与源码分析》。

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


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值