LoopClosing是专门负责做闭环的类,它的主要功能就是检测闭环,计算闭环帧的相对位姿病以此做闭环修正。

- std::list
mlpLoopKeyFrameQueue; // 关键帧队列 -
- std::mutex mMutexLoopQueue;
-
- // Loop detector parameters
- float mnCovisibilityConsistencyTh;
-
- // Loop detector variables
- KeyFrame* mpCurrentKF;
- KeyFrame* mpMatchedKF;
- std::vector
mvConsistentGroups; - std::vector
mvpEnoughConsistentCandidates; - std::vector
mvpCurrentConnectedKFs; - std::vector
mvpCurrentMatchedPoints; - std::vector
mvpLoopMapPoints; - cv::Mat mScw;
- g2o::Sim3 mg2oScw;
- void LoopClosing::Run()
- {
- mbFinished =false;
-
- while(1)
- {
- // Check if there are keyframes in the queue
- if(CheckNewKeyFrames())
- {
- // Detect loop candidates and check covisibility consistency
- if(DetectLoop())
- {
- // Compute similarity transformation [sR|t]
- // In the stereo/RGBD case s=1
- // 单目情况因为尺度是不可观的,所以是sim3,双目、RGBD、有IMU等有尺度的情况下,变为SE3
- if(ComputeSim3()) // 主要是判断该闭环是否可靠,只有在十分可靠的情况下才进行闭环计算
- {
- // Perform loop fusion and pose graph optimization
- CorrectLoop();
- }
- }
- }
-
- ResetIfRequested();
-
- if(CheckFinish())
- break;
-
- usleep(5000);
- }
-
- SetFinish();
- }
LocalMapping模块处理完关键帧之后把它插入进来
- void LoopClosing::InsertKeyFrame(KeyFrame *pKF)
- {
- unique_lock
lock(mMutexLoopQueue) ; - if(pKF->mnId!=0)
- mlpLoopKeyFrameQueue.push_back(pKF);
- }
它的主要流程包括:
1)如果地图中的关键帧数小于10,那么不进行闭环检测
2)获取共视关键帧,并计算他们和当前关键帧之间的BoW分数,求得最低分
3)通过上一步计算出的最低分数到数据库中查找出候选关键帧,这一步相当于是找到了曾经到过此处的关键帧们
4)对候选关键帧集进行连续性检测
- bool LoopClosing::DetectLoop()
- {
- {
- // 取出队首的关键帧,也就是最旧的关键帧
- unique_lock
lock(mMutexLoopQueue) ; - mpCurrentKF = mlpLoopKeyFrameQueue.front();
- mlpLoopKeyFrameQueue.pop_front();
- // Avoid that a keyframe can be erased while it is being process by this thread
- mpCurrentKF->SetNotErase();
- }
-
- //If the map contains less than 10 KF or less than 10 KF have passed from last loop detection
- // 如果距离上次闭环小于10帧,则不进行闭环检测
- if(mpCurrentKF->mnId
10) - {
- mpKeyFrameDB->add(mpCurrentKF);
- mpCurrentKF->SetErase();
- return false;
- }
-
- // Compute reference BoW similarity score
- // This is the lowest score to a connected keyframe in the covisibility graph
- // We will impose loop candidates to have a higher similarity than this
- //计算当前帧及其共视关键帧的词袋模型匹配得分,获得最小得分minScore
- const vector
vpConnectedKeyFrames = mpCurrentKF->GetVectorCovisibleKeyFrames(); - const DBoW2::BowVector &CurrentBowVec = mpCurrentKF->mBowVec;
- float minScore = 1;
- for(size_t i=0; i
size(); i++) - {
- KeyFrame* pKF = vpConnectedKeyFrames[i];
- if(pKF->isBad())
- continue;
- const DBoW2::BowVector &BowVec = pKF->mBowVec;
-
- float score = mpORBVocabulary->score(CurrentBowVec, BowVec);
-
- if(score
- minScore = score;
- }
-
- // Query the database imposing the minimum score
- //在除去当前帧共视关系的关键帧数据中,检测闭环候选帧(这个函数在KeyFrameDatabase中)
- //闭环候选帧删选过程:
- //1,BoW得分>minScore;
- //2,统计满足1的关键帧中有共同单子最多的单词数maxcommonwords
- //3,筛选出共同单词数大于mincommons(=0.8*maxcommons)的关键帧
- //4,相连的关键帧分为一组,计算组得分(总分),得到最大总分bestAccScore,筛选出总分大于minScoreToRetain(=0.75*bestAccScore)的组
- //用得分最高的候选帧IAccScoreAndMathch代表该组,计算组得分的目的是剔除单独一帧得分较高,但是没有共视关键帧作为闭环来说不够鲁棒
- //对于通过了闭环检测的关键帧,还需要通过连续性检测(连续三帧都通过上面的筛选),才能作为闭环候选帧
- vector
vpCandidateKFs = mpKeyFrameDB->DetectLoopCandidates(mpCurrentKF, minScore); -
- // If there are no loop candidates, just add new keyframe and return false
- if(vpCandidateKFs.empty())
- {
- mpKeyFrameDB->add(mpCurrentKF);
- mvConsistentGroups.clear();
- mpCurrentKF->SetErase();
- return false;
- }
-
- // For each loop candidate check consistency with previous loop candidates
- // Each candidate expands a covisibility group (keyframes connected to the loop candidate in the covisibility graph)
- // A group is consistent with a previous group if they share at least a keyframe
- // We must detect a consistent loop in several consecutive keyframes to accept it
- // 在候选帧中检测具有连续性的候选帧
- // 每个候选帧将与自己相连的关键帧构成一个“候选组spCandidateGroup”
- // 检测“候选组”中每一个关键帧是否存在于“连续组”,如果存在nCurrentConsistency++,将该“候选组”放入“当前连续组vCurrentConsistentGroups”
- // 如果nCurrentConsistency大于等于3,那么该”子候选组“代表的候选帧过关,进入mvpEnoughConsistentCandidates
- mvpEnoughConsistentCandidates.clear();
-
- vector
vCurrentConsistentGroups; - vector<bool> vbConsistentGroup(mvConsistentGroups.size(),false);
- for(size_t i=0, iend=vpCandidateKFs.size(); i
- {
- KeyFrame* pCandidateKF = vpCandidateKFs[i];
-
- // 将自己以及与自己相连的关键帧构成一个“候选组”
- set
spCandidateGroup = pCandidateKF->GetConnectedKeyFrames(); - spCandidateGroup.insert(pCandidateKF);
-
- bool bEnoughConsistent = false;
- bool bConsistentForSomeGroup = false;
- // 遍历之前的“连续组”
- for(size_t iG=0, iendG=mvConsistentGroups.size(); iG
- {
- // 取出一个之前的连续组
- set
sPreviousGroup = mvConsistentGroups[iG].first; -
- // 遍历每个“候选组”,检测候选组中每一个关键帧在“连续组”中是否存在
- bool bConsistent = false;
- for(set
::iterator sit=spCandidateGroup.begin(), send=spCandidateGroup.end(); sit!=send;sit++) - {
- if(sPreviousGroup.count(*sit))
- {
- bConsistent=true;
- bConsistentForSomeGroup=true;// 该“候选组”至少与一个”连续组“相连
- break;
- }
- }
-
- if(bConsistent)
- {
- int nPreviousConsistency = mvConsistentGroups[iG].second;
- int nCurrentConsistency = nPreviousConsistency + 1;
- if(!vbConsistentGroup[iG])
- {
- ConsistentGroup cg = make_pair(spCandidateGroup,nCurrentConsistency);
- vCurrentConsistentGroups.push_back(cg);
- vbConsistentGroup[iG]=true; //this avoid to include the same group more than once
- }
- if(nCurrentConsistency>=mnCovisibilityConsistencyTh && !bEnoughConsistent)
- {
- mvpEnoughConsistentCandidates.push_back(pCandidateKF);
- bEnoughConsistent=true; //this avoid to insert the same candidate more than once
- }
- }
- }
-
- // If the group is not consistent with any previous group insert with consistency counter set to zero
- // 如果该“候选组”的所有关键帧都不存在于“连续组”,那么vCurrentConsistentGroups将为空,
- // 于是就把“子候选组”全部拷贝到vCurrentConsistentGroups,并最终用于更新mvConsistentGroups,计数设为0,重新开始
- if(!bConsistentForSomeGroup)
- {
- ConsistentGroup cg = make_pair(spCandidateGroup,0);
- vCurrentConsistentGroups.push_back(cg);
- }
- }
-
- // Update Covisibility Consistent Groups
- mvConsistentGroups = vCurrentConsistentGroups;
-
-
- // Add Current Keyframe to database
- mpKeyFrameDB->add(mpCurrentKF);
-
- if(mvpEnoughConsistentCandidates.empty())
- {
- mpCurrentKF->SetErase();
- return false;
- }
- else
- {
- return true;
- }
-
- mpCurrentKF->SetErase();
- return false;
- }
2. ComputeSim3:计算两帧之间的相对位姿
主要流程包括:
1)对每一个闭环帧,通过BoW的matcher方法进行第一次匹配,匹配闭环帧和当前关键帧之间的匹配关系,如果对应关系少于20个,则丢弃,否则构造一个Sim3求解器并保存起来。
2)对上一步得到的每一个满足条件的闭环帧,通过RANSAC迭代,求解Sim3。
3)通过返回的Sim3进行第二次匹配。
4)使用非线性最小二乘法优化Sim3.
5)使用非线性最小二乘法优化Sim3.
6)使用投影得到更多的匹配点,如果匹配点数量充足,则接受该闭环。
- bool LoopClosing::ComputeSim3()
- {
- // For each consistent loop candidate we try to compute a Sim3
-
- const int nInitialCandidates = mvpEnoughConsistentCandidates.size();
-
- // We compute first ORB matches for each candidate
- // If enough matches are found, we setup a Sim3Solver
- ORBmatcher matcher(0.75,true);
-
- vector
vpSim3Solvers; - vpSim3Solvers.resize(nInitialCandidates);
-
- vector
> vvpMapPointMatches; - vvpMapPointMatches.resize(nInitialCandidates);
-
- vector<bool> vbDiscarded;
- vbDiscarded.resize(nInitialCandidates);
-
- int nCandidates=0; //candidates with enough matches
-
- for(int i=0; i
- {
- // 闭环候选帧中取出一帧关键帧pKF
- KeyFrame* pKF = mvpEnoughConsistentCandidates[i];
-
- // avoid that local mapping erase it while it is being processed in this thread
- // 防止在LocalMapping中KeyFrameCulling函数将此关键帧作为冗余帧剔除
- pKF->SetNotErase();
-
- if(pKF->isBad())
- {
- vbDiscarded[i] = true;
- continue;
- }
-
- // 将当前帧mpCurrentKF与闭环候选关键帧pKF匹配
- // 通过bow加速得到mpCurrentKF与pKF之间的匹配特征点,vvpMapPointMatches是匹配特征点对应的MapPoints
- int nmatches = matcher.SearchByBoW(mpCurrentKF,pKF,vvpMapPointMatches[i]);
-
- // 匹配的特征点数太少,剔除
- if(nmatches<20)
- {
- vbDiscarded[i] = true;
- continue;
- }
- else
- {
- // 构造Sim3求解器
- // 如果mbFixScale为true,则是6DoFf优化,如果是false,则是7DoF优化(单目)
- Sim3Solver* pSolver = new Sim3Solver(mpCurrentKF,pKF,vvpMapPointMatches[i],mbFixScale);
- pSolver->SetRansacParameters(0.99,20,300);
- vpSim3Solvers[i] = pSolver;
- }
- // 参与Sim3计算的候选关键帧数加1
- nCandidates++;
- }
-
- bool bMatch = false;// 标记是否有一个候选帧通过Sim3的求解
-
- // Perform alternatively RANSAC iterations for each candidate
- // until one is succesful or all fail
- // 一直循环所有的候选帧,每个候选帧迭代5次,如果5次迭代后得不到结果,就换下一个候选帧
- // 直到有一个候选帧首次迭代成功,或者某个候选帧总的迭代次数超过限制,直接将它剔除
- while(nCandidates>0 && !bMatch)
- {
- for(int i=0; i
- {
- if(vbDiscarded[i])
- continue;
-
- KeyFrame* pKF = mvpEnoughConsistentCandidates[i];
-
- // Perform 5 Ransac Iterations
- vector<bool> vbInliers;
- int nInliers;
- bool bNoMore;
-
- // 对有较好的匹配的关键帧求取Sim3变换
- Sim3Solver* pSolver = vpSim3Solvers[i];
- cv::Mat Scm = pSolver->iterate(5,bNoMore,vbInliers,nInliers);
-
- // If Ransac reachs max. iterations discard keyframe
- // 总迭代次数达到最大限制还没有求出合格的Sim3变换,该候选帧剔除
- if(bNoMore)
- {
- vbDiscarded[i]=true;
- nCandidates--;
- }
-
- // If RANSAC returns a Sim3, perform a guided matching and optimize with all correspondences
- if(!Scm.empty())
- {
- vector
vpMapPointMatches(vvpMapPointMatches[i].size(), static_cast(NULL)) ; - for(size_t j=0, jend=vbInliers.size(); j
- {
- // 保存inlier的MapPoint
- if(vbInliers[j])
- vpMapPointMatches[j]=vvpMapPointMatches[i][j];
- }
- // 通过步骤3求取的Sim3变换引导关键帧匹配弥补步骤2中的漏匹配
- cv::Mat R = pSolver->GetEstimatedRotation();
- cv::Mat t = pSolver->GetEstimatedTranslation();
- const float s = pSolver->GetEstimatedScale();
- // 查找更多的匹配 使用SearchByBoW进行特征点匹配时会有漏匹配
- // 通过Sim3变换,确定pKF1的特征点在pKF2中的大致区域,同理,确定pKF2的特征点在pKF1中的大致区域
- // 在该区域内通过描述子进行匹配pKF1和pKF2之前漏匹配的特征点,更新匹配vpMapPointMatches
- matcher.SearchBySim3(mpCurrentKF,pKF,vpMapPointMatches,s,R,t,7.5);
-
- // Sim3优化,只要有一个候选帧通过Sim3的求解与优化,就跳出停止对其它候选帧的判断
- g2o::Sim3 gScm(Converter::toMatrix3d(R),Converter::toVector3d(t),s);
- // 优化mpCurrentKF与pKF对应的MapPoints间的Sim3,得到优化后的量gScm
- const int nInliers = Optimizer::OptimizeSim3(mpCurrentKF, pKF, vpMapPointMatches, gScm, 10, mbFixScale);
-
- // If optimization is succesful stop ransacs and continue
- if(nInliers>=20)
- {
- bMatch = true;
- // mpMatchedKF是最终闭环检测出来与当前帧形成闭环的关键帧
- mpMatchedKF = pKF;
- // 得到从世界坐标系到该候选帧的Sim3变换
- g2o::Sim3 gSmw(Converter::toMatrix3d(pKF->GetRotation()),Converter::toVector3d(pKF->GetTranslation()),1.0);
- // 得到优化后从世界坐标系到当前帧的Sim3变换
- mg2oScw = gScm*gSmw;
- mScw = Converter::toCvMat(mg2oScw);
-
- mvpCurrentMatchedPoints = vpMapPointMatches;
- break;//跳出对其它候选帧的判断
- }
- }
- }
- }
-
- // 没有一个闭环匹配候选帧通过Sim3的求解与优化
- if(!bMatch)
- {
- for(int i=0; i
- mvpEnoughConsistentCandidates[i]->SetErase();
- mpCurrentKF->SetErase();
- return false;
- }
-
- // Retrieve MapPoints seen in Loop Keyframe and neighbors
- // 取出闭环匹配上关键帧的相连关键帧,得到它们的MapPoints放入mvpLoopMapPoints
- // 将mpMatchedKF相连的关键帧全部取出来放入vpLoopConnectedKFs
- vector
vpLoopConnectedKFs = mpMatchedKF->GetVectorCovisibleKeyFrames(); - vpLoopConnectedKFs.push_back(mpMatchedKF);
- mvpLoopMapPoints.clear();
- for(vector
::iterator vit=vpLoopConnectedKFs.begin(); vit!=vpLoopConnectedKFs.end(); vit++) - {
- KeyFrame* pKF = *vit;
- vector
vpMapPoints = pKF->GetMapPointMatches(); - for(size_t i=0, iend=vpMapPoints.size(); i
- {
- MapPoint* pMP = vpMapPoints[i];
- if(pMP)
- {
- if(!pMP->isBad() && pMP->mnLoopPointForKF!=mpCurrentKF->mnId)
- {
- mvpLoopMapPoints.push_back(pMP);
- pMP->mnLoopPointForKF=mpCurrentKF->mnId;
- }
- }
- }
- }
-
- // Find more matches projecting with the computed Sim3
- // 将闭环匹配上关键帧以及相连关键帧的MapPoints投影到当前关键帧进行投影匹配
- // 根据投影查找更多的匹配
- // 根据Sim3变换,将每个mvpLoopMapPoints投影到mpCurrentKF上,并根据尺度确定一个搜索区域,
- // 根据该MapPoint的描述子与该区域内的特征点进行匹配,如果匹配误差小于TH_LOW即匹配成功,更新mvpCurrentMatchedPoints
- matcher.SearchByProjection(mpCurrentKF, mScw, mvpLoopMapPoints, mvpCurrentMatchedPoints,10);
-
- // If enough matches accept Loop
- // 断当前帧与检测出的所有闭环关键帧是否有足够多的MapPoints匹配
- int nTotalMatches = 0;
- for(size_t i=0; i
size(); i++) - {
- if(mvpCurrentMatchedPoints[i])
- nTotalMatches++;
- }
-
- if(nTotalMatches>=40)
- {
- for(int i=0; i
- if(mvpEnoughConsistentCandidates[i]!=mpMatchedKF)
- mvpEnoughConsistentCandidates[i]->SetErase();
- return true;
- }
- else
- {
- for(int i=0; i
- mvpEnoughConsistentCandidates[i]->SetErase();
- mpCurrentKF->SetErase();
- return false;
- }
-
- }
3. CorrectLoop:根据闭环做校正
主要流程包括:
1)如果有全局BA运算在运行的话,终止之前的BA运算。
2)使用传播法计算每一个关键帧正确的Sim3变换值
3)优化图
4)全局BA优化
- void LoopClosing::CorrectLoop()
- {
- cout << "Loop detected!" << endl;
-
- // Send a stop signal to Local Mapping
- // Avoid new keyframes are inserted while correcting the loop
- //请求局部地图停止
- mpLocalMapper->RequestStop();
-
- // If a Global Bundle Adjustment is running, abort it
- if(isRunningGBA())
- {
- unique_lock
lock(mMutexGBA) ; - mbStopGBA = true;
-
- mnFullBAIdx++;
-
- if(mpThreadGBA)
- {
- mpThreadGBA->detach();
- delete mpThreadGBA;
- }
- }
-
- // Wait until Local Mapping has effectively stopped
- while(!mpLocalMapper->isStopped())
- {
- usleep(1000);
- }
-
- // Ensure current keyframe is updated
- // 根据共视关系更新当前帧与其它关键帧之间的连接
- mpCurrentKF->UpdateConnections();
-
- // Retrive keyframes connected to the current keyframe and compute corrected Sim3 pose by propagation
- // 得到Sim3优化后,与当前帧相连的关键帧的位姿,以及它们的MapPoints
- // 通过相对位姿关系,可以确定这些相连的关键帧与世界坐标系之间的Sim3变换
- // 取出与当前帧相连的关键帧,包括当前关键帧
- mvpCurrentConnectedKFs = mpCurrentKF->GetVectorCovisibleKeyFrames();
- mvpCurrentConnectedKFs.push_back(mpCurrentKF);
-
- KeyFrameAndPose CorrectedSim3, NonCorrectedSim3;
- CorrectedSim3[mpCurrentKF]=mg2oScw;
- cv::Mat Twc = mpCurrentKF->GetPoseInverse();
-
-
- {
- // Get Map Mutex
- unique_lock
lock(mpMap->mMutexMapUpdate) ; -
- // 得到Sim3调整后其它与当前帧相连关键帧的位姿
- for(vector
::iterator vit=mvpCurrentConnectedKFs.begin(), vend=mvpCurrentConnectedKFs.end(); vit!=vend; vit++) - {
- KeyFrame* pKFi = *vit;
-
- cv::Mat Tiw = pKFi->GetPose();
-
- if(pKFi!=mpCurrentKF)
- {
- // 得到当前帧到pKFi帧的相对变换
- cv::Mat Tic = Tiw*Twc;
- cv::Mat Ric = Tic.rowRange(0,3).colRange(0,3);
- cv::Mat tic = Tic.rowRange(0,3).col(3);
- g2o::Sim3 g2oSic(Converter::toMatrix3d(Ric),Converter::toVector3d(tic),1.0);
- // 当前帧的位姿固定不动,其它的关键帧根据相对关系得到Sim3调整的位姿
- g2o::Sim3 g2oCorrectedSiw = g2oSic*mg2oScw;
- //Pose corrected with the Sim3 of the loop closure
- // 得到闭环g2o优化后各个关键帧的位姿
- CorrectedSim3[pKFi]=g2oCorrectedSiw;
- }
-
- cv::Mat Riw = Tiw.rowRange(0,3).colRange(0,3);
- cv::Mat tiw = Tiw.rowRange(0,3).col(3);
- g2o::Sim3 g2oSiw(Converter::toMatrix3d(Riw),Converter::toVector3d(tiw),1.0);
- //Pose without correction
- // 当前帧相连关键帧,没有进行闭环优化的位姿
- NonCorrectedSim3[pKFi]=g2oSiw;
- }
-
- // Correct all MapPoints obsrved by current keyframe and neighbors, so that they align with the other side of the loop
- // 上一步得到调整相连帧位姿后,修正这些关键帧的地图点
- for(KeyFrameAndPose::iterator mit=CorrectedSim3.begin(), mend=CorrectedSim3.end(); mit!=mend; mit++)
- {
- KeyFrame* pKFi = mit->first;
- g2o::Sim3 g2oCorrectedSiw = mit->second;
- g2o::Sim3 g2oCorrectedSwi = g2oCorrectedSiw.inverse();
-
- g2o::Sim3 g2oSiw =NonCorrectedSim3[pKFi];
-
- vector
vpMPsi = pKFi->GetMapPointMatches(); - for(size_t iMP=0, endMPi = vpMPsi.size(); iMP
- {
- MapPoint* pMPi = vpMPsi[iMP];
- if(!pMPi)
- continue;
- if(pMPi->isBad())
- continue;
- if(pMPi->mnCorrectedByKF==mpCurrentKF->mnId)
- continue;
-
- // Project with non-corrected pose and project back with corrected pose
- cv::Mat P3Dw = pMPi->GetWorldPos();
- Eigen::Matrix<double,3,1> eigP3Dw = Converter::toVector3d(P3Dw);
- Eigen::Matrix<double,3,1> eigCorrectedP3Dw = g2oCorrectedSwi.map(g2oSiw.map(eigP3Dw));
-
- cv::Mat cvCorrectedP3Dw = Converter::toCvMat(eigCorrectedP3Dw);
- pMPi->SetWorldPos(cvCorrectedP3Dw);
- pMPi->mnCorrectedByKF = mpCurrentKF->mnId;
- pMPi->mnCorrectedReference = pKFi->mnId;
- pMPi->UpdateNormalAndDepth();
- }
-
- // Update keyframe pose with corrected Sim3. First transform Sim3 to SE3 (scale translation)
- Eigen::Matrix3d eigR = g2oCorrectedSiw.rotation().toRotationMatrix();
- Eigen::Vector3d eigt = g2oCorrectedSiw.translation();
- double s = g2oCorrectedSiw.scale();
-
- eigt *=(1./s); //[R t/s;0 1]
-
- cv::Mat correctedTiw = Converter::toCvSE3(eigR,eigt);
-
- pKFi->SetPose(correctedTiw);
-
- // Make sure connections are updated
- pKFi->UpdateConnections();
- }
-
- // Start Loop Fusion
- // Update matched map points and replace if duplicated
- // 检查当前帧的MapPoints与闭环匹配帧的MapPoints是否存在冲突,对冲突的MapPoints进行替换或填补
- for(size_t i=0; i
size(); i++) - {
- if(mvpCurrentMatchedPoints[i])
- {
- MapPoint* pLoopMP = mvpCurrentMatchedPoints[i];
- MapPoint* pCurMP = mpCurrentKF->GetMapPoint(i);
- if(pCurMP) // 如果有重复的MapPoint,则用匹配帧的代替现有的
- pCurMP->Replace(pLoopMP);
- else // 如果当前帧没有该MapPoint,则直接添加
- {
- mpCurrentKF->AddMapPoint(pLoopMP,i);
- pLoopMP->AddObservation(mpCurrentKF,i);
- pLoopMP->ComputeDistinctiveDescriptors();
- }
- }
- }
-
- }
-
- // Project MapPoints observed in the neighborhood of the loop keyframe
- // into the current keyframe and neighbors using corrected poses.
- // Fuse duplications.
- // 通过将闭环时相连关键帧的mvpLoopMapPoints投影到这些关键帧中,进行MapPoints检查与替换
- SearchAndFuse(CorrectedSim3);
-
-
- // After the MapPoint fusion, new links in the covisibility graph will appear attaching both sides of the loop
- // 更新当前关键帧之间的共视相连关系,得到因闭环时MapPoints融合而新得到的连接关系
- map
> LoopConnections; -
- // 遍历当前帧相连关键帧
- for(vector
::iterator vit=mvpCurrentConnectedKFs.begin(), vend=mvpCurrentConnectedKFs.end(); vit!=vend; vit++) - {
- KeyFrame* pKFi = *vit;
- // 得到与当前帧相连关键帧的相连关键帧(二级相连)
- vector
vpPreviousNeighbors = pKFi->GetVectorCovisibleKeyFrames(); -
- // Update connections. Detect new links.
- // 更新一级相连关键帧的连接关系
- pKFi->UpdateConnections();
- // 取出该帧更新后的连接关系
- LoopConnections[pKFi]=pKFi->GetConnectedKeyFrames();
- // 从连接关系中去除闭环之前的二级连接关系,剩下的是由闭环得到的连接关系
- for(vector
::iterator vit_prev=vpPreviousNeighbors.begin(), vend_prev=vpPreviousNeighbors.end(); vit_prev!=vend_prev; vit_prev++) - {
- LoopConnections[pKFi].erase(*vit_prev);
- }
- // 从连接关系中去除闭环之前的一级连接关系,剩下的是由闭环得到的连接关系
- for(vector
::iterator vit2=mvpCurrentConnectedKFs.begin(), vend2=mvpCurrentConnectedKFs.end(); vit2!=vend2; vit2++) - {
- LoopConnections[pKFi].erase(*vit2);
- }
- }
-
- // Optimize graph
- // 进行EssentialGraph优化,LoopConnections是形成闭环后新生成的连接关系
- Optimizer::OptimizeEssentialGraph(mpMap, mpMatchedKF, mpCurrentKF, NonCorrectedSim3, CorrectedSim3, LoopConnections, mbFixScale);
-
- mpMap->InformNewBigChange();
-
- // Add loop edge
- // 添加当前帧与闭环匹配帧之间的边
- mpMatchedKF->AddLoopEdge(mpCurrentKF);
- mpCurrentKF->AddLoopEdge(mpMatchedKF);
-
- // Launch a new thread to perform Global Bundle Adjustment
- // 新建一个线程用于全局BA优化
- mbRunningGBA = true;
- mbFinishedGBA = false;
- mbStopGBA = false;
- mpThreadGBA = new thread(&LoopClosing::RunGlobalBundleAdjustment,this,mpCurrentKF->mnId);
-
- // Loop closed. Release Local Mapping.
- mpLocalMapper->Release();
-
- mLastLoopKFid = mpCurrentKF->mnId;
- }
4. MergeLocal
- void LoopClosing::MergeLocal()
- {
- Verbose::PrintMess("MERGE: Merge Visual detected!!!!", Verbose::VERBOSITY_NORMAL);
-
- int numTemporalKFs = 15;
-
- //Relationship to rebuild the essential graph, it is used two times, first in the local window and later in the rest of the map
- KeyFrame* pNewChild;
- KeyFrame* pNewParent;
-
- vector
vpLocalCurrentWindowKFs; - vector
vpMergeConnectedKFs; -
- // Flag that is true only when we stopped a running BA, in this case we need relaunch at the end of the merge
- bool bRelaunchBA = false;
-
- Verbose::PrintMess("MERGE: Check Full Bundle Adjustment", Verbose::VERBOSITY_DEBUG);
- // If a Global Bundle Adjustment is running, abort it
- if(isRunningGBA())
- {
- unique_lock
lock(mMutexGBA) ; - mbStopGBA = true;
-
- mnFullBAIdx++;
-
- if(mpThreadGBA)
- {
- mpThreadGBA->detach();
- delete mpThreadGBA;
- }
- bRelaunchBA = true;
- }
-
- Verbose::PrintMess("MERGE: Request Stop Local Mapping", Verbose::VERBOSITY_DEBUG);
- mpLocalMapper->RequestStop();
- // Wait until Local Mapping has effectively stopped
- while(!mpLocalMapper->isStopped())
- {
- usleep(1000);
- }
- Verbose::PrintMess("MERGE: Local Map stopped", Verbose::VERBOSITY_DEBUG);
-
- mpLocalMapper->EmptyQueue();
-
- // Merge map will become in the new active map with the local window of KFs and MPs from the current map.
- // Later, the elements of the current map will be transform to the new active map reference, in order to keep real time tracking
- Map* pCurrentMap = mpCurrentKF->GetMap();
- Map* pMergeMap = mpMergeMatchedKF->GetMap();
-
- // Ensure current keyframe is updated
- mpCurrentKF->UpdateConnections();
-
- //Get the current KF and its neighbors(visual->covisibles; inertial->temporal+covisibles)
- set
spLocalWindowKFs; - //Get MPs in the welding area from the current map
- set
spLocalWindowMPs; - if(pCurrentMap->IsInertial() && pMergeMap->IsInertial()) //TODO Check the correct initialization
- {
- KeyFrame* pKFi = mpCurrentKF;
- int nInserted = 0;
- while(pKFi && nInserted < numTemporalKFs)
- {
- spLocalWindowKFs.insert(pKFi);
- pKFi = mpCurrentKF->mPrevKF;
- nInserted++;
-
- set
spMPi = pKFi->GetMapPoints(); - spLocalWindowMPs.insert(spMPi.begin(), spMPi.end());
- }
-
- pKFi = mpCurrentKF->mNextKF;
- while(pKFi)
- {
- spLocalWindowKFs.insert(pKFi);
-
- set
spMPi = pKFi->GetMapPoints(); - spLocalWindowMPs.insert(spMPi.begin(), spMPi.end());
- }
- }
- else
- {
- spLocalWindowKFs.insert(mpCurrentKF);
- }
-
- vector
vpCovisibleKFs = mpCurrentKF->GetBestCovisibilityKeyFrames(numTemporalKFs); - spLocalWindowKFs.insert(vpCovisibleKFs.begin(), vpCovisibleKFs.end());
- const int nMaxTries = 3;
- int nNumTries = 0;
- while(spLocalWindowKFs.size() < numTemporalKFs && nNumTries < nMaxTries)
- {
- vector
vpNewCovKFs; - vpNewCovKFs.empty();
- for(KeyFrame* pKFi : spLocalWindowKFs)
- {
- vector
vpKFiCov = pKFi->GetBestCovisibilityKeyFrames(numTemporalKFs/2); - for(KeyFrame* pKFcov : vpKFiCov)
- {
- if(pKFcov && !pKFcov->isBad() && spLocalWindowKFs.find(pKFcov) == spLocalWindowKFs.end())
- {
- vpNewCovKFs.push_back(pKFcov);
- }
-
- }
- }
-
- spLocalWindowKFs.insert(vpNewCovKFs.begin(), vpNewCovKFs.end());
- nNumTries++;
- }
-
- //TODO TEST
- //vector
vpTestKFs = pCurrentMap->GetAllKeyFrames(); - //spLocalWindowKFs.insert(vpTestKFs.begin(), vpTestKFs.end());
- for(KeyFrame* pKFi : spLocalWindowKFs)
- {
- if(!pKFi || pKFi->isBad())
- continue;
-
- set
spMPs = pKFi->GetMapPoints(); - spLocalWindowMPs.insert(spMPs.begin(), spMPs.end());
- }
-
- set
spMergeConnectedKFs; - if(pCurrentMap->IsInertial() && pMergeMap->IsInertial()) //TODO Check the correct initialization
- {
- KeyFrame* pKFi = mpMergeMatchedKF;
- int nInserted = 0;
- while(pKFi && nInserted < numTemporalKFs)
- {
- spMergeConnectedKFs.insert(pKFi);
- pKFi = mpCurrentKF->mPrevKF;
- nInserted++;
- }
-
- pKFi = mpMergeMatchedKF->mNextKF;
- while(pKFi)
- {
- spMergeConnectedKFs.insert(pKFi);
- }
- }
- else
- {
- spMergeConnectedKFs.insert(mpMergeMatchedKF);
- }
- vpCovisibleKFs = mpMergeMatchedKF->GetBestCovisibilityKeyFrames(numTemporalKFs);
- spMergeConnectedKFs.insert(vpCovisibleKFs.begin(), vpCovisibleKFs.end());
- nNumTries = 0;
- while(spMergeConnectedKFs.size() < numTemporalKFs && nNumTries < nMaxTries)
- {
- vector
vpNewCovKFs; - for(KeyFrame* pKFi : spMergeConnectedKFs)
- {
- vector
vpKFiCov = pKFi->GetBestCovisibilityKeyFrames(numTemporalKFs/2); - for(KeyFrame* pKFcov : vpKFiCov)
- {
- if(pKFcov && !pKFcov->isBad() && spMergeConnectedKFs.find(pKFcov) == spMergeConnectedKFs.end())
- {
- vpNewCovKFs.push_back(pKFcov);
- }
-
- }
- }
-
- spMergeConnectedKFs.insert(vpNewCovKFs.begin(), vpNewCovKFs.end());
- nNumTries++;
- }
-
- set
spMapPointMerge; - for(KeyFrame* pKFi : spMergeConnectedKFs)
- {
- set
vpMPs = pKFi->GetMapPoints(); - spMapPointMerge.insert(vpMPs.begin(),vpMPs.end());
- }
-
- vector
vpCheckFuseMapPoint; - vpCheckFuseMapPoint.reserve(spMapPointMerge.size());
- std::copy(spMapPointMerge.begin(), spMapPointMerge.end(), std::back_inserter(vpCheckFuseMapPoint));
-
- cv::Mat Twc = mpCurrentKF->GetPoseInverse();
-
- cv::Mat Rwc = Twc.rowRange(0,3).colRange(0,3);
- cv::Mat twc = Twc.rowRange(0,3).col(3);
- g2o::Sim3 g2oNonCorrectedSwc(Converter::toMatrix3d(Rwc),Converter::toVector3d(twc),1.0);
- g2o::Sim3 g2oNonCorrectedScw = g2oNonCorrectedSwc.inverse();
- g2o::Sim3 g2oCorrectedScw = mg2oMergeScw;
-
- KeyFrameAndPose vCorrectedSim3, vNonCorrectedSim3;
- vCorrectedSim3[mpCurrentKF]=g2oCorrectedScw;
- vNonCorrectedSim3[mpCurrentKF]=g2oNonCorrectedScw;
-
- for(KeyFrame* pKFi : spLocalWindowKFs)
- {
- if(!pKFi || pKFi->isBad())
- {
- continue;
- }
-
- g2o::Sim3 g2oCorrectedSiw;
-
- if(pKFi!=mpCurrentKF)
- {
- cv::Mat Tiw = pKFi->GetPose();
- cv::Mat Riw = Tiw.rowRange(0,3).colRange(0,3);
- cv::Mat tiw = Tiw.rowRange(0,3).col(3);
- g2o::Sim3 g2oSiw(Converter::toMatrix3d(Riw),Converter::toVector3d(tiw),1.0);
- //Pose without correction
- vNonCorrectedSim3[pKFi]=g2oSiw;
-
- cv::Mat Tic = Tiw*Twc;
- cv::Mat Ric = Tic.rowRange(0,3).colRange(0,3);
- cv::Mat tic = Tic.rowRange(0,3).col(3);
- g2o::Sim3 g2oSic(Converter::toMatrix3d(Ric),Converter::toVector3d(tic),1.0);
- g2oCorrectedSiw = g2oSic*mg2oMergeScw;
- vCorrectedSim3[pKFi]=g2oCorrectedSiw;
- }
- else
- {
- g2oCorrectedSiw = g2oCorrectedScw;
- }
- pKFi->mTcwMerge = pKFi->GetPose();
-
- // Update keyframe pose with corrected Sim3. First transform Sim3 to SE3 (scale translation)
- Eigen::Matrix3d eigR = g2oCorrectedSiw.rotation().toRotationMatrix();
- Eigen::Vector3d eigt = g2oCorrectedSiw.translation();
- double s = g2oCorrectedSiw.scale();
-
- pKFi->mfScale = s;
- eigt *=(1./s); //[R t/s;0 1]
-
- cv::Mat correctedTiw = Converter::toCvSE3(eigR,eigt);
-
- pKFi->mTcwMerge = correctedTiw;
- // Make sure connections are updated
- if(pCurrentMap->isImuInitialized())
- {
- Eigen::Matrix3d Rcor = eigR.transpose()*vNonCorrectedSim3[pKFi].rotation().toRotationMatrix();
- pKFi->mVwbMerge = Converter::toCvMat(Rcor)*pKFi->GetVelocity();
- }
- //TODO DEBUG to know which are the KFs that had been moved to the other map
- //pKFi->mnOriginMapId = 5;
- }
-
- for(MapPoint* pMPi : spLocalWindowMPs)
- {
- if(!pMPi || pMPi->isBad())
- continue;
-
- KeyFrame* pKFref = pMPi->GetReferenceKeyFrame();
- g2o::Sim3 g2oCorrectedSwi = vCorrectedSim3[pKFref].inverse();
- g2o::Sim3 g2oNonCorrectedSiw = vNonCorrectedSim3[pKFref];
-
- // Project with non-corrected pose and project back with corrected pose
- cv::Mat P3Dw = pMPi->GetWorldPos();
- Eigen::Matrix<double,3,1> eigP3Dw = Converter::toVector3d(P3Dw);
- Eigen::Matrix<double,3,1> eigCorrectedP3Dw = g2oCorrectedSwi.map(g2oNonCorrectedSiw.map(eigP3Dw));
- Eigen::Matrix3d eigR = g2oCorrectedSwi.rotation().toRotationMatrix();
- Eigen::Matrix3d Rcor = eigR * g2oNonCorrectedSiw.rotation().toRotationMatrix();
-
- cv::Mat cvCorrectedP3Dw = Converter::toCvMat(eigCorrectedP3Dw);
-
- pMPi->mPosMerge = cvCorrectedP3Dw;
- pMPi->mNormalVectorMerge = Converter::toCvMat(Rcor) * pMPi->GetNormal();
- }
-
- {
- unique_lock
currentLock(pCurrentMap->mMutexMapUpdate) ; // We update the current map with the Merge information - unique_lock
mergeLock(pMergeMap->mMutexMapUpdate) ; // We remove the Kfs and MPs in the merged area from the old map -
- for(KeyFrame* pKFi : spLocalWindowKFs)
- {
- if(!pKFi || pKFi->isBad())
- {
- continue;
- }
-
- pKFi->mTcwBefMerge = pKFi->GetPose();
- pKFi->mTwcBefMerge = pKFi->GetPoseInverse();
- pKFi->SetPose(pKFi->mTcwMerge);
-
- // Make sure connections are updated
- pKFi->UpdateMap(pMergeMap);
- pKFi->mnMergeCorrectedForKF = mpCurrentKF->mnId;
- pMergeMap->AddKeyFrame(pKFi);
- pCurrentMap->EraseKeyFrame(pKFi);
-
- if(pCurrentMap->isImuInitialized())
- {
- pKFi->SetVelocity(pKFi->mVwbMerge);
- }
- }
-
- for(MapPoint* pMPi : spLocalWindowMPs)
- {
- if(!pMPi || pMPi->isBad())
- continue;
-
- pMPi->SetWorldPos(pMPi->mPosMerge);
- pMPi->SetNormalVector(pMPi->mNormalVectorMerge);
- pMPi->UpdateMap(pMergeMap);
- pMergeMap->AddMapPoint(pMPi);
- pCurrentMap->EraseMapPoint(pMPi);
- }
-
- mpAtlas->ChangeMap(pMergeMap);
- mpAtlas->SetMapBad(pCurrentMap);
- pMergeMap->IncreaseChangeIndex();
- }
-
-
- //Rebuild the essential graph in the local window
- pCurrentMap->GetOriginKF()->SetFirstConnection(false);
- pNewChild = mpCurrentKF->GetParent(); // Old parent, it will be the new child of this KF
- pNewParent = mpCurrentKF; // Old child, now it will be the parent of its own parent(we need eliminate this KF from children list in its old parent)
- mpCurrentKF->ChangeParent(mpMergeMatchedKF);
- while(pNewChild )
- {
- pNewChild->EraseChild(pNewParent); // We remove the relation between the old parent and the new for avoid loop
- KeyFrame * pOldParent = pNewChild->GetParent();
-
- pNewChild->ChangeParent(pNewParent);
-
- pNewParent = pNewChild;
- pNewChild = pOldParent;
-
- }
-
- //Update the connections between the local window
- mpMergeMatchedKF->UpdateConnections();
-
- vpMergeConnectedKFs = mpMergeMatchedKF->GetVectorCovisibleKeyFrames();
- vpMergeConnectedKFs.push_back(mpMergeMatchedKF);
- vpCheckFuseMapPoint.reserve(spMapPointMerge.size());
- std::copy(spMapPointMerge.begin(), spMapPointMerge.end(), std::back_inserter(vpCheckFuseMapPoint));
-
- // Project MapPoints observed in the neighborhood of the merge keyframe
- // into the current keyframe and neighbors using corrected poses.
- // Fuse duplications.
- SearchAndFuse(vCorrectedSim3, vpCheckFuseMapPoint);
-
- // Update connectivity
- for(KeyFrame* pKFi : spLocalWindowKFs)
- {
- if(!pKFi || pKFi->isBad())
- continue;
-
- pKFi->UpdateConnections();
- }
- for(KeyFrame* pKFi : spMergeConnectedKFs)
- {
- if(!pKFi || pKFi->isBad())
- continue;
-
- pKFi->UpdateConnections();
- }
-
- bool bStop = false;
- Verbose::PrintMess("MERGE: Start local BA ", Verbose::VERBOSITY_DEBUG);
- vpLocalCurrentWindowKFs.clear();
- vpMergeConnectedKFs.clear();
- std::copy(spLocalWindowKFs.begin(), spLocalWindowKFs.end(), std::back_inserter(vpLocalCurrentWindowKFs));
- std::copy(spMergeConnectedKFs.begin(), spMergeConnectedKFs.end(), std::back_inserter(vpMergeConnectedKFs));
- if (mpTracker->mSensor==System::IMU_MONOCULAR || mpTracker->mSensor==System::IMU_STEREO)
- {
- Optimizer::MergeInertialBA(mpLocalMapper->GetCurrKF(),mpMergeMatchedKF,&bStop, mpCurrentKF->GetMap(),vCorrectedSim3);
- }
- else
- {
- Optimizer::LocalBundleAdjustment(mpCurrentKF, vpLocalCurrentWindowKFs, vpMergeConnectedKFs,&bStop);
- }
-
- // Loop closed. Release Local Mapping.
- mpLocalMapper->Release();
-
- Verbose::PrintMess("MERGE: Finish the LBA", Verbose::VERBOSITY_DEBUG);
-
-
-
- //Update the non critical area from the current map to the merged map
- vector
vpCurrentMapKFs = pCurrentMap->GetAllKeyFrames(); - vector
vpCurrentMapMPs = pCurrentMap->GetAllMapPoints(); -
- if(vpCurrentMapKFs.size() == 0)
- {
- Verbose::PrintMess("MERGE: There are not KFs outside of the welding area", Verbose::VERBOSITY_DEBUG);
- }
- else
- {
- Verbose::PrintMess("MERGE: Calculate the new position of the elements outside of the window", Verbose::VERBOSITY_DEBUG);
- //Apply the transformation
- {
- if(mpTracker->mSensor == System::MONOCULAR)
- {
- unique_lock
currentLock(pCurrentMap->mMutexMapUpdate) ; // We update the current map with the Merge information -
- for(KeyFrame* pKFi : vpCurrentMapKFs)
- {
- if(!pKFi || pKFi->isBad() || pKFi->GetMap() != pCurrentMap)
- {
- continue;
- }
-
- g2o::Sim3 g2oCorrectedSiw;
-
- cv::Mat Tiw = pKFi->GetPose();
- cv::Mat Riw = Tiw.rowRange(0,3).colRange(0,3);
- cv::Mat tiw = Tiw.rowRange(0,3).col(3);
- g2o::Sim3 g2oSiw(Converter::toMatrix3d(Riw),Converter::toVector3d(tiw),1.0);
- //Pose without correction
- vNonCorrectedSim3[pKFi]=g2oSiw;
-
- cv::Mat Tic = Tiw*Twc;
- cv::Mat Ric = Tic.rowRange(0,3).colRange(0,3);
- cv::Mat tic = Tic.rowRange(0,3).col(3);
- g2o::Sim3 g2oSim(Converter::toMatrix3d(Ric),Converter::toVector3d(tic),1.0);
- g2oCorrectedSiw = g2oSim*mg2oMergeScw;
- vCorrectedSim3[pKFi]=g2oCorrectedSiw;
-
- // Update keyframe pose with corrected Sim3. First transform Sim3 to SE3 (scale translation)
- Eigen::Matrix3d eigR = g2oCorrectedSiw.rotation().toRotationMatrix();
- Eigen::Vector3d eigt = g2oCorrectedSiw.translation();
- double s = g2oCorrectedSiw.scale();
-
- pKFi->mfScale = s;
- eigt *=(1./s); //[R t/s;0 1]
-
- cv::Mat correctedTiw = Converter::toCvSE3(eigR,eigt);
-
- pKFi->mTcwBefMerge = pKFi->GetPose();
- pKFi->mTwcBefMerge = pKFi->GetPoseInverse();
-
- pKFi->SetPose(correctedTiw);
-
- if(pCurrentMap->isImuInitialized())
- {
- Eigen::Matrix3d Rcor = eigR.transpose()*vNonCorrectedSim3[pKFi].rotation().toRotationMatrix();
- pKFi->SetVelocity(Converter::toCvMat(Rcor)*pKFi->GetVelocity()); // TODO: should add here scale s
- }
-
- }
- for(MapPoint* pMPi : vpCurrentMapMPs)
- {
- if(!pMPi || pMPi->isBad()|| pMPi->GetMap() != pCurrentMap)
- continue;
-
- KeyFrame* pKFref = pMPi->GetReferenceKeyFrame();
- g2o::Sim3 g2oCorrectedSwi = vCorrectedSim3[pKFref].inverse();
- g2o::Sim3 g2oNonCorrectedSiw = vNonCorrectedSim3[pKFref];
-
- // Project with non-corrected pose and project back with corrected pose
- cv::Mat P3Dw = pMPi->GetWorldPos();
- Eigen::Matrix<double,3,1> eigP3Dw = Converter::toVector3d(P3Dw);
- Eigen::Matrix<double,3,1> eigCorrectedP3Dw = g2oCorrectedSwi.map(g2oNonCorrectedSiw.map(eigP3Dw));
-
- cv::Mat cvCorrectedP3Dw = Converter::toCvMat(eigCorrectedP3Dw);
- pMPi->SetWorldPos(cvCorrectedP3Dw);
-
- pMPi->UpdateNormalAndDepth();
- }
- }
- }
-
- mpLocalMapper->RequestStop();
- // Wait until Local Mapping has effectively stopped
- while(!mpLocalMapper->isStopped())
- {
- usleep(1000);
- }
-
- // Optimize graph (and update the loop position for each element form the begining to the end)
- if(mpTracker->mSensor != System::MONOCULAR)
- {
- Optimizer::OptimizeEssentialGraph(mpCurrentKF, vpMergeConnectedKFs, vpLocalCurrentWindowKFs, vpCurrentMapKFs, vpCurrentMapMPs);
- }
-
-
- {
- // Get Merge Map Mutex
- unique_lock
currentLock(pCurrentMap->mMutexMapUpdate) ; // We update the current map with the Merge information - unique_lock
mergeLock(pMergeMap->mMutexMapUpdate) ; // We remove the Kfs and MPs in the merged area from the old map -
- for(KeyFrame* pKFi : vpCurrentMapKFs)
- {
- if(!pKFi || pKFi->isBad() || pKFi->GetMap() != pCurrentMap)
- {
- continue;
- }
-
- // Make sure connections are updated
- pKFi->UpdateMap(pMergeMap);
- pMergeMap->AddKeyFrame(pKFi);
- pCurrentMap->EraseKeyFrame(pKFi);
- }
-
- for(MapPoint* pMPi : vpCurrentMapMPs)
- {
- if(!pMPi || pMPi->isBad())
- continue;
-
- pMPi->UpdateMap(pMergeMap);
- pMergeMap->AddMapPoint(pMPi);
- pCurrentMap->EraseMapPoint(pMPi);
- }
- }
- }
-
- mpLocalMapper->Release();
-
- Verbose::PrintMess("MERGE:Completed!!!!!", Verbose::VERBOSITY_DEBUG);
-
- if(bRelaunchBA && (!pCurrentMap->isImuInitialized() || (pCurrentMap->KeyFramesInMap()<200 && mpAtlas->CountMaps()==1)))
- {
- // Launch a new thread to perform Global Bundle Adjustment
- Verbose::PrintMess("Relaunch Global BA", Verbose::VERBOSITY_DEBUG);
- mbRunningGBA = true;
- mbFinishedGBA = false;
- mbStopGBA = false;
- mpThreadGBA = new thread(&LoopClosing::RunGlobalBundleAdjustment,this, pMergeMap, mpCurrentKF->mnId);
- }
-
- mpMergeMatchedKF->AddMergeEdge(mpCurrentKF);
- mpCurrentKF->AddMergeEdge(mpMergeMatchedKF);
-
- pCurrentMap->IncreaseChangeIndex();
- pMergeMap->IncreaseChangeIndex();
-
- mpAtlas->RemoveBadMaps();
-
- }
参考文献
-
相关阅读:
光流法optical flow
基于MiniTest的小程序自动化测试
(Git)git clone报错——SSL certificate problem: self signed certificate
2022年数学建模国赛--赛后总结
string(讲解)
ffmpeg基础四:RTP协议
【历史上的今天】8 月 15 日:苹果推出初代 iMac;谷歌收购摩托罗拉移动;Fuchsia 首次发布
数据安全:.[bkpsvr@firemail.cc].EKING勒索病毒的特点和数据恢复方法
Java多线程篇(3)——线程池
企业集中监控体系思路及架构
-
原文地址:https://blog.csdn.net/xhtchina/article/details/119767848