美文网首页
通俗易懂理解ORBSLAM2初始化模块

通俗易懂理解ORBSLAM2初始化模块

作者: Optimization | 来源:发表于2020-11-01 17:48 被阅读0次

一、通俗易懂理解单目初始化快速特征匹配方法

1.参考资料:

[1] ORBSLAM2 source code

2.主要函数:
void Tracking::Track()
void Tracking::MonocularInitialization()

初始化特征匹配

int ORBmatcher::SearchForInitialization(Frame &F1, Frame &F2, vector<cv::Point2f> &vbPrevMatched, vector<int> &vnMatches12, int windowSize)

找候选匹配点函数,为什么要用网格呢,如果一个网格内没有特征点,直接跳过去,加速搜索。

vector<size_t> Frame::GetFeaturesInArea(const float &x, const float  &y, const float  &r, const int minLevel, const int maxLevel) const;
=========================================================================
题目:单目初始化的两帧是怎样的?
参考:
思路:
1.思路:

连续2帧特征点的数量都大于100才进行初始化。

2.图解(请用纸):
3.公式推导(请用纸):
要点程序:
Tracking::MonocularInitialization()
其他:

二、通俗易懂理解单目初始化中特征点匹配筛查原理和BUG解析

1.参考资料:

[1] ORBSLAM2 source code

2.主要函数:

找最优和次优的逻辑:

            if(vMatchedDistance[i2]<=dist)
                continue;
            // 如果当前匹配距离更小,更新最佳次佳距离
            if(dist<bestDist)
            {
                bestDist2=bestDist;
                bestDist=dist;
                bestIdx2=i2;
            }
            else if(dist<bestDist2)
            {
                bestDist2=dist;
            }

这个BUG导致删除误差的能力下降了




筛选出在旋转角度差落在在直方图区间内数量最多的前三个bin的索引

void ORBmatcher::ComputeThreeMaxima(vector<int>* histo, const int L, int &ind1, int &ind2, int &ind3)
3.问题
1)旋转直方图中的那个BUG到底影响什么?为什么会影响呢?

阈值变大了,单个bin本来是12度,现在变为30度,比如说大部分都是12度以内,但是有的是20度,应该把他们删除,但是如果单个bin是30度的话,那就无法删除,这样弱化了对角度差过大的匹配的删除。

2)如果之前匹配了,那么把之前的删除,就可以信当前的那个匹配吗?

暴力匹配,那么重复怎么办?把之前的匹配不要了,当前的匹配放进去

3)描述子和关键点是根据什么进行关联的呢?

特征点的在vector中的索引值就是描述子的行号

三、通俗易懂理解单目初始化单应矩阵归一化及DLT计算原理

1.参考资料:

[1] ORBSLAM2 source code
[2] 复习SVD分解
[3]解Ax=b

2.主要函数:
bool Initializer::Initialize(const Frame &CurrentFrame, const vector<int> &vMatches12, cv::Mat &R21, cv::Mat &t21,
                             vector<cv::Point3f> &vP3D, vector<bool> &vbTriangulated)
void Initializer::FindHomography(vector<bool> &vbMatchesInliers, float &score, cv::Mat &H21)

为什么要对数据进行归一化?
对于不是很好的条件问题,数据归一化很重要,比如基础矩阵F直接线性变换DLT的计算方法。(如下说明)




归一化操作、归一化函数
一阶距、一阶绝对距

void Initializer::Normalize(const vector<cv::KeyPoint> &vKeys, vector<cv::Point2f> &vNormalizedPoints, cv::Mat &T)

这个式子自己展开下,就是和上面计算的过程一模一样。

cv::Mat Initializer::ComputeH21(
    const vector<cv::Point2f> &vP1, //归一化后的点, in reference frame
    const vector<cv::Point2f> &vP2) //归一化后的点, in current frame
    cv::SVDecomp(A,                         //输入,待进行奇异值分解的矩阵
                 w,                         //输出,奇异值矩阵
                 u,                         //输出,矩阵U
                 vt,                        //输出,矩阵V^T
                 cv::SVD::MODIFY_A |        //输入,MODIFY_A是指允许计算函数可以修改待分解的矩阵,官方文档上说这样可以加快计算速度、节省内存
                     cv::SVD::FULL_UV);     //FULL_UV=把U和VT补充成单位正交方阵

  // 返回最小奇异值所对应的右奇异向量
  // 注意前面说的是右奇异值矩阵的最后一列,但是在这里因为是vt,转置后了,所以是行;由于A有9列数据,故最后一列的下标为8
  return vt.row(8).reshape(0,  //转换后的通道数,这里设置为0表示是与前面相同
                           3);  //转换后的行数,对应V的最后一列

