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 }
浙公网安备 33010602011771号