【特征匹配】ORB原理与源码解析

主要转载自http://blog.csdn.net/luoshixian099/article/details/48523267,本博也会陆续有部分修改



转载请注明出处: http://blog.csdn.net/luoshixian099/article/details/48523267

CSDN-勿在浮沙筑高台



  为了满足实时性的要求,前面文章中介绍过快速提取特征点算法Fast,以及特征描述子Brief。本篇文章介绍的ORB算法结合了Fast和Brief的速度优势,并做了改进,且ORB是免费。

   Ethan Rublee等人2011年在《ORB:An Efficient Alternative to SIFT or SURF》文章中提出了ORB算法。结合Fast与Brief算法,并给Fast特征点增加了方向性,使得特征点具有旋转不变性,并提出了构造金字塔方法,解决尺度不变性,但文章中没有具体详述。实验证明,ORB远优于之前的SIFT与SURF算法。

-------------------------------------------------------------------------------------------------------------------------------

论文核心内容概述:

1.构造金字塔,在每层金字塔上采用Fast算法提取特征点,采用Harris角点响应函数,按角点响应值排序,选取前N个特征点。


2. oFast:计算每个特征点的主方向,灰度质心法,计算特征点半径为r的圆形邻域范围内的灰度质心位置

从中心位置到质心位置的向量,定义为该特 征点的主方向

  定义矩的计算公式,x,y∈[-r,r]:

                                 

             质心位置:

                               

               主方向:

                                  


3.  rBrief:为了解决旋转不变性,把特征点的Patch旋转到主方向上(steered Brief)。通过实验得到,描述子在各个维度上的均值比较离散(偏离0.5),同时维度间相关性很强,说明特征点描述子区分性不好,影响匹配的效果。论文中提出采取学习的方法,采用300K个训练样本点。每一个特征点,选取Patch大小为wp=31,Patch内每对点都采用wt=5大小的子窗口灰度均值做比较,子窗口的个数即为N=(wp-wt)*(wp-wt),从N个窗口中随机选两个做比较即构成描述子的一个bit,论文中采用M=205590种可能的情况:   

       ---------------------------------------------------------------------------------

        1.对所有样本点,做M种测试,构成M维的描述子,每个维度上非1即0;

        2.按均值对M个维度排序(以0.5为中心),组成向量T;

        3.贪婪搜索:把向量T中第一个元素移动到R中,然后继续取T的第二个元素,与R中的所有元素做相关性比较,如果相关性大于指定的阈值Threshold,           抛弃T的这个元素,否则加入到R中;

        4.重复第3个步骤,直到R中有256个元素,若检测完毕,少于256个元素,则降低阈值,重复上述步骤;

       ----------------------------------------------------------------------------------

    rBrief:通过上面的步骤取到的256对点,构成的描述子各维度间相关性很低,区分性好;

                                        

                                              训练前                                            训练后

---------------------------------------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------------------------------------


ORB算法步骤,参考opencv源码:

1.首先构造尺度金字塔;

   金字塔共n层,与SIFT不同,每层仅有一副图像;

   第s层的尺度为,Fator初始尺度(默认为1.2),原图在第0层;

   第s层图像大小:

                              

2.在不同尺度上采用Fast检测特征点;在每一层上按公式计算需要提取的特征点数n,在本层上按Fast角点响应值排序,提取前2n个特征点,然后根据Harris   角点响应值排序, 取前n个特征点,作为本层的特征点;

3.计算每个特征点的主方向(质心法);

4.旋转每个特征点的Patch到主方向,采用上述步骤3的选取的最优的256对特征点做τ测试,构成256维描述子,占32个字节;

                   ,,n=256


5.采用汉明距离做特征点匹配;


----------OpenCV源码解析-------------------------------------------------------

ORB类定义:位置   opencv \ include \ opencv2 \ features2d.hpp

nfeatures:需要的特征点总数;

scaleFactor:尺度因子;

nlevels:金字塔层数;

edgeThreshold:边界阈值;

firstLevel:起始层;

 WTA_K:描述子形成方法,WTA_K=2表示,采用两两比较;line11

 scoreType:角点响应函数,可以选择Harris或者Fast的方法;

 patchSize:特征点邻域大小;