就是V^T的最后一行(也就是第9个奇异向量)变成3X3求解的H矩阵。
U是左奇异向量,是一个8X8的矩阵
V^T是右奇异向量,是一个9X9的矩阵
w是奇异值矩阵,奇异值在对角线上,是一个8X9对角矩阵




奇异值是按照从大到小的顺序排的

=========================================================================
题目:为什么要对图像坐标进行归一化,如何进行归一化,代码和公式各是什么。
参考:
思路:
1.思路:

为什么要对图像坐标进行归一化:能够提高运算结果的精度。

2.图解(请用纸):
3.公式推导(请用纸):

x^{'} = (x - meanX)/ sX
y^{'} = (y - meanY)/ sY
meanX:均值
sX偏离程度的均值

要点程序:

// 归一化

  // Step 4 计算归一化矩阵:其实就是前面做的操作用矩阵变换来表示而已
  // |sX  0  -meanx*sX|
  // |0   sY -meany*sY|
  // |0   0      1    |
  T = cv::Mat::eye(3, 3, CV_32F);
  T.at<float>(0, 0) = sX;
  T.at<float>(1, 1) = sY;
  T.at<float>(0, 2) = -meanX * sX;
  T.at<float>(1, 2) = -meanY * sY;

// 去归一化

  cv::Mat T1, T2;
  Normalize(mvKeys1, vPn1, T1);
  Normalize(mvKeys2, vPn2, T2);
  // !
  // 注意这里取的是归一化矩阵T2的转置,因为基础矩阵的定义和单应矩阵不同,两者去归一化的计算也不相同
  cv::Mat T2t = T2.t();
F21i = T2t * Fn * T1;
其他:

// RANSAC 随机采样

  // Step 2
  // 在所有匹配特征点对中随机选择8对匹配特征点为一组,用于估计H矩阵和F矩阵
  // 共选择 mMaxIterations (默认200) 组
  // mvSets用于保存每次迭代时所使用的向量
  mvSets = vector<vector<size_t> >(
      mMaxIterations,  //最大的RANSAC迭代次数
      vector<size_t>(
          8,
          0));  //这个则是第二维元素的初始值,也就是第一维。这里其实也是一个第一维的构造函数,第一维vector有8项,每项的初始值为0.

  //用于进行随机数据样本采样,设置随机数种子
  DUtils::Random::SeedRandOnce(0);

  //开始每一次的迭代
  for (int it = 0; it < mMaxIterations; it++) {
    //迭代开始的时候,所有的点都是可用的
    vAvailableIndices = vAllIndices;

    // Select a minimum set
    //选择最小的数据样本集,使用八点法求,所以这里就循环了八次
    for (size_t j = 0; j < 8; j++) {
      // 随机产生一对点的id,范围从0到N-1
      int randi = DUtils::Random::RandomInt(0, vAvailableIndices.size() - 1);
      // idx表示哪一个索引对应的特征点对被选中
      int idx = vAvailableIndices[randi];

      //将本次迭代这个选中的第j个特征点对的索引添加到mvSets中
      mvSets[it][j] = idx;

      // 由于这对点在本次迭代中已经被使用了,所以我们为了避免再次抽到这个点,就在"点的可选列表"中,
      // 将这个点原来所在的位置用vector最后一个元素的信息覆盖,并且删除尾部的元素
      // 这样就相当于将这个点的信息从"点的可用列表"中直接删除了
      vAvailableIndices[randi] = vAvailableIndices.back();
      vAvailableIndices.pop_back();
    }  //依次提取出8个特征点对
  }  //迭代mMaxIterations次,选取各自迭代时需要用到的最小数据集
=========================================================================
题目:SVD分解中为什么V^T第9个奇异向量就是最优解
参考:
思路:
1.思路:
2.图解(请用纸):
3.公式推导(请用纸):
要点程序:
其他TODO:

我们知道他的奇异值的维度是9(因为是要求9个未知量),但是为什么是第9个呢,咋们怎么知道他的奇异值个数呢。正交矩阵是方阵,所以是第九个。
为什么ATA的特征向量就是VT的特征向量?这个怎么看的?有什么数学推理吗?还有想问下这个回答好像没有回答为什么V的第9个奇异向量是最优解这个问答?


四、通俗易懂理解单目初始化根据得分找到最佳单应矩阵

1.参考资料:

[1] ORBSLAM2 source code

2.主要函数:
       // 单应矩阵原理:X2=H21*X1,其中X1,X2 为归一化后的特征点    
        // 特征点归一化:vPn1 = T1 * mvKeys1, vPn2 = T2 * mvKeys2  得到:T2 * mvKeys2 =  Hn * T1 * mvKeys1   
        // 进一步得到:mvKeys2  = T2.inv * Hn * T1 * mvKeys1
        H21i = T2inv*Hn*T1;
        //然后计算逆
        H12i = H21i.inv();
float Initializer::CheckHomography(
    const cv::Mat &H21,                 //从参考帧到当前帧的单应矩阵
    const cv::Mat &H12,                 //从当前帧到参考帧的单应矩阵
    vector<bool> &vbMatchesInliers,     //匹配好的特征点对的Inliers标记
    float sigma)                        //估计误差
void Initializer::FindFundamental(vector<bool> &vbMatchesInliers, float &score, cv::Mat &F21)
cv::Mat Initializer::ComputeF21(
    const vector<cv::Point2f> &vP1, //归一化后的点, in reference frame
    const vector<cv::Point2f> &vP2) //归一化后的点, in current frame

// 基础矩阵约束:p2^t*F21*p1 = 0,其中p1,p2 为齐次化特征点坐标    
        // 特征点归一化:vPn1 = T1 * mvKeys1, vPn2 = T2 * mvKeys2  
        // 根据基础矩阵约束得到:(T2 * mvKeys2)^t* Hn * T1 * mvKeys1 = 0   
        // 进一步得到:mvKeys2^t * T2^t * Hn * T1 * mvKeys1 = 0
        F21i = T2t*Fn*T1;
float Initializer::CheckFundamental(
    const cv::Mat &F21,             //当前帧和参考帧之间的基础矩阵
    vector<bool> &vbMatchesInliers, //匹配的特征点对属于inliers的标记
    float sigma)                    //方差

极线


=========================================================================
题目:如何找到最佳单应矩阵。如何评判是最佳单应矩阵呢?具体怎么做呢?
参考:
 Initializer::CheckFundamental
思路:
1.思路:

对每一对点正向反向进行计算点到线的距离,误差在允许范围内就累加得分。误差越大,得分越低。然后选择一个最高得分的单应矩阵。

2.图解(请用纸):
3.公式推导(请用纸):
要点程序:
其他:

五、通俗易懂理解卡方检验介绍及在源码中的应用

1.参考资料:

[1] ORBSLAM2 source code
[2] [ORB-SLAM2]卡方分布(Chi-squared)外点(outlier)剔除(说的太好了)

2.主要函数:
float Initializer::CheckHomography(
    const cv::Mat &H21,                 //从参考帧到当前帧的单应矩阵
    const cv::Mat &H12,                 //从当前帧到参考帧的单应矩阵
    vector<bool> &vbMatchesInliers,     //匹配好的特征点对的Inliers标记
    float sigma)                        //估计误差
    // 基于卡方检验计算出的阈值(假设测量有一个像素的偏差)
    // 自由度为2的卡方分布,显著性水平为0.05,对应的临界阈值
    const float th = 5.991;
    ...
        if(chiSquare1>th)
            bIn = false;    
        else
            // 误差越大,得分越低
            score += th - chiSquare1;
float Initializer::CheckFundamental(
    const cv::Mat &F21,             //当前帧和参考帧之间的基础矩阵
    vector<bool> &vbMatchesInliers, //匹配的特征点对属于inliers的标记
    float sigma) 
    // 基于卡方检验计算出的阈值
    // 自由度为1的卡方分布,显著性水平为0.05,对应的临界阈值
    const float th = 3.841;

    // 自由度为2的卡方分布,显著性水平为0.05,对应的临界阈值
    const float thScore = 5.991;
    ...
        // Step 2.4 误差大于阈值就说明这个点是Outlier 
        // ? 为什么判断阈值用的 th(1自由度),计算得分用的thScore(2自由度)
        // ? 可能是为了和CheckHomography 得分统一?
        if(chiSquare1>th)
            bIn = false;
        else
            // 误差越大,得分越低
            score += thScore - chiSquare1;

LocalMapping.cpp

            if(!bStereo2)
            {
                float u2 = fx2*x2*invz2+cx2;
                float v2 = fy2*y2*invz2+cy2;
                float errX2 = u2 - kp2.pt.x;
                float errY2 = v2 - kp2.pt.y;
                if((errX2*errX2+errY2*errY2)>5.991*sigmaSquare2)
                    continue;
            }

3.问题
1)利用协方差对误差归一化是什么意思?

不同的金字塔层级,重投影误差都用同一个相同的阈值不合理,因为每层金字塔,他的不确定是不一样的,用协方差对误差进行归一化,也就是去量纲了。接下来就是阈值的设定。

2)什么是卡方分布,为什么要使用卡方分布,如何使用卡方分布,大于卡方分布值是什么意思呢?

