ORB_SLAM 中特征提取代码介绍

ORB_SLAM 中特征提取部分介绍

来自一个小学渣的介绍,仅供参考
ORB特征提取可以分为两部分:

  1. 具有方向的FAST兴趣点检测;
  2. 具有旋转不变的BRIEF兴趣点描述子。
    而ORB特征应用的一个最成功的例子恐怕就是ORB_SLAM了。下面要介绍的就是ORB_SLAM中extract ORB的部分。
    ORB特征提取部分
    由于该部分是优化的重点,所以该部分原理着重介绍。
    1、 初始化。对于特征的提取过程首先是初始化过程,初始化过程的目的是将接下来可能用到的参数和图像赋值或生成。在初始化的过程中需要初始化的参数如下:
    //参数设计,重点
    int nFeatures = 1000; //特征点数
    float fScaleFactor = 1.2; //高斯金字塔的放大系数
    int nLevels = 8; //高斯金字塔的层数
    int fIniThFAST = 40; //FAST特征的第一个参数
    int fMinThFAST = 7; //采样点不足时FAST参数
    以上参数的意义是,nFeatures表示需要在每帧图像中提取的特征点的数量。为了保证尺度不变性,需要建立高斯金字塔,在本项目中,作者采用的是nLevels指示的8层金字塔。对于每层金字塔都是原图像经过缩放得到的,缩放的系数是前面的fScaleFactor,对于每层金字塔,所要提取的点数根据公式计算得到,最终的总是为1000. 剩下的两个参数是fIniThFAST和fMinThFAST,其是角点检测过程中FAST算法的阈值,之所以有两个阈值,是因为首先用较大的参数可以提取出更加明显的角点,如果提取的数量明显小于期望值,那么就需要适当将参数降低使得最终得到的角点数量满足要求。
    上述参数初始化完成后,根据公式计算金字塔每层要提取的特征点数量。对于Brief特征中需要用到的512个随机点,采用的是直接赋值的方法,作者已经给出了31*31邻域中相对的点坐标模板,计算时直接换算成相对于图像的绝对坐标即可。
    接下来要考虑的是计算特征点的方向。本项目中采用的是在一个圆形区域中计算,为了能够快速计算该区域,采用的是首先生成蒙版的方法。所以首先初始化的过程中就将该圆形区域的相对坐标存储在umax数组中。至此,初始化过程结束。
for (v = 0; v <= vmax; ++v)
        umax[v] = cvRound(sqrt(hp2 - v * v));

    // 计算一个对称的圆形区域
    for (v = HALF_PATCH_SIZE, v0 = 0; v >= vmin; --v)
    {
        while (umax[v0] == umax[v0 + 1])
            ++v0;
        umax[v] = v0;
        ++v0;
    }

2、计算高斯金字塔。该过程较为简单,直接通过图像的缩放就可以达到目的。主要使用的函数就是如下所示的resize函数。

 resize(mvImagePyramid[level - 1], mvImagePyramid[level], sz, 0, 0, INTER_LINEAR);

该函数可以通过线性缩放得到需要的图像。

3、特征点提取与分配。为了使接下来计算PnP时能够更加准确,需要保持的原则就是尽量使得特征点能够均匀分布在整个图像中,所以需要将图像分成更小的部分分别提取特征点,并用某种机制来分配。在本项目中,作者提供了两种分配方案分别是八叉树的方法和传统的方法。考虑到在室内场景中并不需要提供太多的特征点,而在1000个特征点以下时,传统的方法表现要由于建造八叉树的方法。所以在实际应用中,都是采用传统的方法,而不去花费大量收时间用于建造八叉树。

 void ORBextractor::ComputeKeyPointsOld(std::vector<std::vector > &allKeypoints)

传统方法的第一步就是计算需要将图像分割成多少个元包(cell),对于每个元包分别提取特征点。元包的计算方法为,根据需要提取的特征点数目,假设每个元包中需要提取5个特征点,以此来进行计算需要的cell数目。
接着对上面计算好的元包分别进行特征点的提取。这里注意,由于FAST特征在计算角点时向内缩进了3个像素才开始计算,所以在使用FAST之前还需要对图像加一条宽度为3像素的边。然后就要用到之前初始化的两个阈值参数,首先使用阈值较大的参数作为FAST特征点检测的阈值,如果提取到的特征点数目足够多,那么直接计算下一个元包即可,否则就要使用较小的参数重新提取。在本项目中,特征点数目的阈值设定为3.
然后就涉及到了特征点的数目分配问题。由于图像中不可避免的存在纹理丰富和纹理较浅的区域,在纹理较丰富的区域,角点的数目可能提取很多,而在纹理不丰富的区域,角点的数目可能很少。而在分配各区域选取的特征点数目时,就要考虑前面提到的极可能均匀的问题。所以采用的方法是循环将特征点数目不足的元包中的剩余数目分配到其他所有元包中,知道最后取得足够数量的特征点。当然,如果最初提取的特征点数目就不足预期,那么直接全部选取即可。所以这种方法并不能保证最终得到的特征点数目一定能达到1000。
对于那些特征点数目特别多的元包,采用的是对各个角点的质量进行排序,选择最好的前n个特征点作为最终结果。
4、计算方向。为了使得提取的特征点具有旋转不变性,需要计算每个特征点的方向。方法是计算以特征点为中心以像素为权值的圆形区域上的重心,以中心和重心的连线作为该特征点的方向。

static void computeOrientation(const Mat& image, vector& keypoints, const vector<int>& umax)
{

    for (vector::iterator keypoint = keypoints.begin(),
        keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)
    {
        keypoint->angle = IC_Angle(image, keypoint->pt, umax);
    }
}

5、计算描述子。Brief特征一大特点就是得到的是二进制的描述子,这样可以直接用异或计算汉明距离,从而方便地进行匹配。计算首先将坐标系的X轴旋转到和前面计算的方向重合的方向,然后用之前已经直接给出的随机点计算二进制描述子。这里为了抵御噪声的影响,并没有直接比较两个像素点的灰度值,而是首先计算两个像素点周围5*5像素和然后再比较,这样就相当于进行了一次滤波,能够有效的抑制噪声。
以上的代码部分可以直接在github上下载。我主要研究的是特征提取与描述子生成的部分,所以将这部分单独拿出来分析了。另外,通过实验可知,将FAST算法替换为最先进的AGAST算法可以实现一定程度上的加速。

Raul Mur-Artal, J.M.MMontiel, and Juan D. Tardos ORB-SLAM: a versatile and Accurate Monocular SLAM System ICCV 2010
Ethan Rublee and Vincent Rabaud and Kurt Konolige and Gary Bradski, ORB: an efcient alternative to SIFT or SURF, ICCV 2011

你可能感兴趣的:(ORB-SLAM)