[cpp]  view plain  copy 
 
  1. /*! 
  2.  ORB implementation. 
  3. */  
  4. class CV_EXPORTS_W ORB : public Feature2D  
  5. {  
  6. public:  
  7.     // the size of the signature in bytes  
  8.     enum { kBytes = 32, HARRIS_SCORE=0, FAST_SCORE=1 };  
  9.   
  10.     CV_WRAP explicit ORB(int nfeatures = 500, float scaleFactor = 1.2f, int nlevels = 8, int edgeThreshold = 31,//构造函数  
  11.         int firstLevel = 0, int WTA_K=2, int scoreType=ORB::HARRIS_SCORE, int patchSize=31 );  
  12.   
  13.     // returns the descriptor size in bytes  
  14.     int descriptorSize() const;   //描述子占用的字节数,默认32字节  
  15.     // returns the descriptor type  
  16.     int descriptorType() const;//描述子类型,8位整形数  
  17.   
  18.     // Compute the ORB features and descriptors on an image  
  19.     void operator()(InputArray image, InputArray mask, vector<KeyPoint>& keypoints) const;  
  20.   
  21.     // Compute the ORB features and descriptors on an image  
  22.     void operator()( InputArray image, InputArray mask, vector<KeyPoint>& keypoints,    //提取特征点与形成描述子  
  23.                      OutputArray descriptors, bool useProvidedKeypoints=false ) const;  
  24.   
  25.     AlgorithmInfo* info() const;  
  26.   
  27. protected:  
  28.   
  29.     void computeImpl( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const;//计算描述子  
  30.     void detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;//检测特征点  
  31.   
  32.     CV_PROP_RW int nfeatures;//特征点总数  
  33.     CV_PROP_RW double scaleFactor;//尺度因子  
  34.     CV_PROP_RW int nlevels;//金字塔内层数  
  35.     CV_PROP_RW int edgeThreshold;//边界阈值  
  36.     CV_PROP_RW int firstLevel;//开始层数  
  37.     CV_PROP_RW int WTA_K;//描述子形成方法,默认WTA_K=2,两两比较  
  38.     CV_PROP_RW int scoreType;//角点响应函数  
  39.     CV_PROP_RW int patchSize;//邻域Patch大小  
  40. };  

特征提取及形成描述子:通过这个函数对图像提取Fast特征点或者计算特征描述子

_image:输入图像;

_mask:掩码图像;

_keypoints:输入角点;

_descriptors:如果为空,只寻找特征点,不计算特征描述子;

_useProvidedKeypoints:如果为true,函数只计算特征描述子;


[cpp]  view plain  copy 
 
  1. /** Compute the ORB features and descriptors on an image 
  2.  * @param img the image to compute the features and descriptors on 
  3.  * @param mask the mask to apply 
  4.  * @param keypoints the resulting keypoints 
  5.  * @param descriptors the resulting descriptors 
  6.  * @param do_keypoints if true, the keypoints are computed, otherwise used as an input 
  7.  * @param do_descriptors if true, also computes the descriptors 
  8.  */  
  9. void ORB::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _keypoints,  
  10.                       OutputArray _descriptors, bool useProvidedKeypoints) const    //输入 图像image 掩码mask  关键点矢量 输出描述符数组 判定是否有已经提供关键点
  11. {  
  12.     CV_Assert(patchSize >= 2);  
  13.   
  14.     bool do_keypoints = !useProvidedKeypoints;  
  15.     bool do_descriptors = _descriptors.needed();  
  16.   
  17.     if( (!do_keypoints && !do_descriptors) || _image.empty() )  
  18.         return;  
  19.   
  20.     //ROI region of interest 感兴趣区域处理 
  21.     const int HARRIS_BLOCK_SIZE = 9;//Harris角点响应需要的边界大小  
  22.     int halfPatchSize = patchSize / 2;.//邻域半径  
  23.     int border = std::max(edgeThreshold, std::max(halfPatchSize, HARRIS_BLOCK_SIZE/2))+1;//采用最大的边界  
  24.   
  25.     Mat image = _image.getMat(), mask = _mask.getMat();       // 将输入的  _image和 _mask转化成Mat 形式的image 与 mask
  26.     if( image.type() != CV_8UC1 )  
  27.         cvtColor(_image, image, CV_BGR2GRAY);//转灰度图  
  28.   
  29.     int levelsNum = this->nlevels;//金字塔层数  
  30.   
  31.     if( !do_keypoints )   //不做特征点检测  
  32.     {  
  33.         // if we have pre-computed keypoints, they may use more levels than it is set in parameters  
  34.         // !!!TODO!!! implement more correct method, independent from the used keypoint detector.  
  35.         // Namely, the detector should provide correct size of each keypoint. Based on the keypoint size  
  36.         // and the algorithm used (i.e. BRIEF, running on 31x31 patches) we should compute the approximate  
  37.         // scale-factor that we need to apply. Then we should cluster all the computed scale-factors and  
  38.         // for each cluster compute the corresponding image.  
  39.         //  
  40.         // In short, ultimately the descriptor should  
  41.         // ignore octave parameter and deal only with the keypoint size.
  42.         //如果我们有预先计算的关键点,可能会使用比在参数中设置的更多层数
  43.         //!!! TODO !!! 实现更正确的方法要独立于已经检测了关键点检测器。 也就是说,检测器应该提供每个关键点的正确尺寸。 基于关键点尺寸和使用的算法(即Brief,
  44.         //在31x31块上运行),我们应该计算我们需要应用的近似比例因子。 然后我们应该对所有计算的比例因子进行聚类,并为每个聚类计算相应的图像。  
  45.         //简而言之,描述符最终应该忽略八度参数,只处理关键点大小。 

  46.         levelsNum = 0;  
  47.         forsize_t i = 0; i < _keypoints.size(); i++ )  
  48.             levelsNum = std::max(levelsNum, std::max(_keypoints[i].octave, 0));//提取特征点的最大层数  一般来说octave的增加意味着图像的分辨率减半
  49.         levelsNum++;  
  50.     }  
  51.   
  52.     // Pre-compute the scale pyramids  
  53.     vector<Mat> imagePyramid(levelsNum), maskPyramid(levelsNum);//创建尺度金字塔图像  
  54.     for (int level = 0; level < levelsNum; ++level)  
  55.     {  
  56.         float scale = 1/getScale(level, firstLevel, scaleFactor);  //每层对应的尺度  getScale 获取当前整体的等比缩放的比例用的是pow函数,见下
  57.         /* 
  58.         static inline float getScale(int level, int firstLevel, double scaleFactor) 
  59.             { 
  60.                    return (float)std::pow(scaleFactor, (double)(level - firstLevel));pow求次方的函数 
  61.             }    
  62.         */  
  63.         Size sz(cvRound(image.cols*scale), cvRound(image.rows*scale));//每层对应的图像大小    cvRound 四舍五入,返回最接近的整数
  64.         Size wholeSize(sz.width + border*2, sz.height + border*2);   //  border 是之前定义的最大边界
  65.         Mat temp(wholeSize, image.type()), masktemp;  
  66.         imagePyramid[level] = temp(Rect(border, border, sz.width, sz.height));    //  Rect 函数,创建一个矩形窗口:矩形左上角横纵坐标,宽度和高度 
  67.         if( !mask.empty() )  
  68.         {  
  69.             masktemp = Mat(wholeSize, mask.type());  
  70.             maskPyramid[level] = masktemp(Rect(border, border, sz.width, sz.height));  
  71.         }  
  72.   
  73.         // Compute the resized image  
  74.         if( level != firstLevel )    //得到金字塔每层的图像(非原图 层)
  75.         {  
  76.             if( level < firstLevel )         //如果小于起始层  ??为什么会小于起始层
  77.             {  
  78.                 resize(image, imagePyramid[level], sz, 0, 0, INTER_LINEAR);      // resize ( 原图,新图,Size dsize, double fx=0.double fy=0, int interpolation=INTER_LINER)
  79.                 if (!mask.empty())    // 图像掩码
  80.                     resize(mask, maskPyramid[level], sz, 0, 0, INTER_LINEAR);  
  81.             }  
  82.             else      //如果大于起始层
  83.             {  
  84.                 resize(imagePyramid[level-1], imagePyramid[level], sz, 0, 0, INTER_LINEAR);  // resize ( 上一层图像,这一层图像,Size dsize, 0,0,INTER_LINER)
  85.                 if (!mask.empty())    // 图像掩码
  86.                 {  
  87.                     resize(maskPyramid[level-1], maskPyramid[level], sz, 0, 0, INTER_LINEAR);   // 获得该层mask
  88.                     threshold(maskPyramid[level], maskPyramid[level], 254, 0, THRESH_TOZERO);    // threshold(input, output, 阈值, double maxval, type); 
  89.                                                           // 第四个maxval 是当五的type取binary或binary_inv的最大值,第五个代表当前type为THRESH_TOZERO,当前值大于阈值不改变,否则为0
  90.                 }  
  91.             }  
  92.   
  93.             // 所有非起始层  掩码后   扩大图像边界
  94.            // copyMakeBorder(src输入, dst输出,int top, int bottom,int left, int right,原图四周扩充边缘的大小, int border Type)    

  95.             copyMakeBorder(imagePyramid[level], temp, border, border, border, border,  
  96.                            BORDER_REFLECT_101+BORDER_ISOLATED);   //扩大图像的边界

  97.             if (!mask.empty())     // 扩大掩码边界
  98.                 copyMakeBorder(maskPyramid[level], masktemp, border, border, border, border,  
  99.                                BORDER_CONSTANT+BORDER_ISOLATED);  
  100.         }  
  101.         else   //原图层处理
  102.         {  
  103.             copyMakeBorder(image, temp, border, border, border, border,//扩大图像的四个边界  
  104.                            BORDER_REFLECT_101);  
  105.             if( !mask.empty() )         //扩大掩码的的四个边界
  106.                 copyMakeBorder(mask, masktemp, border, border, border, border,  
  107.                                BORDER_CONSTANT+BORDER_ISOLATED);  
  108.         }  
  109.     }  
  110.   
  111.     // Pre-compute the keypoints (we keep the best over all scales, so this has to be done beforehand 
  112.   //如果没有提供关键点,因需要保存每一层最佳关键点 所以必须先计算关键点
  113.     vector < vector<KeyPoint> > allKeypoints;  
  114.     if( do_keypoints )//提取角点  
  115.     {  
  116.         // Get keypoints, those will be far enough from the border that no check will be required for the descriptor  
  117.         computeKeyPoints(imagePyramid, maskPyramid, allKeypoints,  //对每一层图像提取角点,见下面(1)的分析  
  118.                          nfeatures, firstLevel, scaleFactor,  
  119.                          edgeThreshold, patchSize, scoreType);  
  120.   
  121.         // make sure we have the right number of keypoints keypoints  
  122.         /*vector<KeyPoint> temp; 
  123.  
  124.         for (int level = 0; level < n_levels; ++level) 
  125.         { 
  126.             vector<KeyPoint>& keypoints = all_keypoints[level]; 
  127.             temp.insert(temp.end(), keypoints.begin(), keypoints.end()); 
  128.             keypoints.clear(); 
  129.         } 
  130.  
  131.         KeyPoint::retainBest(temp, n_features_); 
  132.  
  133.         for (vector<KeyPoint>::iterator keypoint = temp.begin(), 
  134.              keypoint_end = temp.end(); keypoint != keypoint_end; ++keypoint) 
  135.             all_keypoints[keypoint->octave].push_back(*keypoint);*/  
  136.     }  
  137.     else  //不提取角点  
  138.     {  
  139.         // Remove keypoints very close to the border  
  140.         KeyPointsFilter::runByImageBorder(_keypoints, image.size(), edgeThreshold);  
  141.   
  142.         // Cluster the input keypoints depending on the level they were computed at  
  143.         allKeypoints.resize(levelsNum);  
  144.         for (vector<KeyPoint>::iterator keypoint = _keypoints.begin(),  
  145.              keypointEnd = _keypoints.end(); keypoint != keypointEnd; ++keypoint)  
  146.             allKeypoints[keypoint->octave].push_back(*keypoint);    //把角点信息存入allKeypoints内  
  147.   
  148.         // Make sure we rescale the coordinates  
  149.         for (int level = 0; level < levelsNum; ++level)   //把角点位置信息缩放到指定层位置上  
  150.         {  
  151.             if (level == firstLevel)  
  152.                 continue;        //如果为原始层,跳出不执行下面的步骤;补位原始层,将角点信息缩放到每一层中
  153.   
  154.             vector<KeyPoint> & keypoints = allKeypoints[level];  
  155.             float scale = 1/getScale(level, firstLevel, scaleFactor);  
  156.             for (vector<KeyPoint>::iterator keypoint = keypoints.begin(),  
  157.                  keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)  
  158.                 keypoint->pt *= scale;   //缩放  
  159.         }  
  160.     }  
  161.   
  162.     Mat descriptors;          
  163.     vector<Point> pattern;  
  164.   
  165. //修改处

  166.     if( do_descriptors ) //计算特征描述子  
  167.     {  
  168.         int nkeypoints = 0;  
  169.         for (int level = 0; level < levelsNum; ++level)  
  170.             nkeypoints += (int)allKeypoints[level].size();//得到所有层的角点总数  
  171.         if( nkeypoints == 0 )  
  172.             _descriptors.release();  
  173.         else  
  174.         {  
  175.             _descriptors.create(nkeypoints, descriptorSize(), CV_8U);//创建一个矩阵存放描述子,每一行表示一个角点信息  
  176.             descriptors = _descriptors.getMat();  
  177.         }  
  178.   
  179.         const int npoints = 512;//取512个点,共256对,产生256维描述子,32个字节  
  180.         Point patternbuf[npoints];  
  181.         const Point* pattern0 = (const Point*)bit_pattern_31_;//训练好的256对数据点位置  
  182.   
  183.         if( patchSize != 31 )  
  184.         {  
  185.             pattern0 = patternbuf;  
  186.             makeRandomPattern(patchSize, patternbuf, npoints);  
  187.         }  
  188.   
  189.         CV_Assert( WTA_K == 2 || WTA_K == 3 || WTA_K == 4 );  
  190.   
  191.         if( WTA_K == 2 )  //WTA_K=2使用两个点之间作比较  
  192.             std::copy(pattern0, pattern0 + npoints, std::back_inserter(pattern));  
  193.         else  
  194.         {  
  195.             int ntuples = descriptorSize()*4;  
  196.             initializeOrbPattern(pattern0, pattern, ntuples, WTA_K, npoints);  
  197.         }  
  198.     }  
  199.   
  200.     _keypoints.clear();  
  201.     int offset = 0;  
  202.     for (int level = 0; level < levelsNum; ++level)//依次计算每一层的角点描述子  
  203.     {  
  204.         // Get the features and compute their orientation  
  205.         vector<KeyPoint>& keypoints = allKeypoints[level];  
  206.         int nkeypoints = (int)keypoints.size();//本层内角点个数  
  207.   
  208.         // Compute the descriptors  
  209.         if (do_descriptors)  
  210.         {  
  211.             Mat desc;  
  212.             if (!descriptors.empty())  
  213.             {  
  214.                 desc = descriptors.rowRange(offset, offset + nkeypoints);  
  215.             }  
  216.   
  217.             offset += nkeypoints;  //偏移量  
  218.             // preprocess the resized image  
  219.             Mat& workingMat = imagePyramid[level];  
  220.             //boxFilter(working_mat, working_mat, working_mat.depth(), Size(5,5), Point(-1,-1), true, BORDER_REFLECT_101);  
  221.             GaussianBlur(workingMat, workingMat, Size(7, 7), 2, 2, BORDER_REFLECT_101);//高斯平滑图像  
  222.             computeDescriptors(workingMat, keypoints, desc, pattern, descriptorSize(), WTA_K);//计算本层内角点的描述子,(3)  
  223.         }  
  224.   
  225.         // Copy to the output data  
  226.         if (level != firstLevel)  //角点位置信息返回到原图上  
  227.         {  
  228.             float scale = getScale(level, firstLevel, scaleFactor);  
  229.             for (vector<KeyPoint>::iterator keypoint = keypoints.begin(),  
  230.                  keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)  
  231.                 keypoint->pt *= scale;   
  232.         }  
  233.         // And add the keypoints to the output  
  234.         _keypoints.insert(_keypoints.end(), keypoints.begin(), keypoints.end());//存入描述子信息,返回  
  235.     }  
  236. }  