在概率论和数理统计中,k个自由度的卡方分布为k个互相独立服从标准正态的随机变量的平方和。卡方分布的自由度即为向量的维度。


3)计算F矩阵的时候,为什么卡方分布的自由度为1

因为是计算的是点到线的距离

4)AX=0的求解是怎么进行的,如何解释SVD分解的结果中包含求解的结果呢?

5)AX=b呢?问题同上。

6)为什么F矩阵的秩为2,那么应该注意什么?

这是基础矩阵的重要性质。因为F求不出尺度,尺度等价性。

7)什么对角阵、正定阵、正交阵是什么意思呢?

正交阵<==>A的转置等于A的逆。
正定阵:x^Tx > 0,x是一个非0向量
对角矩阵:(diagonal matrix)是一个主对角线之外的元素皆为0的矩阵,常写为diag(a1,a2,...,an) 。

=========================================================================
题目:卡方检验在源码中是怎么应用的?卡方分布大概是什么意思。计算F矩阵的时候,为什么卡方分布的自由度为1。计算H矩阵的时候是多少呢?
参考:
思路:
1.思路:

自由度为2,服从高斯分布,出现的概率超过95%时对应的卡方分布的值,如果小于这个值的话,那就是内点。
H的话是重投影,自由度是2。
F的话是计算点到极线的距离,自由度是1,维度降低了。

