SIFT算法步骤二(在DOG空间中查找极值特征点)

 1 void __scaleSpaceExtrema(const vector<Mat> &dog_pyramid, vector<Feature> &feats, int octaves, int intervals,
 2     double contrast_thres, int curvature_thres)
 3 {
 4     double prelim_constrast_thres = 0.5*contrast_thres / intervals;
 5 
 6     int layer_per_octave_dog = intervals + 2;
 7     for (int oct = 0; oct < octaves; oct++)
 8     {
 9         Size s = dog_pyramid[oct*layer_per_octave_dog].size();//获取每一层图片大小
10         for (int lay = 1; lay <= intervals; lay++)            //DOG金字塔每组有intervals+2层,首尾两张没法求极值,所以每组求极值的图片有intervals张
11         {
12             int idx = oct*layer_per_octave_dog + lay;
13             const Mat& dog = dog_pyramid[idx];
14             
15             for (int r = SIFT_IMG_BORDER; r < s.height - SIFT_IMG_BORDER; r++)
16             {
17                 for (int c = SIFT_IMG_BORDER; c < s.width - SIFT_IMG_BORDER; c++)
18                 {
19                     float pixel_val = dog.at<float>(r, c);
20                     if (abs(pixel_val) <= prelim_constrast_thres)  //去除低对比度图片
21                         continue;
22                     if (!__isExtremum(dog_pyramid, idx, r, c))        //去除非极值点
23                         continue;
24 
25                     Feature feat;
26                     //检查初始极值点修正后的点是否仍为合法极值点
27                     if (!__interpExtremum(dog_pyramid, feat, idx, r, c, intervals, contrast_thres))
28                         continue;
29                     //检查该点是否在边缘上,去除边缘点
30                     if (__isTooEdgeLike(dog_pyramid[feat.__idx], feat.__r, feat.__c, curvature_thres))
31                         continue;
32                     feats.push_back(feat);
33                 }
34             }
35         }
36     }
37 }

1.去除非极值点

 1 bool __isExtremum(const vector<Mat>& dog_pyramid,int idx,int r,int c)
 2 {
 3     float pixel_val = dog_pyramid[idx].at<float>(r, c);
 4     int flag = pixel_val>0 ? 1 : -1;
 5     //如果pixel_val大于0,则该点必须比周围点都大;否则就必须比周围点都小
 6     //这样才满足是一个极值点
 7     //(但为什么不能是pixel_val>0而且pixel_val比周围点都小呢??)
 8     for (int i = -1; i <= 1; i++)
 9     {
10         for (int j = -1; j <= 1; j++)
11         {
12             for (int k = -1; k <= 1; k++)
13             {
14                 if (pixel_val * flag < dog_pyramid[idx + i].at<float>(r + j, c + k) * flag)
15                 {
16                     return false;
17                 }
18             }
19         }
20     }
21 }