(1)提取角点:computeKeyPoints

imagePyramid:即构造好的金字塔

[cpp]  view plain  copy 
 
  1. /** Compute the ORB keypoints on an image 
  2.  * @param image_pyramid the image pyramid to compute the features and descriptors on 
  3.  * @param mask_pyramid the masks to apply at every level 
  4.  * @param keypoints the resulting keypoints, clustered per level 
  5.  */  
  6. static void computeKeyPoints(const vector<Mat>& imagePyramid,  
  7.                              const vector<Mat>& maskPyramid,  
  8.                              vector<vector<KeyPoint> >& allKeypoints,  
  9.                              int nfeatures, int firstLevel, double scaleFactor,  
  10.                              int edgeThreshold, int patchSize, int scoreType )  
  11. {  
  12.     int nlevels = (int)imagePyramid.size();  //金字塔层数  
  13.     vector<int> nfeaturesPerLevel(nlevels);  
  14.   
  15.     // fill the extractors and descriptors for the corresponding scales  
  16.     float factor = (float)(1.0 / scaleFactor);  
  17.     float ndesiredFeaturesPerScale = nfeatures*(1 - factor)/(1 - (float)pow((double)factor, (double)nlevels));//  
  18.   
  19.     int sumFeatures = 0;  
  20.     forint level = 0; level < nlevels-1; level++ )   //对每层图像上分配相应角点数  
  21.     {  
  22.         nfeaturesPerLevel[level] = cvRound(ndesiredFeaturesPerScale);  
  23.         sumFeatures += nfeaturesPerLevel[level];  
  24.         ndesiredFeaturesPerScale *= factor;  
  25.     }  
  26.     nfeaturesPerLevel[nlevels-1] = std::max(nfeatures - sumFeatures, 0);//剩下角点数,由最上层图像提取  
  27.   
  28.     // Make sure we forget about what is too close to the boundary  
  29.     //edge_threshold_ = std::max(edge_threshold_, patch_size_/2 + kKernelWidth / 2 + 2);  
  30.   
  31.     // pre-compute the end of a row in a circular patch  
  32.     int halfPatchSize = patchSize / 2;           //计算每个特征点圆邻域的位置信息  
  33.     vector<int> umax(halfPatchSize + 2);  
  34.     int v, v0, vmax = cvFloor(halfPatchSize * sqrt(2.f) / 2 + 1);  
  35.     int vmin = cvCeil(halfPatchSize * sqrt(2.f) / 2);  
  36.     for (v = 0; v <= vmax; ++v)           //  
  37.         umax[v] = cvRound(sqrt((double)halfPatchSize * halfPatchSize - v * v));  
  38.     // Make sure we are symmetric  
  39.     for (v = halfPatchSize, v0 = 0; v >= vmin; --v)  
  40.     {  
  41.         while (umax[v0] == umax[v0 + 1])  
  42.             ++v0;  
  43.         umax[v] = v0;  
  44.            ++v0;  
  45.     }  
  46.   
  47.     allKeypoints.resize(nlevels);  
  48.   
  49.     for (int level = 0; level < nlevels; ++level)  
  50.     {  
  51.         int featuresNum = nfeaturesPerLevel[level];  
  52.         allKeypoints[level].reserve(featuresNum*2);  
  53.   
  54.         vector<KeyPoint> & keypoints = allKeypoints[level];  
  55.   
  56.         // Detect FAST features, 20 is a good threshold  
  57.         FastFeatureDetector fd(20, true);        
  58.         fd.detect(imagePyramid[level], keypoints, maskPyramid[level]);//Fast角点检测  
  59.   
  60.         // Remove keypoints very close to the border  
  61.         KeyPointsFilter::runByImageBorder(keypoints, imagePyramid[level].size(), edgeThreshold);//去除邻近边界的点  
  62.   
  63.         if( scoreType == ORB::HARRIS_SCORE )  
  64.         {  
  65.             // Keep more points than necessary as FAST does not give amazing corners  
  66.             KeyPointsFilter::retainBest(keypoints, 2 * featuresNum);//按Fast强度排序,保留前2*featuresNum个特征点  
  67.   
  68.             // Compute the Harris cornerness (better scoring than FAST)  
  69.             HarrisResponses(imagePyramid[level], keypoints, 7, HARRIS_K); //计算每个角点的Harris强度响应  
  70.         }  
  71.   
  72.         //cull to the final desired level, using the new Harris scores or the original FAST scores.  
  73.         KeyPointsFilter::retainBest(keypoints, featuresNum);//按Harris强度排序,保留前featuresNum个  
  74.   
  75.         float sf = getScale(level, firstLevel, scaleFactor);  
  76.   
  77.         // Set the level of the coordinates  
  78.         for (vector<KeyPoint>::iterator keypoint = keypoints.begin(),  
  79.              keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)  
  80.         {  
  81.             keypoint->octave = level;  //层信息  
  82.             keypoint->size = patchSize*sf; //  
  83.         }  
  84.   
  85.         computeOrientation(imagePyramid[level], keypoints, halfPatchSize, umax);  //计算角点的方向,(2)分析  
  86.     }  
  87. }  