2.图解(请用纸):
3.公式推导(请用纸):
要点程序:

其他:
=========================================================================
题目:利用协方差对误差归一化是什么意思?
参考:
思路:
1.思路:

不同的金字塔层级,重投影误差都用同一个相同的阈值不合理,因为每层金字塔,他的不确定是不一样的,用协方差对误差进行归一化,也就是去量纲了。接下来就是阈值的设定。使用卡方分布进行设定。

2.图解(请用纸):
3.公式推导(请用纸):
要点程序:

其他:
=========================================================================
题目:AX=0求解。AX=b求解在代码中有什么应用呢?
参考:
思路:
1.思路:

[!问题已提出][w1]11.通俗易懂理解单目初始化单应矩阵归一化及DLT计算原理

2.图解(请用纸):
3.公式推导(请用纸):
要点程序:

其他:

六、通俗易懂理解从单应矩阵恢复位姿及三角化

1.参考资料:

[1] ORBSLAM2 source code

2.主要函数:
bool Initializer::ReconstructH(
    vector<bool> &vbMatchesInliers,  //匹配点对的内点标记
    cv::Mat &H21,                    //从参考帧到当前帧的单应矩阵
    cv::Mat &K,                      //相机的内参数矩阵
    cv::Mat &R21,                    //计算出来的相机旋转
    cv::Mat &t21,                    //计算出来的相机平移
    vector<cv::Point3f>
        &vP3D,  //世界坐标系下,三角化测量特征点对之后得到的特征点的空间坐标
    vector<bool> &vbTriangulated,  //特征点对被三角化测量的标记
    float minParallax,  //在进行三角化测量时,观测正常所允许的最小视差角
    int minTriangulated)  //最少被三角化的点对数(其实也是点个数)

通过H恢复R,t需要看吗?作者说不用看。不是重点。
CheckRT很关键的一个函数(很值得学习)

int Initializer::CheckRT(const cv::Mat &R, const cv::Mat &t,
                         const vector<cv::KeyPoint> &vKeys1,
                         const vector<cv::KeyPoint> &vKeys2,
                         const vector<Match> &vMatches12,
                         vector<bool> &vbMatchesInliers, const cv::Mat &K,
                         vector<cv::Point3f> &vP3D, float th2,
                         vector<bool> &vbGood, float &parallax) 
