ORB-SLAM2源码学习:ORBextractor.cc:ORBextractor特征提取器③
前言
特征点均匀化:
特征点一般集中在图像纹理比较丰富的区域,在缺乏纹理的区域则特征点数量会少。这会造成特征点冗余,本来几个特征点就能描述清楚某个区域,那么其余的该区域的特征点就多余了。当特征点过多的集中在某一个区域还会造成无法计算相机的位姿即特征点太过集中影响SLAM精度。
1.这个构造函数是干什么的?
ORBextractor是 ORB(Oriented FAST and Rotated BRIEF)特征提取器的构造函数,用于初始化 ORB 特征提取器的各项参数和内部数据结构。这个构造函数主要完成以下几项任务:
1.初始化特征提取器参数。
2.计算金字塔各层的缩放因子、层数平方值、缩放因子和层数平方值的倒数。
3.为图像金字塔容器和每层应分配的特征点容器分配内存。
4.按图像金字塔层级面积比例分配特征点数量(这里只是求出每层金字塔应该分配多少特征点,实际上并没用将特征点填入金字塔内)
5.加载预定义的二进制特征描述符。
6.为计算特征点方向进行预处理数据
2.构造函数ORBextractor的列表初始化
ORBextractor::ORBextractor(int _nfeatures, float _scaleFactor, int _nlevels,int _iniThFAST, int _minThFAST):nfeatures(_nfeatures),// 提取的特征点数。scaleFactor(_scaleFactor),// 图像金字塔的缩放系数。nlevels(_nlevels),// 图像金字塔层数。iniThFAST(_iniThFAST),// 提取FAST角点的阈值。minThFAST(_minThFAST)// 初始阈值检测不到角点则采用该较低的阈值。
3.构造函数ORBextractor内部解析
3.1 设置并计算金字塔的一些参数。
通过遍历金字塔的每一层,计算金字塔的缩放因子(mvScaleFactor)、层数平方(mvLevelSigma2)、缩放因子倒数(mvInvScaleFactor)、层数平方倒数(mvInvLevelSigma2)。
mvScaleFactor.resize(nlevels);//存储每层图像缩放系数的vector调整其大小为金字塔的层数。mvLevelSigma2.resize(nlevels);//存储每层图像相对初始图像缩放因子的平方的vector调整其大小为金字塔的层数。//对于初始图像,这两个参数设置为1。mvScaleFactor[0]=1.0f;mvLevelSigma2[0]=1.0f;for(int i=1; i<nlevels; i++)//遍历金字塔的每一层计算mvScaleFactor和mvLevelSigma2。{mvScaleFactor[i]=mvScaleFactor[i-1]*scaleFactor;//下层乘缩放因子scaleFactor(累乘)。mvLevelSigma2[i]=mvScaleFactor[i]*mvScaleFactor[i];// 缩放因子scaleFactor的平方。}mvInvScaleFactor.resize(nlevels);//存储每层图像缩放系数倒数的vector调整其大小为金字塔的层数。mvInvLevelSigma2.resize(nlevels);//存储每层图像相对初始图像缩放因子的平方倒数的vector调整其大小为金字塔的层数。for(int i=0; i<nlevels; i++)//遍历金字塔的每一层计算mvInvScaleFactor和mvInvLevelSigma2{//计算倒数mvInvScaleFactor[i]=1.0f/mvScaleFactor[i];mvInvLevelSigma2[i]=1.0f/mvLevelSigma2[i];}
mvImagePyramid.resize(nlevels);//调整图像金字塔vector的大小为层数。
3.2 定义并逐层分配的特征点个数mnFeaturesPerLevel
3.2.1 数学背景
如何给金字塔分配特征点数量?
面积求和利用了等比数列求和的思想。
3.2.2代码实现
mnFeaturesPerLevel.resize(nlevels);//调整金字塔每一层应该提取特征点数的vector大小,调整为金字塔的层数。float factor = 1.0f / scaleFactor;//为了下边计算每层应该分配多少的特征点作铺垫(作为计算公式的一部分)。float nDesiredFeaturesPerScale = nfeatures*(1 - factor)/(1 - (float)pow((double)factor, (double)nlevels));// 计算第0层应分配的特征点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);
这里我有个问题:那么如果每一层都四舍五入取大了,导致可分配的特征点数不够怎么办?
3.3将bit_pattern_31_中的信息拷贝到pattern中
const int npoints = 512;//变量pattern的长度。const Point* pattern0 = (const Point*)bit_pattern_31_;//(const Point*)强制类型转换将int[]转换为Point*std::copy(pattern0, pattern0 + npoints, std::back_inserter(pattern));// 将内容复制到pattern中。
为什么是512?
在 ORB 中使用的模式是一个预定义的 256 对像素点(即 512 个像素的位置)bit_pattern_31_
,这些像素对的位置被提前计算好并存储在一个静态整型数组bit_pattern_31_中
。每一对像素的比较结果都会生成一个二进制位(0 或 1),256 对像素的比较结果组合成一个 256 位的描述符。
3.3.1 bit_pattern_31_
static int bit_pattern_31_[256*4] =
{8,-3, 9,5/*mean (0), correlation (0)*/,4,2, 7,-12/*mean (1.12461e-05), correlation (0.0437584)*/.......
}
每组数据由四个整数组成: (x1, y1, x2, y2)。其中 (x1, y1) 和 (x2, y2) 表示一个坐标对。这些坐标是从图像的一个特征点附近采样的。这些点的组合基于预先训练和优化,以确保特征描述子在图像旋转和其他几何变换下具有鲁棒性。代码中的注释部分(如 /*mean (0), correlation (0)*/)表示从统计数据中得到的这对坐标的均值和相关性。这些信息在设计 ORB 描述子时是通过大量训练数据得出的。
3.4计算不同的v值对应的u的最大值的容器umax
3.4.1示意图
3.4.2代码实现
//This is for orientation// pre-compute the end of a row in a circular patch//umax存储不同v位置对应的u的最大值umax.resize(HALF_PATCH_SIZE + 1);//const int HALF_PATCH_SIZE = 15;int v, v0, vmax = cvFloor(HALF_PATCH_SIZE * sqrt(2.f) / 2 + 1);//计算结果为11。int vmin = cvCeil(HALF_PATCH_SIZE * sqrt(2.f) / 2);//计算结果为11。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 symmetricfor (v = HALF_PATCH_SIZE, v0 = 0; v >= vmin; --v){while (umax[v0] == umax[v0 + 1])++v0;umax[v] = v0;++v0;}
3.4.3循环思路:
外层循环从v=0开始,一直到v=11(即 vmax
),umax依次是:15 15 15 15 14 14 14 13 13 12 11 10
为确保对称性:内层循环作对称性调整从 v = 15
开始,向下遍历:
umax[0]=umax[1]=15 v0=1
umax[1]=umax[2]=15 v0=2
umax[2]=umax[3]=15 v0=3
umax[3] =15不等于umax[4]=14 此时v0不自增还是=3 umax[15]=3.
根据此思路依次递推,直到 v = vmin = 11
,umax依次是:3 6 8 9 10
最后的结果为16个数。
3.4.4可视化代码显示大致是这样的:
v = 0 | *********************** (umax = 15)
v = 1 | *********************** (umax = 15)
v = 2 | *********************** (umax = 15)
v = 3 | *********************** (umax = 15)
v = 4 | ********************* (umax = 14)
v = 5 | ********************* (umax = 14)
v = 6 | ********************* (umax = 14)
v = 7 | ******************* (umax = 13)
v = 8 | ******************* (umax = 13)
v = 9 | *************** (umax = 12)
v = 10 | ************* (umax = 11)
v = 11 | *********** (umax = 10)
v = 12 | ********* (umax = 9)
v = 13 | ******* (umax = 8)
v = 14 | ****** (umax = 6)
v = 15 | *** (umax = 3)
结束语
以上就是我学习到的内容,如果对您有帮助请多多支持我,如果哪里有问题欢迎大家在评论区积极讨论,我看到会及时回复。