认真的虎ORBSLAM2源码解读(四):图解ORB特征提取ORBextractor

本文详细解读ORB_SLAM2中的ORB特征提取过程,包括ORBextractor构造函数中umax的计算,图像金字塔的生成,关键点的四叉树分布与非极大值抑制。内容涵盖ORB特征点的定位、描述子计算及四叉树在特征点筛选中的应用。
摘要由CSDN通过智能技术生成

1.参考博客

1.1.理论基础参考

Oriented FAST and Rotated BRIEF
Features From Accelerated Segment Test
Binary Robust Independent Elementary Features
Sift中尺度空间、高斯金字塔、差分金字塔(DOG金字塔)、图像金字塔

1.2.参考博客

ORB-SLAM2的源码阅读(二):ORB特征提取
一起学ORBSLAM(2)ORB特征点提取
ORBSLAM2源码学习(1)ORBextractor

2.头文件ORBextractor.h

// 分配四叉树时用到的结点类型
class ExtractorNode {
   
    public:
        ExtractorNode() : bNoMore(false) {
   }
        void DivideNode(ExtractorNode &n1, ExtractorNode &n2, ExtractorNode &n3, ExtractorNode &n4);
        std::vector<cv::KeyPoint> vKeys;
        cv::Point2i UL, UR, BL, BR;
        std::list<ExtractorNode>::iterator lit;
        bool bNoMore;
    };
 
class ORBextractor
{
   
public:
    
    enum {
   HARRIS_SCORE=0, FAST_SCORE=1 };
    
    //nfeatures,ORB特征点数量   scaleFactor,相邻层的放大倍数  nlevels,层数  iniThFAST,提取FAST角点时初始阈值   minThFAST提取FAST角点时,更小的阈值  
    //设置两个阈值的原因是在FAST提取角点进行分块后有可能在某个块中在原始阈值情况下提取不到角点,使用更小的阈值进一步提取
    //ORBextractor构造函数
    ///功能:提取特征前的准备工作
    ORBextractor(int nfeatures, float scaleFactor, int nlevels,
                 int iniThFAST, int minThFAST);
 
    ~ORBextractor(){
   }
 
    // Compute the ORB features and descriptors on an image.
    // ORB are dispersed on the image using an octree.
    // Mask is ignored in the current implementation.
    //重载()运算符
    void operator()( cv::InputArray image, cv::InputArray mask,
      std::vector<cv::KeyPoint>& keypoints,
      cv::OutputArray descriptors);
 
    int inline GetLevels(){
   
        return nlevels;}
 
    float inline GetScaleFactor(){
   
        return scaleFactor;}
 
    std::vector<float> inline GetScaleFactors(){
   
        return mvScaleFactor;
    }
 
    std::vector<float> inline GetInverseScaleFactors(){
   
        return mvInvScaleFactor;
    }
 
    std::vector<float> inline GetScaleSigmaSquares(){
   
        return mvLevelSigma2;
    }
 
    std::vector<float> inline GetInverseScaleSigmaSquares(){
   
        return mvInvLevelSigma2;
    }
    //图像金字塔 存放各层的图片
    std::vector<cv::Mat> mvImagePyramid;
 
protected:
    //建立图像金字塔
	//将原始图像一级级缩小并依次存在mvImagePyramid里
    void ComputePyramid(cv::Mat image);
    //利用四叉树提取高斯金字塔中每层图像的orb关键点
    void ComputeKeyPointsOctTree(std::vector<std::vector<cv::KeyPoint> >& allKeypoints);    
    //将关键点分配到四叉树,筛选关键点
    std::vector<cv::KeyPoint> DistributeOctTree(const std::vector<cv::KeyPoint>& vToDistributeKeys, const int &minX,const int &maxX, const int &minY, const int &maxY, const int &nFeatures, const int &level);
 
 	//作者遗留下的旧的orb关键点方法
    void ComputeKeyPointsOld(std::vector<std::vector<cv::KeyPoint> >& allKeypoints);
    //存储关键点附近patch的点对相对位置
    std::vector<cv::Point> pattern;
    //提取特征点的最大数量
    int nfeatures;
    //每层之间的缩放比例
    double scaleFactor;
    //高斯金字塔的层数
    int nlevels;
    //iniThFAST提取FAST角点时初始阈值
    int iniThFAST;
    //minThFAST提取FAST角点时更小的阈值
    int minThFAST;
    //每层的特征数量
    std::vector<int> mnFeaturesPerLevel;
    //Patch圆的u轴方向最大坐标
    std::vector<int> umax;
    //每层的相对于原始图像的缩放比例
    std::vector<float> mvScaleFactor;
    //mvScaleFactor的倒数
    std::vector<float> mvInvScaleFactor;
    //mvScaleFactor的平方
    std::vector<float> mvLevelSigma2;
    //mvScaleFactor的平方的倒数
    std::vector<float> mvInvLevelSigma2;
};