// 第二个相机的光心在世界坐标系下的坐标
  cv::Mat O2 = -R.t() * t;
//给定投影矩阵P1,P2和图像上的点kp1,kp2,从而恢复3D坐标 (三角化)
void Initializer::Triangulate(
    const cv::KeyPoint &kp1,  //特征点, in reference frame
    const cv::KeyPoint &kp2,  //特征点, in current frame
    const cv::Mat &P1,        //投影矩阵P1
    const cv::Mat &P2,        //投影矩阵P2
    cv::Mat &x3D)             //三维点

三角化的分析看 第6讲 视觉前端(切题 Done)这个理解思路会更好。




  //这个就是上面注释中的矩阵A
  cv::Mat A(4, 4, CV_32F);

  //构造参数矩阵A
  A.row(0) = kp1.pt.x * P1.row(2) - P1.row(0);
  A.row(1) = kp1.pt.y * P1.row(2) - P1.row(1);
  A.row(2) = kp2.pt.x * P2.row(2) - P2.row(0);
  A.row(3) = kp2.pt.y * P2.row(2) - P2.row(1);

  //奇异值分解的结果
  cv::Mat u, w, vt;
  //对系数矩阵A进行奇异值分解
  cv::SVD::compute(A, w, u, vt, cv::SVD::MODIFY_A | cv::SVD::FULL_UV);
  //根据前面的结论,奇异值分解右矩阵的最后一行其实就是解,原理类似于前面的求最小二乘解,四个未知数四个方程正好正定
  //别忘了我们更习惯用列向量来表示一个点的空间坐标
  x3D = vt.row(3).t();
  //为了符合其次坐标的形式,使最后一维为1
  x3D = x3D.rowRange(0, 3) / x3D.at<float>(3);

取的是前4行的值,用第4个元素进行归一化 或者说用取的是前3行的值,用第3个元素进行归一化,有点奇怪?
VINS也是这样的,没问题。就是把第四维弄成1。

=========================================================================
题目:如何从单应矩阵中选择最佳的位姿。
参考:
思路:
1.思路:

(1)三角化的特征点的x,y,z坐标是否无穷大
(2)夹角小于0.36度认为不好
(3)2帧中重投影误差是否太大。
(4)看下good点的数量。找good点最多的,并且good点要足够突出(second good < 0.75* good)等

2.图解(请用纸):
3.公式推导(请用纸):
要点程序:
int Initializer::CheckRT(const cv::Mat &R, const cv::Mat &t,
                         const vector<cv::KeyPoint> &vKeys1,
                         const vector<cv::KeyPoint> &vKeys2,
                         const vector<Match> &vMatches12,
                         vector<bool> &vbMatchesInliers, const cv::Mat &K,
                         vector<cv::Point3f> &vP3D, float th2,
                         vector<bool> &vbGood, float &parallax) 
其他:
=========================================================================
题目:三角化理论请写出来。
参考:三角化的分析看 第6讲 视觉前端(切题 Done)这个理解思路会更好。
思路:
1.思路:
2.图解(请用纸):
3.公式推导(请用纸):

\lambda_k x_k = P_ky,\lambda_k为深度值,x_k为相机归一化坐标,P_k是投影矩阵T^c_wy是点在世界坐标系下的坐标.
取第三行, \lambda_k = P_{k3}y, 所以u_k P_{k3}y - P_{k1}y = 0 v_k P_{k3}y - P_{k2}y = 0
\begin{pmatrix} u_kP_{k3} - P_{k1} \\ v_kP_{k3} - P_{k2} \\ ...\end{pmatrix}y = 0

要点程序:

其他:

七.通俗易懂理解ORBSLAM2从基础矩阵得到最佳位姿和三维点

1.参考资料:

[1] ORBSLAM2 source code

