• ORB-SLAM2 ---- ORBextractor::ORBextractor类


    目录

    1.这个类做了什么

    2.特征点提取器的构造函数

    3.ORBextractor::ORBextractor类解析 

    3.1 逐层计算图像金字塔中图像相当于初始图像的缩放系数、平方、倒数 mvScaleFactor、mvLevelSigma2、mvInvScaleFactor、mvInvLevelSigma2

    3.2  逐层计算要分配的特征点个数mnFeaturesPerLevel

    3.2.1 数学背景

    3.2.2 相应代码

    3.3 将int数组变化成cv::Point点

    3.4 计算存储不同v位置对应的u的最大值的容器umax


    1.这个类做了什么

            计算每层图像金字塔缩系数容器mvScaleFactor、缩放系数平方mvLevelSigma2的容器、金字塔缩系数倒数容器mvInvScaleFactor、缩放系数平方倒数容器mvLevelSigma2、计算每层图像金字塔特征点数量的容器mnFeaturesPerLevel,计算vmax

    2.特征点提取器的构造函数

    1. ORBextractor::ORBextractor(int _nfeatures, //指定要提取的特征点数目
    2. float _scaleFactor, //指定图像金字塔的缩放系数
    3. int _nlevels, //指定图像金字塔的层数
    4. int _iniThFAST, //指定初始的FAST特征点提取参数,可以提取出最明显的角点
    5. int _minThFAST): //如果初始阈值没有检测到角点,降低到这个阈值提取出弱一点的角点
    6. nfeatures(_nfeatures), scaleFactor(_scaleFactor), nlevels(_nlevels),
    7. iniThFAST(_iniThFAST), minThFAST(_minThFAST)//设置这些参数

            传进来的参数是

    1.要总共要提取的特征点数目,通过这个类会将这些特征点均匀分布在各个图像金字塔中,最终在mnFeaturesPerLevel容器中存储。

    2.图像金字塔的缩放系数,用于辅助计算各层应该分配的特征点数量。

    3.提取FAST特征点的阈值,以防用高标准提取特征点而提取不到那么多特征点转而采用低阈值。

    3.ORBextractor::ORBextractor类解析 

    3.1 逐层计算图像金字塔中图像相当于初始图像的缩放系数、平方、倒数 mvScaleFactor、mvLevelSigma2、mvInvScaleFactor、mvInvLevelSigma2

    1. //存储每层图像缩放系数的vector调整为符合图层数目的大小
    2. mvScaleFactor.resize(nlevels);
    3. //存储这个sigma^2,其实就是每层图像相对初始图像缩放因子的平方
    4. mvLevelSigma2.resize(nlevels);
    5. //对于初始图像,这两个参数都是1
    6. mvScaleFactor[0]=1.0f;
    7. mvLevelSigma2[0]=1.0f;
    8. //然后逐层计算图像金字塔中图像相当于初始图像的缩放系数
    9. for(int i=1; i
    10. {
    11. //其实就是这样累乘计算得出来的
    12. mvScaleFactor[i]=mvScaleFactor[i-1]*scaleFactor;
    13. //原来这里的sigma^2就是每层图像相对于初始图像缩放因子的平方
    14. mvLevelSigma2[i]=mvScaleFactor[i]*mvScaleFactor[i];
    15. }
    16. //接下来的两个向量保存上面的参数的倒数
    17. mvInvScaleFactor.resize(nlevels);
    18. mvInvLevelSigma2.resize(nlevels);
    19. for(int i=0; i
    20. {
    21. mvInvScaleFactor[i]=1.0f/mvScaleFactor[i];
    22. mvInvLevelSigma2[i]=1.0f/mvLevelSigma2[i];
    23. }
    24. //调整图像金字塔vector以使得其符合设定的图像层数
    25. mvImagePyramid.resize(nlevels);
    26. //每层需要提取出来的特征点个数,这个向量也要根据图像金字塔设定的层数进行调整
    27. mnFeaturesPerLevel.resize(nlevels);
    1. # ORB Extractor: Scale factor between levels in the scale pyramid
    2. ORBextractor.scaleFactor: 1.2
    3. # ORB Extractor: Number of levels in the scale pyramid
    4. ORBextractor.nLevels: 8

            mvScaleFactor存放各个层级金字塔的缩放系数,容器内容为 1.2 1.2*1.2 ....,长度为金字塔的层数。mvLevelSigma2存放mvScaleFactor的平方,剩余两个容器存放它们的倒数。

    1. ///这个是用来存储图像金字塔的变量,一个元素存储一层图像
    2. std::vector mvImagePyramid;
    std::vector<int> mnFeaturesPerLevel;		///<分配到每层图像中,要提取的特征点数目

    3.2  逐层计算要分配的特征点个数mnFeaturesPerLevel

    3.2.1 数学背景

    1. # ORB Extractor: Number of features per image
    2. ORBextractor.nFeatures: 1000
    3. # ORB Extractor: Number of levels in the scale pyramid
    4. ORBextractor.nLevels: 8

            这里用到了等比数列的思想,由于我们在配置文件里规定的图像金字塔数目为8,总特征点数目为1000,那么我们如何将这1000个特征点放到8层金子塔呢?

            我们采取金字塔层面积与分得特征点数量成正比的原则,我们先计算八层图像金字塔的总面积,再计算单位面积的特征点数量。

            再用各层面积乘以单位面积的特征点数量得到该层应分配的特征点数目,由于取整处理,于是将所有特征点-前面所有层的特征点个数和作为最后一层图像金字塔分配特征点数目。

    3.2.2 相应代码

    1. //图片降采样缩放系数的倒数
    2. float factor = 1.0f / scaleFactor;
    3. //第0层图像应该分配的特征点数量
    4. float nDesiredFeaturesPerScale = nfeatures*(1 - factor)/(1 - (float)pow((double)factor, (double)nlevels));
    5. //用于在特征点个数分配的,特征点的累计计数清空
    6. int sumFeatures = 0;
    7. //开始逐层计算要分配的特征点个数,顶层图像除外(看循环后面)
    8. for( int level = 0; level < nlevels-1; level++ )
    9. {
    10. //分配 cvRound : 返回个参数最接近的整数值
    11. mnFeaturesPerLevel[level] = cvRound(nDesiredFeaturesPerScale);
    12. //累计
    13. sumFeatures += mnFeaturesPerLevel[level];
    14. //乘系数
    15. nDesiredFeaturesPerScale *= factor;
    16. }
    17. //由于前面的特征点个数取整操作,可能会导致剩余一些特征点个数没有被分配,所以这里就将这个余出来的特征点分配到最高的图层中
    18. mnFeaturesPerLevel[nlevels-1] = std::max(nfeatures - sumFeatures, 0);

    3.3 将int数组变化成cv::Point点

    1. //成员变量pattern的长度,也就是点的个数,这里的512表示512个点(上面的数组中是存储的坐标所以是256*2*2)
    2. const int npoints = 512;
    3. //获取用于计算BRIEF描述子的随机采样点点集头指针
    4. //注意到pattern0数据类型为Points*,bit_pattern_31_是int[]型,所以这里需要进行强制类型转换
    5. const Point* pattern0 = (const Point*)bit_pattern_31_;
    6. //使用std::back_inserter的目的是可以快覆盖掉这个容器pattern之前的数据 相当于初始化并删除
    7. //其实这里的操作就是,将在全局变量区域的、int格式的随机采样点以cv::point格式复制到当前类对象中的成员变量中
    8. //将这些工业模式下的点pattern0-pattern0 + npoints段内存内的点以cv::point格式复制到当前类对象中的成员变量pattern中
    9. std::copy(pattern0, pattern0 + npoints, std::back_inserter(pattern));
    1. static int bit_pattern_31_[256*4] =
    2. {
    3. 8,-3, 9,5/*mean (0), correlation (0)*/,
    4. 4,2, 7,-12/*mean (1.12461e-05), correlation (0.0437584)*/,
    5. -11,9, -8,2/*mean (3.37382e-05), correlation (0.0617409)*/,

            bit_pattern_31_存储描述子,因为描述子为一对点进行比较,因此256*4共记录了256*4/2=512对点的灰度值比较结果 。然后用pattern0保存指针信息,之后将bit_pattern_31_中的信息拷贝到pattern0中。

            std::copy(pattern0, pattern0 + npoints, std::back_inserter(pattern));的意思是将首地址到末地址pattern0 + npoints,作为一个cv::Point类型的数据包含2个int,于是pattern0 + npoints其实是走了pattern0 + npoints*2个int数据,然后将其尾插到pattern中。

    3.4 计算存储不同v位置对应的u的最大值的容器umax

    1. //umax存储不同v位置对应的u的最大值 u--x v--y
    2. umax.resize(HALF_PATCH_SIZE + 1);
    3. //cvFloor返回不大于参数的最大整数值,cvCeil返回不小于参数的最小整数值,cvRound则是四舍五入
    4. int v, //循环辅助变量
    5. v0, //辅助变量
    6. vmax = cvFloor(HALF_PATCH_SIZE * sqrt(2.f) / 2 + 1);
    7. int vmin = cvCeil(HALF_PATCH_SIZE * sqrt(2.f) / 2);
    8. const double hp2 = HALF_PATCH_SIZE*HALF_PATCH_SIZE;
    9. for (v = 0; v <= vmax; ++v)
    10. umax[v] = cvRound(sqrt(hp2 - v * v));
    11. for (v = HALF_PATCH_SIZE, v0 = 0; v >= vmin; --v)
    12. {
    13. while (umax[v0] == umax[v0 + 1])
    14. ++v0;
    15. umax[v] = v0;
    16. ++v0;
    17. }

            cvFloor返回不大于参数的最大整数值,cvCeil返回不小于参数的最小整数值,cvRound则是四舍五入。明白这三点后,我们开始进行代码的解读。

            我们定义vmax\frac{\sqrt{2}}{2}R+1 向下取整,vmin为\frac{\sqrt{2}}{2}R向上取整。hp2为半径的平方。然后从v=0到v=vmax用勾股定理\sqrt{R^2-v^2}计算出umax[v]的值。我们看看具体怎么算:

    1. const int PATCH_SIZE = 31; ///<使用灰度质心法计算特征点的方向信息时,图像块的大小,或者说是直径
    2. const int HALF_PATCH_SIZE = 15; ///<上面这个大小的一半,或者说是半径
    3. const int EDGE_THRESHOLD = 19; ///<算法生成的图像边

            这里vmax = vmin = 11。

            我们先从v=0 到 v=11 勾股定理计算 15 15 15 15 14 14 14 13 13 12 11 10

            然后从HALF_PATCH_SIZE开始计算(第二个for循环),我们判断umax[0] == umax[1]==15,v0++,我们判断umax[1] == umax[2] ==15,v0++,我们判断umax[2] == umax[3] ==15,v0++,我们判断umax[3] != umax[4] ,此时v0=3,我们让umax[HALF_PATCH_SIZE] = 3,以此类推...

  • 相关阅读:
    实战Spring Boot集成quartz任务调度框架
    requests 在 Python 3.2 中使用 OAuth 导入失败的问题与解决方案
    vs工具箱在哪里找
    入门力扣自学笔记278 C++ (题目编号:2605)
    安卓系统精简与优化
    初识MySQL
    Flutter组件--Padding和AnimatedPadding
    Python机器学习 | AI芯片调研
    分享一些实用的API接口
    网络渗透课2
  • 原文地址:https://blog.csdn.net/qq_41694024/article/details/126288776