SIFT算法步骤

 1 void extractSiftFeatures(const Mat &img, vector<KeyPoint> &keypoints, Mat &descriptor, int intervals, double sigma,
 2     double contrast_thres, int curvature_thres, bool img_dbl, int descr_width, int descr_hist_bins)
 3 {
 4     Mat init_img = __createInitImg(img, img_dbl, sigma); //根据img_dbl决定是否需要将原来的图片扩大作为第0层
 5 
 6     Size s = init_img.size();
 7     int octaves = log(min(s.width, s.height)) / log(2) - 2;   //确定金字塔的组数
 8 
 9     vector<Mat> gaussian_pyramid;
10     __buildGaussPyramid(init_img, gaussian_pyramid, octaves, intervals, sigma); //建立高斯金字塔
11 
12     vector<Mat> dog_pyramid;
13     __buildDogPyramid(gaussian_pyramid, dog_pyramid, octaves, intervals); //建立DOG金字塔
14 
15     vector<Feature> feats;
16     __scaleSpaceExtrema(dog_pyramid, feats, octaves, intervals, contrast_thres, curvature_thres); //在DOG空间中查找极值,并记录为特征
17 
18     __calcFeatureScales(feats, sigma, intervals); //计算特征点的尺度
19 
20     if (img_dbl)
21         __adjustForImgDbl(feats);
22 
23     __calcFeatureOris(feats, gaussian_pyramid, intervals + 3); //计算特征点的方向
24 
25     __computeDescriptors(feats, gaussian_pyramid, intervals + 3, descr_width, descr_hist_bins);//计算描述子
26 
27     __feats2KeyPoints(feats, keypoints);
28 
29     __featsVec2Mat(feats, descriptor);
30 }

1.根据img_dbl确定是否需要扩大原来的图像

 1 Mat __createInitImg(const Mat &img, bool img_dbl, double sigma) {
 2     if (img_dbl) {
 3         Mat init_img(img.size() * 2, CV_32FC1);
 4         resize(img, init_img, init_img.size(), 0, 0, INTER_CUBIC);
 5 
 6         //这一句的理解是,图片初始尺度已经是SIFT_INIT_SIGMA(0.5)了,因为第0层尺度是1.6,所以还需要再把
 7         //图片进行一次高斯模糊,使得尺度变成1.6,那么这次高斯模糊所需要的尺度就是sqrt(1.6^2-(0.5*2)^2)
 8         //这里0.5*2是因为图片大小扩大了一倍,相当于现在图片的尺度是1.0了
 9         double sig_diff = sqrt(sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA * 4);
10         GaussianBlur(init_img, init_img, Size(), sig_diff, sig_diff);
11         return init_img;
12     }
13     else {
14         Mat init_img = Mat(img.size(), CV_32FC1);
15 
16         double sig_diff = sqrt(sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA);
17         GaussianBlur(img, init_img, Size(), sig_diff, sig_diff);
18         return init_img;
19     }
20 }

 2.有了初始图像,就可以建立高斯金字塔了

 1 void __buildGaussPyramid(const Mat& base, vector<Mat>& gaussian_pyramid, int octaves, int intervals, double sigma)
 2 {
 3     //octaves是组数,intervals是组内层数,sigma默认1.6
 4     int layer_per_octave = intervals + 3;    //每一组中层数加3
 5     vector<double> sigmas(layer_per_octave);
 6     double k = pow(2.0f, 1.0f / intervals);
 7 
 8     //计算sigma序列,注意因为第0层图片已经经过高斯模糊了,之后的模糊是在第0层的基础上做的
 9     //那第1层尺度要变成k*sigma,需要在已经模糊化的第0层的基础上再模糊多少,才能让第1层尺度变成k*sigma呢?
10     //根据高斯模糊性质,sigmas[1]=sqrt((k*sigma)^2-sigma^2),这其实和__createInitImg函数中sig_diff道理是一样的
11     //之后的sigmas[2]=sqrt((k*k*sigma)^2-sigma^2)=k*sigmas[1],依次类推
12     sigmas[0] = sigma;
13     sigmas[1] = sigma*sqrt(k*k - 1); //sigma默认值是1.6,不过这里sigmas[1]的计算方式有点疑问
14     for (int i = 2; i < layer_per_octave; i++)
15     {
16         sigmas[i] = sigmas[i - 1] * k;
17     }
18     int layers = octaves*layer_per_octave;
19     gaussian_pyramid.reserve(layers);
20 
21     for (int oct = 0; oct < octaves; oct++)
22     {
23         for (int lay = 0; lay < layer_per_octave; lay++)
24         {
25             if (oct == 0 && lay == 0)
26             {
27                 gaussian_pyramid.push_back(base);
28                 continue;
29             }
30 
31             if (lay == 0)
32             {
33                 //因为这一层尺度大小就是2*sigma,所以采样后不需要再高斯模糊。这里也可以看出取倒数第三张可以保证尺度连续
34                 const Mat &last = gaussian_pyramid[gaussian_pyramid.size() - 3]; 
35                 Size s = last.size();
36                 Mat down_img(s.height / 2, s.width / 2, CV_32FC1);
37                 resize(last, down_img, down_img.size(), 0, 0, CV_INTER_NN);
38                 gaussian_pyramid.push_back(down_img);
39                 continue;
40             }
41 
42             const Mat &last = gaussian_pyramid.back();
43             Mat smooth_img(last.size(), CV_32FC1);
44             GaussianBlur(last, smooth_img, Size(), sigmas[lay], sigmas[lay]);
45             gaussian_pyramid.push_back(smooth_img);
46         }
47     }
48 }

3.通过高斯金字塔,计算DOG金字塔,就是同一组中高斯金字塔上一层减去下一层

 1 void __buildDogPyramid(const vector<Mat>& gaussian_pyramid, vector<Mat>& dog_pyramid, int octaves, int intervals)
 2 {
 3     int layer_per_octave_dog = intervals + 2;
 4     int layer_per_octave_gaussian = intervals + 3;
 5     dog_pyramid.reserve(octaves*layer_per_octave_dog);
 6     for (int oct = 0; oct < octaves; oct++)
 7     {
 8         for (int lay = 0; lay < layer_per_octave_dog; lay++)
 9         {
10             int idx = oct*layer_per_octave_gaussian + lay;
11             dog_pyramid.push_back(gaussian_pyramid[idx + 1] - gaussian_pyramid[idx]);
12         }
13     }
14 }

 

这样,SIFT算法初始化工作算是做好了。

posted @ 2016-11-25 18:35  vaevaevae  阅读(889)  评论(0)    收藏  举报