2.主要函数:
bool Initializer::ReconstructF(
    vector<bool> &vbMatchesInliers,  //匹配好的特征点对的Inliers标记
    cv::Mat &F21,                    //从参考帧到当前帧的基础矩阵
    cv::Mat &K,                      //相机的内参数矩阵
    cv::Mat &R21,  //计算好的相机从参考帧到当前帧的旋转
    cv::Mat &t21,  //计算好的相机从参考帧到当前帧的平移
    vector<cv::Point3f> &vP3D,  //三角化测量之后的特征点的空间坐标
    vector<bool> &vbTriangulated,  //某个特征点是否被三角化了的标记
    float minParallax,  //认为三角化测量有效的最小视差角
    int minTriangulated)  //认为使用三角化测量进行数据判断的最小测量点数量
    if (mpInitializer->Initialize(
            mCurrentFrame,  //当前帧
            mvIniMatches,   //当前帧和参考帧的特征点的匹配关系
            Rcw, tcw,       //初始化得到的相机的位姿
            mvIniP3D,       //进行三角化得到的空间点集合
            vbTriangulated))  //以及对应于mvIniMatches来讲,其中哪些点被三角化了
=========================================================================
题目:F矩阵分解得到4组解,如何选择最好的呢?H矩阵分解是几组解呢?
参考:
思路:
1.思路:

F矩阵分解得到4组解,如何选择最好的呢?看每一种有效3D点的个数。看哪一种大于阈值0.7 * maxGood并且只有他一个。然后看下视差是否大于最小视差来判断最佳位姿。
H矩阵分解是几组解呢?8组解。看下good点的数量。找good点最多的,并且good点要足够突出(second good < 0.75* good)等

2.图解(请用纸):
3.公式推导(请用纸):
要点程序:

其他:

八.通俗易懂理解初始化3维点来构造初始化地图

1.参考资料:

[1] ORBSLAM2 source code

2.主要函数:
void Tracking::CreateInitialMapMonocular()
KeyFrame::KeyFrame(Frame &F, Map *pMap, KeyFrameDatabase *pKFDB)
void KeyFrame::ComputeBoW()
void MapPoint::ComputeDistinctiveDescriptors()
void MapPoint::UpdateNormalAndDepth()

九.通俗易懂理解初始化关键帧更新共视关系,尺度归一化

1.参考资料:

[1] ORBSLAM2 source code
[2] 通俗易懂理解KeyFrame类问题 要点(切题 Done)

2.主要函数:
void Tracking::CreateInitialMapMonocular()
  pKFini->UpdateConnections();
  pKFcur->UpdateConnections();
void KeyFrame::UpdateConnections()
void KeyFrame::AddConnection(KeyFrame *pKF, const int &weight)
void KeyFrame::UpdateBestCovisibles() 
  float medianDepth = pKFini->ComputeSceneMedianDepth(2);
float KeyFrame::ComputeSceneMedianDepth(const int q)
=========================================================================
题目:初始化场景的中值深度(地图点在某帧相机坐标系下深度的中间值)后,如何进行尺度归一化。
参考:
思路:
1.思路:

对当前帧和所有地图点进行尺度归一化。

2.图解(请用纸):
3.公式推导(请用纸):
要点程序:
  float medianDepth = pKFini->ComputeSceneMedianDepth(2);
  float invMedianDepth = 1.0f / medianDepth;

  //两个条件,一个是平均深度要大于0,另外一个是在当前帧中被观测到的地图点的数目应该大于100
  if (medianDepth < 0 || pKFcur->TrackedMapPoints(1) < 100) {
    cout << "Wrong initialization, reseting..." << endl;
    Reset();
    return;
  }

  //将两帧之间的变换归一化到平均深度1的尺度下
  // Scale initial baseline
  cv::Mat Tc2w = pKFcur->GetPose();
  // x/z y/z 将z归一化到1
  Tc2w.col(3).rowRange(0, 3) = Tc2w.col(3).rowRange(0, 3) * invMedianDepth;
  pKFcur->SetPose(Tc2w);

  // Scale points
  // 把3D点的尺度也归一化到1
  //? 为什么是pKFini? 是不是就算是使用 pKFcur 得到的结果也是相同的?
  vector<MapPoint*> vpAllMapPoints = pKFini->GetMapPointMatches();
  for (size_t iMP = 0; iMP < vpAllMapPoints.size(); iMP++) {
    if (vpAllMapPoints[iMP]) {
      MapPoint* pMP = vpAllMapPoints[iMP];
      pMP->SetWorldPos(pMP->GetWorldPos() * invMedianDepth);
    }
  }

其他:

相关文章

网友评论

      本文标题:通俗易懂理解ORBSLAM2初始化模块

      本文链接:https://www.haomeiwen.com/subject/dsjwlktx.html