(2)为每个角点计算主方向,质心法;
[cpp]  view plain  copy 
 
  1. static void computeOrientation(const Mat& image, vector<KeyPoint>& keypoints,  
  2.                                int halfPatchSize, const vector<int>& umax)  
  3. {  
  4.     // Process each keypoint  
  5.     for (vector<KeyPoint>::iterator keypoint = keypoints.begin(),  //为每个角点计算主方向  
  6.          keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)  
  7.     {  
  8.         keypoint->angle = IC_Angle(image, halfPatchSize, keypoint->pt, umax);//计算质心方向  
  9.     }  
  10. }  
[cpp]  view plain  copy 
 
  1. static float IC_Angle(const Mat& image, const int half_k, Point2f pt,  
  2.                       const vector<int> & u_max)  
  3. {  
  4.     int m_01 = 0, m_10 = 0;  
  5.   
  6.     const uchar* center = &image.at<uchar> (cvRound(pt.y), cvRound(pt.x));  
  7.   
  8.     // Treat the center line differently, v=0  
  9.     for (int u = -half_k; u <= half_k; ++u)  
  10.         m_10 += u * center[u];  
  11.   
  12.     // Go line by line in the circular patch  
  13.     int step = (int)image.step1();  
  14.     for (int v = 1; v <= half_k; ++v)    //每次处理对称的两行v  
  15.     {  
  16.         // Proceed over the two lines  
  17.         int v_sum = 0;  
  18.         int d = u_max[v];  
  19.         for (int u = -d; u <= d; ++u)  
  20.         {  
  21.             int val_plus = center[u + v*step], val_minus = center[u - v*step];  
  22.             v_sum += (val_plus - val_minus); //计算m_01时,位置上差一个符号  
  23.             m_10 += u * (val_plus + val_minus);  
  24.         }  
  25.         m_01 += v * v_sum;//计算上下两行的m_01  
  26.     }  
  27.   
  28.     return fastAtan2((float)m_01, (float)m_10);//计算角度  
  29. }  