2.通过插值更新极值点

 1 bool __interpExtremum(const vector<Mat> &dog_pyramid, Feature &feat, int idx, int r, int c, int intervals, double contrast_thres)
 2 {
 3     int layer_per_octave_dog = intervals + 2;
 4     Size s = dog_pyramid[idx].size();
 5 
 6     int i = 0;
 7     double xi, xr, xc;
 8     while (i < SIFT_MAX_INTERP_STEPS) //如果超过了最大迭代步数还不收敛,则说明该点不是极值点
 9     {
10         __interpStep(dog_pyramid, idx, r, c, xi, xr, xc);    //根据导数=0所求出的方向进行一次迭代
11         if (abs(xi) < 0.5&&abs(xr) < 0.5&&abs(xc) < 0.5)    //如果这几个偏移都小于0.5,则认为收敛到了极值点附近
12         {
13             break;
14         }
15 
16         int lay = idx%layer_per_octave_dog + round(xi);        //lay用来检测是否超出了某一组范围
17 
18         //迭代一次后更新系数
19         c += round(xc);
20         r += round(xr);
21         idx += round(xi);        //更新索引其实是在更新尺度
22 
23         //lay用来检测是否超出了某一组范围
24         //r和c分别不能超过[SIFT_IMG_BORDER,s.height - SIFT_IMG_BORDER]和[SIFT_IMG_BORDER,s.width - SIFT_IMG_BORDER]
25         if (lay < 1 || lay > intervals || c < SIFT_IMG_BORDER || r < SIFT_IMG_BORDER ||
26             c >= s.width - SIFT_IMG_BORDER || r >= s.height - SIFT_IMG_BORDER) {
27             return false;
28         }
29         i++;
30     }
31 
32     if (i >= SIFT_MAX_INTERP_STEPS)
33         return false;
34 
35     //求插值后所得到点的对比度
36     double contrast = __interpContrast(dog_pyramid, idx, r, c, xi, xr, xc);
37 
38     if (abs(contrast) < contrast_thres / intervals)
39         return false;
40     
41     int octave = idx / layer_per_octave_dog;
42 
43     //The final offset is added to the location
44     //of its sample point to get the interpolated estimate for the location of the extremum.
45     //但为啥要乘以pow(2.0,octave)呢?因为该图片已经缩小了这个倍数,而__x和__y表示在原图片尺寸中,该特征点的坐标
46 feat.__x = (c + xc) * pow(2.0, octave); 47 feat.__y = (r + xr) * pow(2.0, octave); 48 feat.__contrast = contrast; 49 50 feat.__r = r; 51 feat.__c = c; 52 feat.__octave = octave; //组数 53 feat.__interval = idx % layer_per_octave_dog; //组内层数 54 feat.__idx = idx; //全局索引号 55 feat.__sub_interval = xi; //final offset中xi的值 56 57 return true; 58 }

 插值公式是将尺度空间函数在点(r,c,idx)处泰勒展开至二次项,然后对其求导,另导数等于0, 求出更新一次后的δx

 1 void __interpStep(const vector<Mat> &dog_pyramid, int idx, int r, int c, double &xi, double &xr, double &xc) 
 2 {
 3     //在点(idx,r,c)处展开
 4     Mat dD = __derivative(dog_pyramid, idx, r, c);
 5     Mat H = __hessian(dog_pyramid, idx, r, c);
 6     Mat H_inv = H.inv(DECOMP_SVD);
 7     Mat X = -H_inv*dD;
 8     xi = X.at<double>(2, 0);
 9     xr = X.at<double>(1, 0);
10     xc = X.at<double>(0, 0);
11 }

接下来是把所求的这个δx代入

得到

求出更新后新的点的值(也就是对比度)

 1 double __interpContrast(const vector<Mat> &dog_pyramid, int idx, int r, int c, double xi, double xr, double xc)
 2 {
 3     Mat dD = __derivative(dog_pyramid, idx, r, c);
 4     Mat X(3, 1, CV_64FC1);
 5     X.at<double>(2, 0) = xi;
 6     X.at<double>(1, 0) = xr;
 7     X.at<double>(0, 0) = xc;
 8 
 9     Mat t = dD.t()*X;
10     return (dog_pyramid[idx].at<float>(r, c) + 0.5 * t.at<double>(0, 0));
11 }

若所求值小于阈值,则去除该点。

 

3.判断该点是否位于边缘部分

 1 bool __isTooEdgeLike(const Mat &dog, int r, int c, int curvature_thres)
 2 {
 3     //这个其实就是用Harris角点相关知识判断是否在边上,论文中curvature_thres=10
 4     double d = dog.at<float>(r, c);
 5     double dxx = dog.at<float>(r, c + 1) + dog.at<float>(r, c - 1) - 2 * d;
 6     double dyy = dog.at<float>(r + 1, c) + dog.at<float>(r - 1, c) - 2 * d;
 7     double dxy = (dog.at<float>(r + 1, c + 1) - dog.at<float>(r + 1, c - 1) -
 8         dog.at<float>(r - 1, c + 1) + dog.at<float>(r - 1, c - 1)) / 4.0;
 9     double tr = dxx + dyy;
10     double det = dxx*dyy - dxy*dxy;
11 
12     if (det <= 0)
13         return true;
14     if (tr*tr / det < (curvature_thres + 1.0)*(curvature_thres + 1.0) / curvature_thres)
15         return false;
16     return true;
17 }

 

至此,求出极值特征点步骤结束

 

 =========================================================================

之后就是对所求极值特征点进行一些后处理

 1 void __calcFeatureScales(vector<Feature> &feats,double sigma,int intervals)
 2 {
 3     for (auto it = feats.begin(); it != feats.end(); it++)
 4     {
 5         float interval = (*it).__interval + (*it).__sub_interval;
 6         //全局而言,这个特征点对应的sigma
 7         (*it).__scl = sigma*pow(2.0, (*it).__octave + interval / intervals);
 8         //组内而言,这个特征点对应的sigma
 9         (*it).__scl_octave = sigma*pow(2.0, interval / intervals);
10     }
11 }

 

如果曾经将原始图片扩大一倍,那么相应的x,y,sigma都要除以2

1 void __adjustForImgDbl(vector<Feature> &feats) {
2     for (auto it = feats.begin(); it != feats.end(); it++) {
3         (*it).__x /= 2.0;
4         (*it).__y /= 2.0;
5         (*it).__scl /= 2.0;
6     }
7 }

 

posted @ 2016-11-25 19:51  vaevaevae  阅读(1726)  评论(0)    收藏  举报