3.源文件ORBextractor.cc

3.1.类ORBextractor构造函数

功能:提取特征前的准备工作

///功能:提取特征前的准备工作
ORBextractor::ORBextractor(int _nfeatures, float _scaleFactor, int _nlevels,
         int _iniThFAST, int _minThFAST):
    nfeatures(_nfeatures), scaleFactor(_scaleFactor), nlevels(_nlevels),
    iniThFAST(_iniThFAST), minThFAST(_minThFAST)
{
   
    mvScaleFactor.resize(nlevels);
    mvLevelSigma2.resize(nlevels);
    //初始化mvScaleFactor,mvLevelSigma2
    mvScaleFactor[0]=1.0f;
    mvLevelSigma2[0]=1.0f;
    //根据scaleFactor计算每层的mvScaleFactor,其值单调递增
    for(int i=1; i<nlevels; i++)
    {
   
        mvScaleFactor[i]=mvScaleFactor[i-1]*scaleFactor;
        mvLevelSigma2[i]=mvScaleFactor[i]*mvScaleFactor[i];
    }

    mvInvScaleFactor.resize(nlevels);
    mvInvLevelSigma2.resize(nlevels);
    
    //计算 mvInvScaleFactor,mvInvLevelSigma2
    for(int i=0; i<nlevels; i++)
    {
   
        mvInvScaleFactor[i]=1.0f/mvScaleFactor[i];
        mvInvLevelSigma2[i]=1.0f/mvLevelSigma2[i];
    }

    mvImagePyramid.resize(nlevels);
    
    //对于缩放的每层高斯金字塔图像,计算其对应每层待提取特征的数量放入mnFeaturesPerLevel中,使得每层特征点的数列成等比数列数列递减
    mnFeaturesPerLevel.resize(nlevels);
    float factor = 1.0f / scaleFactor;
    float nDesiredFeaturesPerScale = nfeatures*(1 - factor)/(1 - (float)pow((double)factor, (double)nlevels));

    int sumFeatures = 0;
    for( int level = 0; level < nlevels-1; level++ )
    {
   
        mnFeaturesPerLevel[level] = cvRound(nDesiredFeaturesPerScale);
        sumFeatures += mnFeaturesPerLevel[level];
        nDesiredFeaturesPerScale *= factor;
    }
    mnFeaturesPerLevel[nlevels-1] = std::max(nfeatures - sumFeatures, 0);
    
    //准备计算关键点keypoint的brief描述子时所需要的pattern
    //这个pattern一共有512个点对;
    const int npoints = 512;
    //bit_pattern_31_是一个1024维的数组,其信息是512对点对的相对中心点的像素坐标
    //这里将bit_pattern_31_里的信息以Point的形式存储在了std::vector<cv::Point> pattern里;
    //最后pattern储存了512个Point
    const Point* pattern0 = (const Point*)bit_pattern_31_;
    std::copy(pattern0, pattern0 + npoints, std::back_inserter(pattern));

    //This is for orientation
    // pre-compute the end of a row in a circular patch
    //这里是为了计算关键点方向的准备工作
    //我们是要在以关键点keypoint像素坐标点为中心的直径为PATCH_SIZE,半径为HALF_PATCH_SIZE的patch圆内计算关键点keypoint的方向。
    //那如何描述这个patch圆的范围呢?这里选择的是储存不同v所对应的的umax来描述这个patch圆的范围。
    umax.resize(HALF_PATCH_SIZE + 1);

    int v, v0, vmax = cvFloor(HALF_PATCH_SIZE * sqrt(2.f) / 2 + 1);
    int vmin = cvCeil(HALF_PATCH_SIZE * sqrt(2.f) / 2);
    const double hp2 = HALF_PATCH_SIZE*HALF_PATCH_SIZE;
    for (v = 0; v <= vmax; ++v)
        umax[v] = cvRound(sqrt(hp2 - v * v));

    // Make sure we are symmetric
    //其实是算当v=vmax至HALF_PATCH_SIZE时的umax[v]
    for (v = HALF_PATCH_SIZE, v0 = 0; v >= vmin; --v)
    {
   
        while (umax[v0] == umax[v0 + 1])
            ++v0;
        umax[v] = v0;
        ++v0;
    }
}

3.1.1.umax的计算

为什么要计算umax?
我们知道,ORB特征点是由FAST关键点和BRIEF描述子组成的。
ORB特征点的关键点告诉我们ORB特征点的位置信息。在后面我们还需要计算ORB特征点的方向。
而ORB特征点是根据像素坐标点为中心的直径为PATCH_SIZE,半径为HALF_PATCH_SIZE的patch圆内计算出来的(具体计算方法在)。
在源文件ORBextractor.cc,我们可以看到这个patch圆的相关信息

const int PATCH_SIZE = 31;
const int HALF_PATCH_SIZE = 15<
  • 36
    点赞
  • 133
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值