(3)计算特征点描述子

[cpp]  view plain  copy 
 
  1. static void computeDescriptors(const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors,  
  2.                                const vector<Point>& pattern, int dsize, int WTA_K)  
  3. {  
  4.     //convert to grayscale if more than one color  
  5.     CV_Assert(image.type() == CV_8UC1);  
  6.     //create the descriptor mat, keypoints.size() rows, BYTES cols  
  7.     descriptors = Mat::zeros((int)keypoints.size(), dsize, CV_8UC1);  
  8.   
  9.     for (size_t i = 0; i < keypoints.size(); i++)  
  10.         computeOrbDescriptor(keypoints[i], image, &pattern[0], descriptors.ptr((int)i), dsize, WTA_K);  
  11. }  
[cpp]  view plain  copy 
 
  1. static void computeOrbDescriptor(const KeyPoint& kpt,  
  2.                                  const Mat& img, const Point* pattern,  
  3.                                  uchar* desc, int dsize, int WTA_K)  
  4. {  
  5.     float angle = kpt.angle;   
  6.     //angle = cvFloor(angle/12)*12.f;  
  7.     angle *= (float)(CV_PI/180.f);  
  8.     float a = (float)cos(angle), b = (float)sin(angle);  
  9.   
  10.     const uchar* center = &img.at<uchar>(cvRound(kpt.pt.y), cvRound(kpt.pt.x));  
  11.     int step = (int)img.step;  
  12.   
  13. #if 1  
  14.     #define GET_VALUE(idx) \       //取旋转后一个像素点的值  
  15.         center[cvRound(pattern[idx].x*b + pattern[idx].y*a)*step + \  
  16.                cvRound(pattern[idx].x*a - pattern[idx].y*b)]  
  17. #else  
  18.     float x, y;  
  19.     int ix, iy;  
  20.     #define GET_VALUE(idx) \ //取旋转后一个像素点,插值法  
  21.         (x = pattern[idx].x*a - pattern[idx].y*b, \  
  22.         y = pattern[idx].x*b + pattern[idx].y*a, \  
  23.         ix = cvFloor(x), iy = cvFloor(y), \  
  24.         x -= ix, y -= iy, \  
  25.         cvRound(center[iy*step + ix]*(1-x)*(1-y) + center[(iy+1)*step + ix]*(1-x)*y + \  
  26.                 center[iy*step + ix+1]*x*(1-y) + center[(iy+1)*step + ix+1]*x*y))  
  27. #endif  
  28.   
  29.     if( WTA_K == 2 )  
  30.     {  
  31.         for (int i = 0; i < dsize; ++i, pattern += 16)//每个特征描述子长度为32个字节  
  32.         {  
  33.             int t0, t1, val;  
  34.             t0 = GET_VALUE(0); t1 = GET_VALUE(1);  
  35.             val = t0 < t1;  
  36.             t0 = GET_VALUE(2); t1 = GET_VALUE(3);  
  37.             val |= (t0 < t1) << 1;  
  38.             t0 = GET_VALUE(4); t1 = GET_VALUE(5);  
  39.             val |= (t0 < t1) << 2;  
  40.             t0 = GET_VALUE(6); t1 = GET_VALUE(7);  
  41.             val |= (t0 < t1) << 3;  
  42.             t0 = GET_VALUE(8); t1 = GET_VALUE(9);  
  43.             val |= (t0 < t1) << 4;  
  44.             t0 = GET_VALUE(10); t1 = GET_VALUE(11);  
  45.             val |= (t0 < t1) << 5;  
  46.             t0 = GET_VALUE(12); t1 = GET_VALUE(13);  
  47.             val |= (t0 < t1) << 6;  
  48.             t0 = GET_VALUE(14); t1 = GET_VALUE(15);  
  49.             val |= (t0 < t1) << 7;  
  50.   
  51.             desc[i] = (uchar)val;  
  52.         }  
  53.     }  
  54.     else if( WTA_K == 3 )  
  55.     {  
  56.         for (int i = 0; i < dsize; ++i, pattern += 12)  
  57.         {  
  58.             int t0, t1, t2, val;  
  59.             t0 = GET_VALUE(0); t1 = GET_VALUE(1); t2 = GET_VALUE(2);  
  60.             val = t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0);  
  61.   
  62.             t0 = GET_VALUE(3); t1 = GET_VALUE(4); t2 = GET_VALUE(5);  
  63.             val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 2;  
  64.   
  65.             t0 = GET_VALUE(6); t1 = GET_VALUE(7); t2 = GET_VALUE(8);  
  66.             val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 4;  
  67.   
  68.             t0 = GET_VALUE(9); t1 = GET_VALUE(10); t2 = GET_VALUE(11);  
  69.             val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 6;  
  70.   
  71.             desc[i] = (uchar)val;  
  72.         }  
  73.     }  
  74.     else if( WTA_K == 4 )  
  75.     {  
  76.         for (int i = 0; i < dsize; ++i, pattern += 16)  
  77.         {  
  78.             int t0, t1, t2, t3, u, v, k, val;  
  79.             t0 = GET_VALUE(0); t1 = GET_VALUE(1);  
  80.             t2 = GET_VALUE(2); t3 = GET_VALUE(3);  
  81.             u = 0, v = 2;  
  82.             if( t1 > t0 ) t0 = t1, u = 1;  
  83.             if( t3 > t2 ) t2 = t3, v = 3;  
  84.             k = t0 > t2 ? u : v;  
  85.             val = k;  
  86.   
  87.             t0 = GET_VALUE(4); t1 = GET_VALUE(5);  
  88.             t2 = GET_VALUE(6); t3 = GET_VALUE(7);  
  89.             u = 0, v = 2;  
  90.             if( t1 > t0 ) t0 = t1, u = 1;  
  91.             if( t3 > t2 ) t2 = t3, v = 3;  
  92.             k = t0 > t2 ? u : v;  
  93.             val |= k << 2;  
  94.   
  95.             t0 = GET_VALUE(8); t1 = GET_VALUE(9);  
  96.             t2 = GET_VALUE(10); t3 = GET_VALUE(11);  
  97.             u = 0, v = 2;  
  98.             if( t1 > t0 ) t0 = t1, u = 1;  
  99.             if( t3 > t2 ) t2 = t3, v = 3;  
  100.             k = t0 > t2 ? u : v;  
  101.             val |= k << 4;  
  102.   
  103.             t0 = GET_VALUE(12); t1 = GET_VALUE(13);  
  104.             t2 = GET_VALUE(14); t3 = GET_VALUE(15);  
  105.             u = 0, v = 2;  
  106.             if( t1 > t0 ) t0 = t1, u = 1;  
  107.             if( t3 > t2 ) t2 = t3, v = 3;  
  108.             k = t0 > t2 ? u : v;  
  109.             val |= k << 6;  
  110.   
  111.             desc[i] = (uchar)val;  
  112.         }  
  113.     }  
  114.     else  
  115.         CV_Error( CV_StsBadSize, "Wrong WTA_K. It can be only 2, 3 or 4." );  
  116.   
  117.     #undef GET_VALUE  
  118. }  


关于octave

Number of OCTAVE. Increasing the scale by an OCTAVE means  doubling the size of the smoothing kernel, whose effect is roughly  equivalent to halving the image resolution. By default, the scale  space spans as many OCTAVE as possible (i.e. roughly <code>  log2(min(width,height)</code>), which has the effect of searching  keypoints of all possible sizes.- <b>First OCTAVE index</b>. By convention, the OCTAVE of index 0  starts with the image full resolution. Specifying an index greater  than 0 starts the scale space at a lower resolution (e.g. 1 halves  the resolution). Similarly, specifying a negative index starts the  scale space at an higher resolution image, and can be useful to  extract very small features (since this is obtained by interpolating  the input image, it does not make much sense to go past -1).
- <b>Number of levels per OCTAVE</b>. Each OCTAVE is sampled at this  given number of intermediate scales (by default 3). Increasing this number might in principle return more refined keypoints, but in   practice can make their selection unstable due to noise (see [1]).Keypoints are further refined by eliminating those that are likely tobe unstable, either because they are selected nearby an image edge, rather than an image blob, or are found on image structures with low contrast.

参考自 https://github.com/vlfeat/vlfeat/blob/38a03e12daf50ee98633de06120834d0d1d87e23/vl/sift.c#L1948


关于 threshold使用:

http://www.itnose.net/detail/6060285.html

参考:

Ethan Rublee et. ORB:An Efficient Alternative to SIFT or SURF

http://www.cnblogs.com/ronny/p/4083537.html

猜你喜欢

转载自blog.csdn.net/jucilan3330/article/details/65935232