/** * This file is part of ORB-SLAM3 * * Copyright (C) 2017-2020 Carlos Campos, Richard Elvira, Juan J. Gómez Rodríguez, José M.M. Montiel and Juan D. Tardós, University of Zaragoza. * Copyright (C) 2014-2016 Raúl Mur-Artal, José M.M. Montiel and Juan D. Tardós, University of Zaragoza. * * ORB-SLAM3 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public * License as published by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ORB-SLAM3 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with ORB-SLAM3. * If not, see . */ #include "LoopClosing.h" #include "Sim3Solver.h" #include "Converter.h" #include "Optimizer.h" #include "ORBmatcher.h" #include "G2oTypes.h" #include #include namespace ORB_SLAM3 { LoopClosing::LoopClosing(Atlas *pAtlas, KeyFrameDatabase *pDB, ORBVocabulary *pVoc, const bool bFixScale): mbResetRequested(false), mbResetActiveMapRequested(false), mbFinishRequested(false), mbFinished(true), mpAtlas(pAtlas), mpKeyFrameDB(pDB), mpORBVocabulary(pVoc), mpMatchedKF(NULL), mLastLoopKFid(0), mbRunningGBA(false), mbFinishedGBA(true), mbStopGBA(false), mpThreadGBA(NULL), mbFixScale(bFixScale), mnFullBAIdx(0), mnLoopNumCoincidences(0), mnMergeNumCoincidences(0), mbLoopDetected(false), mbMergeDetected(false), mnLoopNumNotFound(0), mnMergeNumNotFound(0) { mnCovisibilityConsistencyTh = 3; mpLastCurrentKF = static_cast(NULL); } void LoopClosing::SetTracker(Tracking *pTracker) { mpTracker=pTracker; } void LoopClosing::SetLocalMapper(LocalMapping *pLocalMapper) { mpLocalMapper=pLocalMapper; } void LoopClosing::Run() { mbFinished =false; while(1) { //NEW LOOP AND MERGE DETECTION ALGORITHM //---------------------------- if(CheckNewKeyFrames()) { if(mpLastCurrentKF) { mpLastCurrentKF->mvpLoopCandKFs.clear(); mpLastCurrentKF->mvpMergeCandKFs.clear(); } if(NewDetectCommonRegions()) { if(mbMergeDetected) { if ((mpTracker->mSensor==System::IMU_MONOCULAR ||mpTracker->mSensor==System::IMU_STEREO) && (!mpCurrentKF->GetMap()->isImuInitialized())) { cout << "IMU is not initilized, merge is aborted" << endl; } else { Verbose::PrintMess("*Merged detected", Verbose::VERBOSITY_NORMAL); Verbose::PrintMess("Number of KFs in the current map: " + to_string(mpCurrentKF->GetMap()->KeyFramesInMap()), Verbose::VERBOSITY_DEBUG); cv::Mat mTmw = mpMergeMatchedKF->GetPose(); g2o::Sim3 gSmw2(Converter::toMatrix3d(mTmw.rowRange(0, 3).colRange(0, 3)),Converter::toVector3d(mTmw.rowRange(0, 3).col(3)),1.0); cv::Mat mTcw = mpCurrentKF->GetPose(); g2o::Sim3 gScw1(Converter::toMatrix3d(mTcw.rowRange(0, 3).colRange(0, 3)),Converter::toVector3d(mTcw.rowRange(0, 3).col(3)),1.0); g2o::Sim3 gSw2c = mg2oMergeSlw.inverse(); g2o::Sim3 gSw1m = mg2oMergeSlw; mSold_new = (gSw2c * gScw1); if(mpCurrentKF->GetMap()->IsInertial() && mpMergeMatchedKF->GetMap()->IsInertial()) { if(mSold_new.scale()<0.90||mSold_new.scale()>1.1){ mpMergeLastCurrentKF->SetErase(); mpMergeMatchedKF->SetErase(); mnMergeNumCoincidences = 0; mvpMergeMatchedMPs.clear(); mvpMergeMPs.clear(); mnMergeNumNotFound = 0; mbMergeDetected = false; Verbose::PrintMess("scale bad estimated. Abort merging", Verbose::VERBOSITY_NORMAL); continue; } // If inertial, force only yaw if ((mpTracker->mSensor==System::IMU_MONOCULAR ||mpTracker->mSensor==System::IMU_STEREO) && mpCurrentKF->GetMap()->GetIniertialBA1()) // TODO, maybe with GetIniertialBA1 { Eigen::Vector3d phi = LogSO3(mSold_new.rotation().toRotationMatrix()); phi(0)=0; phi(1)=0; mSold_new = g2o::Sim3(ExpSO3(phi),mSold_new.translation(),1.0); } } // cout << "tw2w1: " << mSold_new.translation() << endl; // cout << "Rw2w1: " << mSold_new.rotation().toRotationMatrix() << endl; // cout << "Angle Rw2w1: " << 180*LogSO3(mSold_new.rotation().toRotationMatrix())/3.14 << endl; // cout << "scale w2w1: " << mSold_new.scale() << endl; mg2oMergeSmw = gSmw2 * gSw2c * gScw1; mg2oMergeScw = mg2oMergeSlw; // TODO UNCOMMENT if (mpTracker->mSensor==System::IMU_MONOCULAR ||mpTracker->mSensor==System::IMU_STEREO) MergeLocal2(); else MergeLocal(); } vdPR_CurrentTime.push_back(mpCurrentKF->mTimeStamp); vdPR_MatchedTime.push_back(mpMergeMatchedKF->mTimeStamp); vnPR_TypeRecogn.push_back(1); // Reset all variables mpMergeLastCurrentKF->SetErase(); mpMergeMatchedKF->SetErase(); mnMergeNumCoincidences = 0; mvpMergeMatchedMPs.clear(); mvpMergeMPs.clear(); mnMergeNumNotFound = 0; mbMergeDetected = false; if(mbLoopDetected) { // Reset Loop variables mpLoopLastCurrentKF->SetErase(); mpLoopMatchedKF->SetErase(); mnLoopNumCoincidences = 0; mvpLoopMatchedMPs.clear(); mvpLoopMPs.clear(); mnLoopNumNotFound = 0; mbLoopDetected = false; } } if(mbLoopDetected) { vdPR_CurrentTime.push_back(mpCurrentKF->mTimeStamp); vdPR_MatchedTime.push_back(mpLoopMatchedKF->mTimeStamp); vnPR_TypeRecogn.push_back(0); Verbose::PrintMess("*Loop detected", Verbose::VERBOSITY_NORMAL); mg2oLoopScw = mg2oLoopSlw; //*mvg2oSim3LoopTcw[nCurrentIndex]; if(mpCurrentKF->GetMap()->IsInertial()) { cv::Mat Twc = mpCurrentKF->GetPoseInverse(); g2o::Sim3 g2oTwc(Converter::toMatrix3d(Twc.rowRange(0, 3).colRange(0, 3)),Converter::toVector3d(Twc.rowRange(0, 3).col(3)),1.0); g2o::Sim3 g2oSww_new = g2oTwc*mg2oLoopScw; Eigen::Vector3d phi = LogSO3(g2oSww_new.rotation().toRotationMatrix()); //cout << "tw2w1: " << g2oSww_new.translation() << endl; //cout << "Rw2w1: " << g2oSww_new.rotation().toRotationMatrix() << endl; //cout << "Angle Rw2w1: " << 180*phi/3.14 << endl; //cout << "scale w2w1: " << g2oSww_new.scale() << endl; if (fabs(phi(0))<0.008f && fabs(phi(1))<0.008f && fabs(phi(2))<0.349f) { if(mpCurrentKF->GetMap()->IsInertial()) { // If inertial, force only yaw if ((mpTracker->mSensor==System::IMU_MONOCULAR ||mpTracker->mSensor==System::IMU_STEREO) && mpCurrentKF->GetMap()->GetIniertialBA2()) // TODO, maybe with GetIniertialBA1 { phi(0)=0; phi(1)=0; g2oSww_new = g2o::Sim3(ExpSO3(phi),g2oSww_new.translation(),1.0); mg2oLoopScw = g2oTwc.inverse()*g2oSww_new; } } mvpLoopMapPoints = mvpLoopMPs;//*mvvpLoopMapPoints[nCurrentIndex]; CorrectLoop(); } else { cout << "BAD LOOP!!!" << endl; } } else { mvpLoopMapPoints = mvpLoopMPs; CorrectLoop(); } // Reset all variables mpLoopLastCurrentKF->SetErase(); mpLoopMatchedKF->SetErase(); mnLoopNumCoincidences = 0; mvpLoopMatchedMPs.clear(); mvpLoopMPs.clear(); mnLoopNumNotFound = 0; mbLoopDetected = false; } } mpLastCurrentKF = mpCurrentKF; } ResetIfRequested(); if(CheckFinish()){ // cout << "LC: Finish requested" << endl; break; } usleep(5000); } //ofstream f_stats; //f_stats.open("PlaceRecognition_stats" + mpLocalMapper->strSequence + ".txt"); //f_stats << "# current_timestamp, matched_timestamp, [0:Loop, 1:Merge]" << endl; //f_stats << fixed; //for(int i=0; i< vdPR_CurrentTime.size(); ++i) //{ // f_stats << 1e9*vdPR_CurrentTime[i] << "," << 1e9*vdPR_MatchedTime[i] << "," << vnPR_TypeRecogn[i] << endl; //} //f_stats.close(); SetFinish(); } void LoopClosing::InsertKeyFrame(KeyFrame *pKF) { unique_lock lock(mMutexLoopQueue); if(pKF->mnId!=0) mlpLoopKeyFrameQueue.push_back(pKF); } bool LoopClosing::CheckNewKeyFrames() { unique_lock lock(mMutexLoopQueue); return(!mlpLoopKeyFrameQueue.empty()); } bool LoopClosing::NewDetectCommonRegions() { { 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(); mpCurrentKF->mbCurrentPlaceRecognition = true; mpLastMap = mpCurrentKF->GetMap(); } if(mpLastMap->IsInertial() && !mpLastMap->GetIniertialBA1()) { mpKeyFrameDB->add(mpCurrentKF); mpCurrentKF->SetErase(); return false; } if(mpTracker->mSensor == System::STEREO && mpLastMap->GetAllKeyFrames().size() < 5) //12 { mpKeyFrameDB->add(mpCurrentKF); mpCurrentKF->SetErase(); return false; } if(mpLastMap->GetAllKeyFrames().size() < 12) { mpKeyFrameDB->add(mpCurrentKF); mpCurrentKF->SetErase(); return false; } //Check the last candidates with geometric validation // Loop candidates bool bLoopDetectedInKF = false; bool bCheckSpatial = false; if(mnLoopNumCoincidences > 0) { bCheckSpatial = true; // Find from the last KF candidates cv::Mat mTcl = mpCurrentKF->GetPose() * mpLoopLastCurrentKF->GetPoseInverse(); g2o::Sim3 gScl(Converter::toMatrix3d(mTcl.rowRange(0, 3).colRange(0, 3)),Converter::toVector3d(mTcl.rowRange(0, 3).col(3)),1.0); g2o::Sim3 gScw = gScl * mg2oLoopSlw; int numProjMatches = 0; vector vpMatchedMPs; bool bCommonRegion = DetectAndReffineSim3FromLastKF(mpCurrentKF, mpLoopMatchedKF, gScw, numProjMatches, mvpLoopMPs, vpMatchedMPs); if(bCommonRegion) { bLoopDetectedInKF = true; mnLoopNumCoincidences++; mpLoopLastCurrentKF->SetErase(); mpLoopLastCurrentKF = mpCurrentKF; mg2oLoopSlw = gScw; mvpLoopMatchedMPs = vpMatchedMPs; mbLoopDetected = mnLoopNumCoincidences >= 3; mnLoopNumNotFound = 0; if(!mbLoopDetected) { //f_succes_pr << mpCurrentKF->mNameFile << " " << "8"<< endl; //f_succes_pr << "% Number of spatial consensous: " << std::to_string(mnLoopNumCoincidences) << endl; cout << "PR: Loop detected with Reffine Sim3" << endl; } } else { bLoopDetectedInKF = false; /*f_succes_pr << mpCurrentKF->mNameFile << " " << "8"<< endl; f_succes_pr << "% Number of spatial consensous: " << std::to_string(mnLoopNumCoincidences) << endl;*/ mnLoopNumNotFound++; if(mnLoopNumNotFound >= 2) { /*for(int i=0; iSetErase(); mvpLoopCandidateKF[i]->SetErase(); mvpLoopLastKF[i]->mbCurrentPlaceRecognition = true; } mvpLoopCandidateKF.clear(); mvpLoopLastKF.clear(); mvg2oSim3LoopTcw.clear(); mvnLoopNumMatches.clear(); mvvpLoopMapPoints.clear(); mvvpLoopMatchedMapPoints.clear();*/ mpLoopLastCurrentKF->SetErase(); mpLoopMatchedKF->SetErase(); //mg2oLoopScw; mnLoopNumCoincidences = 0; mvpLoopMatchedMPs.clear(); mvpLoopMPs.clear(); mnLoopNumNotFound = 0; } } } //Merge candidates bool bMergeDetectedInKF = false; if(mnMergeNumCoincidences > 0) { // Find from the last KF candidates cv::Mat mTcl = mpCurrentKF->GetPose() * mpMergeLastCurrentKF->GetPoseInverse(); g2o::Sim3 gScl(Converter::toMatrix3d(mTcl.rowRange(0, 3).colRange(0, 3)),Converter::toVector3d(mTcl.rowRange(0, 3).col(3)),1.0); g2o::Sim3 gScw = gScl * mg2oMergeSlw; int numProjMatches = 0; vector vpMatchedMPs; bool bCommonRegion = DetectAndReffineSim3FromLastKF(mpCurrentKF, mpMergeMatchedKF, gScw, numProjMatches, mvpMergeMPs, vpMatchedMPs); if(bCommonRegion) { //cout << "BoW: Merge reffined Sim3 transformation suscelful" << endl; bMergeDetectedInKF = true; mnMergeNumCoincidences++; mpMergeLastCurrentKF->SetErase(); mpMergeLastCurrentKF = mpCurrentKF; mg2oMergeSlw = gScw; mvpMergeMatchedMPs = vpMatchedMPs; mbMergeDetected = mnMergeNumCoincidences >= 3; } else { //cout << "BoW: Merge reffined Sim3 transformation failed" << endl; mbMergeDetected = false; bMergeDetectedInKF = false; mnMergeNumNotFound++; if(mnMergeNumNotFound >= 2) { /*cout << "+++++++Merge detected failed in KF" << endl; for(int i=0; iSetErase(); mvpMergeCandidateKF[i]->SetErase(); mvpMergeLastKF[i]->mbCurrentPlaceRecognition = true; } mvpMergeCandidateKF.clear(); mvpMergeLastKF.clear(); mvg2oSim3MergeTcw.clear(); mvnMergeNumMatches.clear(); mvvpMergeMapPoints.clear(); mvvpMergeMatchedMapPoints.clear();*/ mpMergeLastCurrentKF->SetErase(); mpMergeMatchedKF->SetErase(); mnMergeNumCoincidences = 0; mvpMergeMatchedMPs.clear(); mvpMergeMPs.clear(); mnMergeNumNotFound = 0; } } } if(mbMergeDetected || mbLoopDetected) { //f_time_pr << "Geo" << " " << timeGeoKF_ms.count() << endl; mpKeyFrameDB->add(mpCurrentKF); return true; } //------------- //TODO: This is only necessary if we use a minimun score for pick the best candidates const vector vpConnectedKeyFrames = mpCurrentKF->GetVectorCovisibleKeyFrames(); const DBoW2::BowVector &CurrentBowVec = mpCurrentKF->mBowVec; /*float minScore = 1; for(size_t i=0; iisBad()) continue; const DBoW2::BowVector &BowVec = pKF->mBowVec; float score = mpORBVocabulary->score(CurrentBowVec, BowVec); if(score vpMergeBowCand, vpLoopBowCand; //cout << "LC: bMergeDetectedInKF: " << bMergeDetectedInKF << " bLoopDetectedInKF: " << bLoopDetectedInKF << endl; if(!bMergeDetectedInKF || !bLoopDetectedInKF) { // Search in BoW mpKeyFrameDB->DetectNBestCandidates(mpCurrentKF, vpLoopBowCand, vpMergeBowCand,3); } // Check the BoW candidates if the geometric candidate list is empty //Loop candidates /*#ifdef COMPILEDWITHC11 std::chrono::steady_clock::time_point timeStartGeoBoW = std::chrono::steady_clock::now(); #else std::chrono::monotonic_clock::time_point timeStartGeoBoW = std::chrono::monotonic_clock::now(); #endif*/ if(!bLoopDetectedInKF && !vpLoopBowCand.empty()) { mbLoopDetected = DetectCommonRegionsFromBoW(vpLoopBowCand, mpLoopMatchedKF, mpLoopLastCurrentKF, mg2oLoopSlw, mnLoopNumCoincidences, mvpLoopMPs, mvpLoopMatchedMPs); } // Merge candidates //cout << "LC: Find BoW candidates" << endl; if(!bMergeDetectedInKF && !vpMergeBowCand.empty()) { mbMergeDetected = DetectCommonRegionsFromBoW(vpMergeBowCand, mpMergeMatchedKF, mpMergeLastCurrentKF, mg2oMergeSlw, mnMergeNumCoincidences, mvpMergeMPs, mvpMergeMatchedMPs); } //cout << "LC: add to KFDB" << endl; mpKeyFrameDB->add(mpCurrentKF); if(mbMergeDetected || mbLoopDetected) { return true; } //cout << "LC: erase KF" << endl; mpCurrentKF->SetErase(); mpCurrentKF->mbCurrentPlaceRecognition = false; return false; } bool LoopClosing::DetectAndReffineSim3FromLastKF(KeyFrame* pCurrentKF, KeyFrame* pMatchedKF, g2o::Sim3 &gScw, int &nNumProjMatches, std::vector &vpMPs, std::vector &vpMatchedMPs) { set spAlreadyMatchedMPs; nNumProjMatches = FindMatchesByProjection(pCurrentKF, pMatchedKF, gScw, spAlreadyMatchedMPs, vpMPs, vpMatchedMPs); cout << "REFFINE-SIM3: Projection from last KF with " << nNumProjMatches << " matches" << endl; int nProjMatches = 30; int nProjOptMatches = 50; int nProjMatchesRep = 100; /*if(mpTracker->mSensor==System::IMU_MONOCULAR ||mpTracker->mSensor==System::IMU_STEREO) { nProjMatches = 50; nProjOptMatches = 50; nProjMatchesRep = 80; }*/ if(nNumProjMatches >= nProjMatches) { cv::Mat mScw = Converter::toCvMat(gScw); cv::Mat mTwm = pMatchedKF->GetPoseInverse(); g2o::Sim3 gSwm(Converter::toMatrix3d(mTwm.rowRange(0, 3).colRange(0, 3)),Converter::toVector3d(mTwm.rowRange(0, 3).col(3)),1.0); g2o::Sim3 gScm = gScw * gSwm; Eigen::Matrix mHessian7x7; bool bFixedScale = mbFixScale; // TODO CHECK; Solo para el monocular inertial if(mpTracker->mSensor==System::IMU_MONOCULAR && !pCurrentKF->GetMap()->GetIniertialBA2()) bFixedScale=false; int numOptMatches = Optimizer::OptimizeSim3(mpCurrentKF, pMatchedKF, vpMatchedMPs, gScm, 10, bFixedScale, mHessian7x7, true); cout << "REFFINE-SIM3: Optimize Sim3 from last KF with " << numOptMatches << " inliers" << endl; if(numOptMatches > nProjOptMatches) { g2o::Sim3 gScw_estimation(Converter::toMatrix3d(mScw.rowRange(0, 3).colRange(0, 3)), Converter::toVector3d(mScw.rowRange(0, 3).col(3)),1.0); vector vpMatchedMP; vpMatchedMP.resize(mpCurrentKF->GetMapPointMatches().size(), static_cast(NULL)); nNumProjMatches = FindMatchesByProjection(pCurrentKF, pMatchedKF, gScw_estimation, spAlreadyMatchedMPs, vpMPs, vpMatchedMPs); //cout << "REFFINE-SIM3: Projection with optimize Sim3 from last KF with " << nNumProjMatches << " matches" << endl; if(nNumProjMatches >= nProjMatchesRep) { gScw = gScw_estimation; return true; } } } return false; } bool LoopClosing::DetectCommonRegionsFromBoW(std::vector &vpBowCand, KeyFrame* &pMatchedKF2, KeyFrame* &pLastCurrentKF, g2o::Sim3 &g2oScw, int &nNumCoincidences, std::vector &vpMPs, std::vector &vpMatchedMPs) { int nBoWMatches = 20; int nBoWInliers = 15; int nSim3Inliers = 20; int nProjMatches = 50; int nProjOptMatches = 80; /*if(mpTracker->mSensor==System::IMU_MONOCULAR ||mpTracker->mSensor==System::IMU_STEREO) { nBoWMatches = 20; nBoWInliers = 15; nSim3Inliers = 30; nProjMatches = 50; nProjOptMatches = 70; }*/ set spConnectedKeyFrames = mpCurrentKF->GetConnectedKeyFrames(); int nNumCovisibles = 5; ORBmatcher matcherBoW(0.9, true); ORBmatcher matcher(0.75, true); int nNumGuidedMatching = 0; // Varibles to select the best numbe KeyFrame* pBestMatchedKF; int nBestMatchesReproj = 0; int nBestNumCoindicendes = 0; g2o::Sim3 g2oBestScw; std::vector vpBestMapPoints; std::vector vpBestMatchedMapPoints; int numCandidates = vpBowCand.size(); vector vnStage(numCandidates, 0); vector vnMatchesStage(numCandidates, 0); int index = 0; for(KeyFrame* pKFi : vpBowCand) { //cout << endl << "-------------------------------" << endl; if(!pKFi || pKFi->isBad()) continue; // Current KF against KF with covisibles version std::vector vpCovKFi = pKFi->GetBestCovisibilityKeyFrames(nNumCovisibles); vpCovKFi.push_back(vpCovKFi[0]); vpCovKFi[0] = pKFi; std::vector > vvpMatchedMPs; vvpMatchedMPs.resize(vpCovKFi.size()); std::set spMatchedMPi; int numBoWMatches = 0; KeyFrame* pMostBoWMatchesKF = pKFi; int nMostBoWNumMatches = 0; std::vector vpMatchedPoints = std::vector(mpCurrentKF->GetMapPointMatches().size(), static_cast(NULL)); std::vector vpKeyFrameMatchedMP = std::vector(mpCurrentKF->GetMapPointMatches().size(), static_cast(NULL)); int nIndexMostBoWMatchesKF=0; for(int j=0; jisBad()) continue; int num = matcherBoW.SearchByBoW(mpCurrentKF, vpCovKFi[j], vvpMatchedMPs[j]); //cout << "BoW: " << num << " putative matches with KF " << vpCovKFi[j]->mnId << endl; if (num > nMostBoWNumMatches) { nMostBoWNumMatches = num; nIndexMostBoWMatchesKF = j; } } bool bAbortByNearKF = false; for(int j=0; jisBad()) continue; if(spMatchedMPi.find(pMPi_j) == spMatchedMPi.end()) { spMatchedMPi.insert(pMPi_j); numBoWMatches++; vpMatchedPoints[k]= pMPi_j; vpKeyFrameMatchedMP[k] = vpCovKFi[j]; } } } //cout <<"BoW: " << numBoWMatches << " independent putative matches" << endl; if(!bAbortByNearKF && numBoWMatches >= nBoWMatches) // TODO pick a good threshold { /*cout << "-------------------------------" << endl; cout << "Geometric validation with " << numBoWMatches << endl; cout << "KFc: " << mpCurrentKF->mnId << "; KFm: " << pMostBoWMatchesKF->mnId << endl;*/ // Geometric validation bool bFixedScale = mbFixScale; if(mpTracker->mSensor==System::IMU_MONOCULAR && !mpCurrentKF->GetMap()->GetIniertialBA2()) bFixedScale=false; Sim3Solver solver = Sim3Solver(mpCurrentKF, pMostBoWMatchesKF, vpMatchedPoints, bFixedScale, vpKeyFrameMatchedMP); solver.SetRansacParameters(0.99, nBoWInliers, 300); // at least 15 inliers bool bNoMore = false; vector vbInliers; int nInliers; bool bConverge = false; cv::Mat mTcm; while(!bConverge && !bNoMore) { mTcm = solver.iterate(20,bNoMore, vbInliers, nInliers, bConverge); } //cout << "Num inliers: " << nInliers << endl; if(bConverge) { //cout <<"BoW: " << nInliers << " inliers in Sim3Solver" << endl; // Match by reprojection //int nNumCovisibles = 5; vpCovKFi.clear(); vpCovKFi = pMostBoWMatchesKF->GetBestCovisibilityKeyFrames(nNumCovisibles); int nInitialCov = vpCovKFi.size(); vpCovKFi.push_back(pMostBoWMatchesKF); set spCheckKFs(vpCovKFi.begin(), vpCovKFi.end()); set spMapPoints; vector vpMapPoints; vector vpKeyFrames; for(KeyFrame* pCovKFi : vpCovKFi) { for(MapPoint* pCovMPij : pCovKFi->GetMapPointMatches()) { if(!pCovMPij || pCovMPij->isBad()) continue; if(spMapPoints.find(pCovMPij) == spMapPoints.end()) { spMapPoints.insert(pCovMPij); vpMapPoints.push_back(pCovMPij); vpKeyFrames.push_back(pCovKFi); } } } //cout << "Point cloud: " << vpMapPoints.size() << endl; g2o::Sim3 gScm(Converter::toMatrix3d(solver.GetEstimatedRotation()),Converter::toVector3d(solver.GetEstimatedTranslation()),solver.GetEstimatedScale()); g2o::Sim3 gSmw(Converter::toMatrix3d(pMostBoWMatchesKF->GetRotation()),Converter::toVector3d(pMostBoWMatchesKF->GetTranslation()),1.0); g2o::Sim3 gScw = gScm*gSmw; // Similarity matrix of current from the world position cv::Mat mScw = Converter::toCvMat(gScw); vector vpMatchedMP; vpMatchedMP.resize(mpCurrentKF->GetMapPointMatches().size(), static_cast(NULL)); vector vpMatchedKF; vpMatchedKF.resize(mpCurrentKF->GetMapPointMatches().size(), static_cast(NULL)); int numProjMatches = matcher.SearchByProjection(mpCurrentKF, mScw, vpMapPoints, vpKeyFrames, vpMatchedMP, vpMatchedKF, 8, 1.5); cout <<"BoW: " << numProjMatches << " matches between " << vpMapPoints.size() << " points with coarse Sim3" << endl; if(numProjMatches >= nProjMatches) { // Optimize Sim3 transformation with every matches Eigen::Matrix mHessian7x7; bool bFixedScale = mbFixScale; if(mpTracker->mSensor==System::IMU_MONOCULAR && !mpCurrentKF->GetMap()->GetIniertialBA2()) bFixedScale=false; int numOptMatches = Optimizer::OptimizeSim3(mpCurrentKF, pKFi, vpMatchedMP, gScm, 10, mbFixScale, mHessian7x7, true); //cout <<"BoW: " << numOptMatches << " inliers in the Sim3 optimization" << endl; //cout << "Inliers in Sim3 optimization: " << numOptMatches << endl; if(numOptMatches >= nSim3Inliers) { //cout <<"BoW: " << numOptMatches << " inliers in Sim3 optimization" << endl; g2o::Sim3 gSmw(Converter::toMatrix3d(pMostBoWMatchesKF->GetRotation()),Converter::toVector3d(pMostBoWMatchesKF->GetTranslation()),1.0); g2o::Sim3 gScw = gScm*gSmw; // Similarity matrix of current from the world position cv::Mat mScw = Converter::toCvMat(gScw); vector vpMatchedMP; vpMatchedMP.resize(mpCurrentKF->GetMapPointMatches().size(), static_cast(NULL)); int numProjOptMatches = matcher.SearchByProjection(mpCurrentKF, mScw, vpMapPoints, vpMatchedMP, 5, 1.0); //cout <<"BoW: " << numProjOptMatches << " matches after of the Sim3 optimization" << endl; if(numProjOptMatches >= nProjOptMatches) { cout << "BoW: Current KF " << mpCurrentKF->mnId << "; candidate KF " << pKFi->mnId << endl; cout << "BoW: There are " << numProjOptMatches << " matches between them with the optimized Sim3" << endl; int max_x = -1, min_x = 1000000; int max_y = -1, min_y = 1000000; for(MapPoint* pMPi : vpMatchedMP) { if(!pMPi || pMPi->isBad()) { continue; } tuple indexes = pMPi->GetIndexInKeyFrame(pKFi); int index = get<0>(indexes); if(index >= 0) { int coord_x = pKFi->mvKeysUn[index].pt.x; if(coord_x < min_x) { min_x = coord_x; } if(coord_x > max_x) { max_x = coord_x; } int coord_y = pKFi->mvKeysUn[index].pt.y; if(coord_y < min_y) { min_y = coord_y; } if(coord_y > max_y) { max_y = coord_y; } } } //cout << "BoW: Coord in X -> " << min_x << ", " << max_x << "; and Y -> " << min_y << ", " << max_y << endl; //cout << "BoW: features area in X -> " << (max_x - min_x) << " and Y -> " << (max_y - min_y) << endl; int nNumKFs = 0; //vpMatchedMPs = vpMatchedMP; //vpMPs = vpMapPoints; // Check the Sim3 transformation with the current KeyFrame covisibles vector vpCurrentCovKFs = mpCurrentKF->GetBestCovisibilityKeyFrames(nNumCovisibles); //cout << "---" << endl; //cout << "BoW: Geometrical validation" << endl; int j = 0; while(nNumKFs < 3 && jGetPose() * mpCurrentKF->GetPoseInverse(); g2o::Sim3 gSjc(Converter::toMatrix3d(mTjc.rowRange(0, 3).colRange(0, 3)),Converter::toVector3d(mTjc.rowRange(0, 3).col(3)),1.0); g2o::Sim3 gSjw = gSjc * gScw; int numProjMatches_j = 0; vector vpMatchedMPs_j; bool bValid = DetectCommonRegionsFromLastKF(pKFj,pMostBoWMatchesKF, gSjw,numProjMatches_j, vpMapPoints, vpMatchedMPs_j); if(bValid) { //cout << "BoW: KF " << pKFj->mnId << " has " << numProjMatches_j << " matches" << endl; cv::Mat Tc_w = mpCurrentKF->GetPose(); cv::Mat Tw_cj = pKFj->GetPoseInverse(); cv::Mat Tc_cj = Tc_w * Tw_cj; cv::Vec3d vector_dist = Tc_cj.rowRange(0, 3).col(3); cv::Mat Rc_cj = Tc_cj.rowRange(0, 3).colRange(0, 3); double dist = cv::norm(vector_dist); cout << "BoW: KF " << pKFi->mnId << " to KF " << pKFj->mnId << " is separated by " << dist << " meters" << endl; cout << "BoW: Rotation between KF -> " << Rc_cj << endl; vector v_euler = Converter::toEuler(Rc_cj); v_euler[0] *= 180 /3.1415; v_euler[1] *= 180 /3.1415; v_euler[2] *= 180 /3.1415; cout << "BoW: Rotation in angles (x, y, z) -> (" << v_euler[0] << ", " << v_euler[1] << ", " << v_euler[2] << ")" << endl; nNumKFs++; /*if(numProjMatches_j > numProjOptMatches) { pLastCurrentKF = pKFj; g2oScw = gSjw; vpMatchedMPs = vpMatchedMPs_j; }*/ } j++; } if(nNumKFs < 3) { vnStage[index] = 8; vnMatchesStage[index] = nNumKFs; } if(nBestMatchesReproj < numProjOptMatches) { nBestMatchesReproj = numProjOptMatches; nBestNumCoindicendes = nNumKFs; pBestMatchedKF = pMostBoWMatchesKF; g2oBestScw = gScw; vpBestMapPoints = vpMapPoints; vpBestMatchedMapPoints = vpMatchedMP; } } } } } } index++; } if(nBestMatchesReproj > 0) { pLastCurrentKF = mpCurrentKF; nNumCoincidences = nBestNumCoindicendes; pMatchedKF2 = pBestMatchedKF; pMatchedKF2->SetNotErase(); g2oScw = g2oBestScw; vpMPs = vpBestMapPoints; vpMatchedMPs = vpBestMatchedMapPoints; return nNumCoincidences >= 3; } else { int maxStage = -1; int maxMatched; for(int i=0; i maxStage) { maxStage = vnStage[i]; maxMatched = vnMatchesStage[i]; } } // f_succes_pr << mpCurrentKF->mNameFile << " " << std::to_string(maxStage) << endl; // f_succes_pr << "% NumCand: " << std::to_string(numCandidates) << "; matches: " << std::to_string(maxMatched) << endl; } return false; } bool LoopClosing::DetectCommonRegionsFromLastKF(KeyFrame* pCurrentKF, KeyFrame* pMatchedKF, g2o::Sim3 &gScw, int &nNumProjMatches, std::vector &vpMPs, std::vector &vpMatchedMPs) { set spAlreadyMatchedMPs(vpMatchedMPs.begin(), vpMatchedMPs.end()); nNumProjMatches = FindMatchesByProjection(pCurrentKF, pMatchedKF, gScw, spAlreadyMatchedMPs, vpMPs, vpMatchedMPs); //cout << "Projection from last KF with " << nNumProjMatches << " matches" << endl; int nProjMatches = 30; if(nNumProjMatches >= nProjMatches) { /*cout << "PR_From_LastKF: KF " << pCurrentKF->mnId << " has " << nNumProjMatches << " with KF " << pMatchedKF->mnId << endl; int max_x = -1, min_x = 1000000; int max_y = -1, min_y = 1000000; for(MapPoint* pMPi : vpMatchedMPs) { if(!pMPi || pMPi->isBad()) { continue; } tuple indexes = pMPi->GetIndexInKeyFrame(pMatchedKF); int index = get<0>(indexes); if(index >= 0) { int coord_x = pCurrentKF->mvKeysUn[index].pt.x; if(coord_x < min_x) { min_x = coord_x; } if(coord_x > max_x) { max_x = coord_x; } int coord_y = pCurrentKF->mvKeysUn[index].pt.y; if(coord_y < min_y) { min_y = coord_y; } if(coord_y > max_y) { max_y = coord_y; } } }*/ //cout << "PR_From_LastKF: Coord in X -> " << min_x << ", " << max_x << "; and Y -> " << min_y << ", " << max_y << endl; //cout << "PR_From_LastKF: features area in X -> " << (max_x - min_x) << " and Y -> " << (max_y - min_y) << endl; return true; } return false; } int LoopClosing::FindMatchesByProjection(KeyFrame* pCurrentKF, KeyFrame* pMatchedKFw, g2o::Sim3 &g2oScw, set &spMatchedMPinOrigin, vector &vpMapPoints, vector &vpMatchedMapPoints) { int nNumCovisibles = 5; vector vpCovKFm = pMatchedKFw->GetBestCovisibilityKeyFrames(nNumCovisibles); int nInitialCov = vpCovKFm.size(); vpCovKFm.push_back(pMatchedKFw); set spCheckKFs(vpCovKFm.begin(), vpCovKFm.end()); set spCurrentCovisbles = pCurrentKF->GetConnectedKeyFrames(); for(int i=0; i vpKFs = vpCovKFm[i]->GetBestCovisibilityKeyFrames(nNumCovisibles); int nInserted = 0; int j = 0; while(j < vpKFs.size() && nInserted < nNumCovisibles) { if(spCheckKFs.find(vpKFs[j]) == spCheckKFs.end() && spCurrentCovisbles.find(vpKFs[j]) == spCurrentCovisbles.end()) { spCheckKFs.insert(vpKFs[j]); ++nInserted; } ++j; } vpCovKFm.insert(vpCovKFm.end(), vpKFs.begin(), vpKFs.end()); } set spMapPoints; vpMapPoints.clear(); vpMatchedMapPoints.clear(); for(KeyFrame* pKFi : vpCovKFm) { for(MapPoint* pMPij : pKFi->GetMapPointMatches()) { if(!pMPij || pMPij->isBad()) continue; if(spMapPoints.find(pMPij) == spMapPoints.end()) { spMapPoints.insert(pMPij); vpMapPoints.push_back(pMPij); } } } //cout << "Point cloud: " << vpMapPoints.size() << endl; cv::Mat mScw = Converter::toCvMat(g2oScw); ORBmatcher matcher(0.9, true); vpMatchedMapPoints.resize(pCurrentKF->GetMapPointMatches().size(), static_cast(NULL)); int num_matches = matcher.SearchByProjection(pCurrentKF, mScw, vpMapPoints, vpMatchedMapPoints, 3, 1.5); return num_matches; } 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(); mpLocalMapper->EmptyQueue(); // Proccess keyframes in the queue // If a Global Bundle Adjustment is running, abort it cout << "Request GBA abort" << endl; if(isRunningGBA()) { unique_lock lock(mMutexGBA); mbStopGBA = true; mnFullBAIdx++; if(mpThreadGBA) { cout << "GBA running... Abort!" << endl; mpThreadGBA->detach(); delete mpThreadGBA; } } // Wait until Local Mapping has effectively stopped while(!mpLocalMapper->isStopped()) { usleep(1000); } // Ensure current keyframe is updated cout << "start updating connections" << endl; assert(mpCurrentKF->GetMap()->CheckEssentialGraph()); mpCurrentKF->UpdateConnections(); assert(mpCurrentKF->GetMap()->CheckEssentialGraph()); // Retrive keyframes connected to the current keyframe and compute corrected Sim3 pose by propagation mvpCurrentConnectedKFs = mpCurrentKF->GetVectorCovisibleKeyFrames(); mvpCurrentConnectedKFs.push_back(mpCurrentKF); KeyFrameAndPose CorrectedSim3, NonCorrectedSim3; CorrectedSim3[mpCurrentKF]=mg2oLoopScw; cv::Mat Twc = mpCurrentKF->GetPoseInverse(); Map* pLoopMap = mpCurrentKF->GetMap(); { // Get Map Mutex unique_lock lock(pLoopMap->mMutexMapUpdate); const bool bImuInit = pLoopMap->isImuInitialized(); for(vector::iterator vit=mvpCurrentConnectedKFs.begin(), vend=mvpCurrentConnectedKFs.end(); vit!=vend; vit++) { KeyFrame* pKFi = *vit; cv::Mat Tiw = pKFi->GetPose(); if(pKFi!=mpCurrentKF) { 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); g2o::Sim3 g2oCorrectedSiw = g2oSic*mg2oLoopScw; //Pose corrected with the Sim3 of the loop closure 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 cout << "LC: start correcting KeyFrames" << endl; cout << "LC: there are " << CorrectedSim3.size() << " KFs in the local window" << endl; 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(); iMPisBad()) 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 eigP3Dw = Converter::toVector3d(P3Dw); Eigen::Matrix 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(); // cout << "scale for loop-closing: " << s << endl; eigt *=(1./s); //[R t/s;0 1] cv::Mat correctedTiw = Converter::toCvSE3(eigR,eigt); pKFi->SetPose(correctedTiw); // Correct velocity according to orientation correction if(bImuInit) { Eigen::Matrix3d Rcor = eigR.transpose()*g2oSiw.rotation().toRotationMatrix(); pKFi->SetVelocity(Converter::toCvMat(Rcor)*pKFi->GetVelocity()); } // Make sure connections are updated pKFi->UpdateConnections(); } // TODO Check this index increasement mpAtlas->GetCurrentMap()->IncreaseChangeIndex(); cout << "LC: end correcting KeyFrames" << endl; // Start Loop Fusion // Update matched map points and replace if duplicated cout << "LC: start replacing duplicated" << endl; for(size_t i=0; iGetMapPoint(i); if(pCurMP) pCurMP->Replace(pLoopMP); else { mpCurrentKF->AddMapPoint(pLoopMP,i); pLoopMP->AddObservation(mpCurrentKF,i); pLoopMP->ComputeDistinctiveDescriptors(); } } } cout << "LC: end replacing duplicated" << endl; } // Project MapPoints observed in the neighborhood of the loop keyframe // into the current keyframe and neighbors using corrected poses. // Fuse duplications. //cout << "LC: start SearchAndFuse" << endl; SearchAndFuse(CorrectedSim3, mvpLoopMapPoints); //cout << "LC: end SearchAndFuse" << endl; // After the MapPoint fusion, new links in the covisibility graph will appear attaching both sides of the loop //cout << "LC: start updating covisibility graph" << endl; 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); } } //cout << "LC: end updating covisibility graph" << endl; // Optimize graph //cout << "start opt essentialgraph" << endl; bool bFixedScale = mbFixScale; // TODO CHECK; Solo para el monocular inertial if(mpTracker->mSensor==System::IMU_MONOCULAR && !mpCurrentKF->GetMap()->GetIniertialBA2()) bFixedScale=false; //cout << "Optimize essential graph" << endl; if(pLoopMap->IsInertial() && pLoopMap->isImuInitialized()) { //cout << "With 4DoF" << endl; Optimizer::OptimizeEssentialGraph4DoF(pLoopMap, mpLoopMatchedKF, mpCurrentKF, NonCorrectedSim3, CorrectedSim3, LoopConnections); } else { //cout << "With 7DoF" << endl; Optimizer::OptimizeEssentialGraph(pLoopMap, mpLoopMatchedKF, mpCurrentKF, NonCorrectedSim3, CorrectedSim3, LoopConnections, bFixedScale); } //cout << "Optimize essential graph finished" << endl; //usleep(5*1000*1000); mpAtlas->InformNewBigChange(); // Add loop edge mpLoopMatchedKF->AddLoopEdge(mpCurrentKF); mpCurrentKF->AddLoopEdge(mpLoopMatchedKF); // Launch a new thread to perform Global Bundle Adjustment (Only if few keyframes, if not it would take too much time) if(!pLoopMap->isImuInitialized() || (pLoopMap->KeyFramesInMap()<200 && mpAtlas->CountMaps()==1)) { mbRunningGBA = true; mbFinishedGBA = false; mbStopGBA = false; mpThreadGBA = new thread(&LoopClosing::RunGlobalBundleAdjustment, this, pLoopMap, mpCurrentKF->mnId); } // Loop closed. Release Local Mapping. mpLocalMapper->Release(); mLastLoopKFid = mpCurrentKF->mnId; //TODO old varible, it is not use in the new algorithm } void LoopClosing::MergeLocal() { Verbose::PrintMess("MERGE-VISUAL: Merge Visual detected!!!!", Verbose::VERBOSITY_NORMAL); //mpTracker->SetStepByStep(true); int numTemporalKFs = 15; //TODO (set by parameter): Temporal KFs in the local window if the map is inertial. //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-VISUAL: 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-VISUAL: Request Stop Local Mapping", Verbose::VERBOSITY_DEBUG); mpLocalMapper->RequestStop(); // Wait until Local Mapping has effectively stopped while(!mpLocalMapper->isStopped()) { usleep(1000); } Verbose::PrintMess("MERGE-VISUAL: 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(); Verbose::PrintMess("MERGE-VISUAL: Initially there are " + to_string(pCurrentMap->KeyFramesInMap()) + " KFs and " + to_string(pCurrentMap->MapPointsInMap()) + " MPs in the active map ", Verbose::VERBOSITY_DEBUG); Verbose::PrintMess("MERGE-VISUAL: Initially there are " + to_string(pMergeMap->KeyFramesInMap()) + " KFs and " + to_string(pMergeMap->MapPointsInMap()) + " MPs in the matched map ", Verbose::VERBOSITY_DEBUG); //vector vpMergeKFs = pMergeMap->GetAllKeyFrames(); //// // 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()); Verbose::PrintMess("MERGE-VISUAL: Initial number of KFs in local window from active map: " + to_string(spLocalWindowKFs.size()), Verbose::VERBOSITY_DEBUG); 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++; } Verbose::PrintMess("MERGE-VISUAL: Last number of KFs in local window from the active map: " + to_string(spLocalWindowKFs.size()), Verbose::VERBOSITY_DEBUG); //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()); } Verbose::PrintMess("MERGE-VISUAL: Number of MPs in local window from active map: " + to_string(spLocalWindowMPs.size()), Verbose::VERBOSITY_DEBUG); Verbose::PrintMess("MERGE-VISUAL: Number of MPs in the active map: " + to_string(pCurrentMap->GetAllMapPoints().size()), Verbose::VERBOSITY_DEBUG); Verbose::PrintMess("-------", Verbose::VERBOSITY_DEBUG); 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()); Verbose::PrintMess("MERGE-VISUAL: Initial number of KFs in the local window from matched map: " + to_string(spMergeConnectedKFs.size()), Verbose::VERBOSITY_DEBUG); 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++; } Verbose::PrintMess("MERGE-VISUAL: Last number of KFs in the localwindow from matched map: " + to_string(spMergeConnectedKFs.size()), Verbose::VERBOSITY_DEBUG); 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; //TODO Check the transformation KeyFrameAndPose vCorrectedSim3, vNonCorrectedSim3; vCorrectedSim3[mpCurrentKF]=g2oCorrectedScw; vNonCorrectedSim3[mpCurrentKF]=g2oNonCorrectedScw; //TODO Time test #ifdef COMPILEDWITHC11 std::chrono::steady_clock::time_point timeStartTransfMerge = std::chrono::steady_clock::now(); #else std::chrono::monotonic_clock::time_point timeStartTransfMerge = std::chrono::monotonic_clock::now(); #endif for(KeyFrame* pKFi : spLocalWindowKFs) { if(!pKFi || pKFi->isBad()) { Verbose::PrintMess("Bad KF in correction", Verbose::VERBOSITY_DEBUG); continue; } if(pKFi->GetMap() != pCurrentMap) Verbose::PrintMess("Other map KF, this should't happen", Verbose::VERBOSITY_DEBUG); 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] //cout << "R: " << mg2oMergeScw.rotation().toRotationMatrix() << endl; //cout << "angle: " << 180*LogSO3(mg2oMergeScw.rotation().toRotationMatrix())/3.14 << endl; //cout << "t: " << mg2oMergeScw.translation() << endl; cv::Mat correctedTiw = Converter::toCvSE3(eigR,eigt); pKFi->mTcwMerge = correctedTiw; //pKFi->SetPose(correctedTiw); // Make sure connections are updated //pKFi->UpdateMap(pMergeMap); //pMergeMap->AddKeyFrame(pKFi); //pCurrentMap->EraseKeyFrame(pKFi); //cout << "After -> Map current: " << pCurrentMap << "; New map: " << pKFi->GetMap() << endl; if(pCurrentMap->isImuInitialized()) { Eigen::Matrix3d Rcor = eigR.transpose()*vNonCorrectedSim3[pKFi].rotation().toRotationMatrix(); pKFi->mVwbMerge = Converter::toCvMat(Rcor)*pKFi->GetVelocity(); //pKFi->SetVelocity(Converter::toCvMat(Rcor)*pKFi->GetVelocity()); // TODO: should add here scale s } //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 eigP3Dw = Converter::toVector3d(P3Dw); Eigen::Matrix 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; //cout << "Rcor: " << Rcor << endl; //cout << "Normal: " << pMPi->GetNormal() << endl; pMPi->mNormalVectorMerge = Converter::toCvMat(Rcor) * pMPi->GetNormal(); //pMPi->SetWorldPos(cvCorrectedP3Dw); //pMPi->UpdateMap(pMergeMap); //pMergeMap->AddMapPoint(pMPi); //pCurrentMap->EraseMapPoint(pMPi); //pMPi->UpdateNormalAndDepth(); } #ifdef COMPILEDWITHC11 std::chrono::steady_clock::time_point timeFinishTransfMerge = std::chrono::steady_clock::now(); #else std::chrono::monotonic_clock::time_point timeFinishTransfMerge = std::chrono::monotonic_clock::now(); #endif std::chrono::duration timeTransfMerge = timeFinishTransfMerge - timeStartTransfMerge; // Time in milliseconds Verbose::PrintMess("MERGE-VISUAL: TRANSF ms: " + to_string(timeTransfMerge.count()), Verbose::VERBOSITY_DEBUG); //TODO Time test #ifdef COMPILEDWITHC11 std::chrono::steady_clock::time_point timeStartCritMerge = std::chrono::steady_clock::now(); #else std::chrono::monotonic_clock::time_point timeStartCritMerge = std::chrono::monotonic_clock::now(); #endif { 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()) { //cout << "Bad KF in correction" << endl; 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); //pMPi->UpdateNormalAndDepth(); } mpAtlas->ChangeMap(pMergeMap); mpAtlas->SetMapBad(pCurrentMap); pMergeMap->IncreaseChangeIndex(); //TODO for debug pMergeMap->ChangeId(pCurrentMap->GetId()); } #ifdef COMPILEDWITHC11 std::chrono::steady_clock::time_point timeFinishCritMerge = std::chrono::steady_clock::now(); #else std::chrono::monotonic_clock::time_point timeFinishCritMerge = std::chrono::monotonic_clock::now(); #endif std::chrono::duration timeCritMerge = timeFinishCritMerge - timeStartCritMerge; // Time in milliseconds Verbose::PrintMess("MERGE-VISUAL: New current map: " + to_string(pMergeMap->GetId()), Verbose::VERBOSITY_DEBUG); Verbose::PrintMess("MERGE-VISUAL: CRITICAL ms: " + to_string(timeCritMerge.count()), Verbose::VERBOSITY_DEBUG); Verbose::PrintMess("MERGE-VISUAL: LOCAL MAPPING number of KFs: " + to_string(mpLocalMapper->KeyframesInQueue()), Verbose::VERBOSITY_DEBUG); //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 /*&& spLocalWindowKFs.find(pNewChild) != spLocalWindowKFs.end()*/) { pNewChild->EraseChild(pNewParent); // We remove the relation between the old parent and the new for avoid loop KeyFrame * pOldParent = pNewChild->GetParent(); pNewChild->ChangeParent(pNewParent); //cout << "The new parent of KF " << pNewChild->mnId << " was " << pNewChild->GetParent()->mnId << endl; pNewParent = pNewChild; pNewChild = pOldParent; } //Update the connections between the local window mpMergeMatchedKF->UpdateConnections(); //cout << "MERGE-VISUAL: Essential graph rebuilded" << endl; //std::copy(spMapPointCurrent.begin(), spMapPointCurrent.end(), std::back_inserter(vpCheckFuseMapPoint)); vpMergeConnectedKFs = mpMergeMatchedKF->GetVectorCovisibleKeyFrames(); vpMergeConnectedKFs.push_back(mpMergeMatchedKF); vpCheckFuseMapPoint.reserve(spMapPointMerge.size()); std::copy(spMapPointMerge.begin(), spMapPointMerge.end(), std::back_inserter(vpCheckFuseMapPoint)); //TODO Time test #ifdef COMPILEDWITHC11 std::chrono::steady_clock::time_point timeStartFuseMerge = std::chrono::steady_clock::now(); #else std::chrono::monotonic_clock::time_point timeStartFuseMerge = std::chrono::monotonic_clock::now(); #endif // Project MapPoints observed in the neighborhood of the merge keyframe // into the current keyframe and neighbors using corrected poses. // Fuse duplications. SearchAndFuse(vCorrectedSim3, vpCheckFuseMapPoint); #ifdef COMPILEDWITHC11 std::chrono::steady_clock::time_point timeFinishFuseMerge = std::chrono::steady_clock::now(); #else std::chrono::monotonic_clock::time_point timeFinishFuseMerge = std::chrono::monotonic_clock::now(); #endif std::chrono::duration timeFuseMerge = timeFinishFuseMerge - timeStartFuseMerge; // Time in milliseconds Verbose::PrintMess("MERGE-VISUAL: FUSE DUPLICATED ms: " + to_string(timeFuseMerge.count()), Verbose::VERBOSITY_DEBUG); // Update connectivity Verbose::PrintMess("MERGE-VISUAL: Init to update connections in the welding area", Verbose::VERBOSITY_DEBUG); for(KeyFrame* pKFi : spLocalWindowKFs) { if(!pKFi || pKFi->isBad()) continue; pKFi->UpdateConnections(); } for(KeyFrame* pKFi : spMergeConnectedKFs) { if(!pKFi || pKFi->isBad()) continue; pKFi->UpdateConnections(); } //CheckObservations(spLocalWindowKFs, spMergeConnectedKFs); Verbose::PrintMess("MERGE-VISUAL: Finish to update connections in the welding area", Verbose::VERBOSITY_DEBUG); bool bStop = false; Verbose::PrintMess("MERGE-VISUAL: 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) { Verbose::PrintMess("MERGE-VISUAL: Visual-Inertial", Verbose::VERBOSITY_DEBUG); Optimizer::MergeInertialBA(mpLocalMapper->GetCurrKF(),mpMergeMatchedKF,&bStop, mpCurrentKF->GetMap(),vCorrectedSim3); } else { Verbose::PrintMess("MERGE-VISUAL: Visual", Verbose::VERBOSITY_DEBUG); Verbose::PrintMess("MERGE-VISUAL: Local current window->" + to_string(vpLocalCurrentWindowKFs.size()) + "; Local merge window->" + to_string(vpMergeConnectedKFs.size()), Verbose::VERBOSITY_DEBUG); Optimizer::LocalBundleAdjustment(mpCurrentKF, vpLocalCurrentWindowKFs, vpMergeConnectedKFs,&bStop); } // Loop closed. Release Local Mapping. mpLocalMapper->Release(); //return; Verbose::PrintMess("MERGE-VISUAL: 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-VISUAL: There are not KFs outside of the welding area", Verbose::VERBOSITY_DEBUG); } else { Verbose::PrintMess("MERGE-VISUAL: 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 eigP3Dw = Converter::toVector3d(P3Dw); Eigen::Matrix eigCorrectedP3Dw = g2oCorrectedSwi.map(g2oNonCorrectedSiw.map(eigP3Dw)); cv::Mat cvCorrectedP3Dw = Converter::toCvMat(eigCorrectedP3Dw); pMPi->SetWorldPos(cvCorrectedP3Dw); pMPi->UpdateNormalAndDepth(); } } } Verbose::PrintMess("MERGE-VISUAL: Apply transformation to all elements of the old map", Verbose::VERBOSITY_DEBUG); mpLocalMapper->RequestStop(); // Wait until Local Mapping has effectively stopped while(!mpLocalMapper->isStopped()) { usleep(1000); } Verbose::PrintMess("MERGE-VISUAL: Local Map stopped", Verbose::VERBOSITY_DEBUG); // 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 Verbose::PrintMess("MERGE-VISUAL: There are " + to_string(pMergeMap->KeyFramesInMap()) + " KFs in the map", Verbose::VERBOSITY_DEBUG); Verbose::PrintMess("MERGE-VISUAL: It will be inserted " + to_string(vpCurrentMapKFs.size()) + " KFs in the map", Verbose::VERBOSITY_DEBUG); 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); } Verbose::PrintMess("MERGE-VISUAL: There are " + to_string(pMergeMap->MapPointsInMap()) + " MPs in the map", Verbose::VERBOSITY_DEBUG); Verbose::PrintMess("MERGE-VISUAL: It will be inserted " + to_string(vpCurrentMapMPs.size()) + " MPs in the map", Verbose::VERBOSITY_DEBUG); for(MapPoint* pMPi : vpCurrentMapMPs) { if(!pMPi || pMPi->isBad()) continue; pMPi->UpdateMap(pMergeMap); pMergeMap->AddMapPoint(pMPi); pCurrentMap->EraseMapPoint(pMPi); } Verbose::PrintMess("MERGE-VISUAL: There are " + to_string(pMergeMap->MapPointsInMap()) + " MPs in the map", Verbose::VERBOSITY_DEBUG); } Verbose::PrintMess("MERGE-VISUAL: Optimaze the essential graph", Verbose::VERBOSITY_DEBUG); } mpLocalMapper->Release(); Verbose::PrintMess("MERGE-VISUAL: Finally there are " + to_string(pMergeMap->KeyFramesInMap()) + "KFs and " + to_string(pMergeMap->MapPointsInMap()) + " MPs in the complete map ", Verbose::VERBOSITY_DEBUG); Verbose::PrintMess("MERGE-VISUAL: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(); } void LoopClosing::printReprojectionError(set &spLocalWindowKFs, KeyFrame* mpCurrentKF, string &name) { string path_imgs = "./test_Reproj/"; for(KeyFrame* pKFi : spLocalWindowKFs) { //cout << "KF " << pKFi->mnId << endl; cv::Mat img_i = cv::imread(pKFi->mNameFile, CV_LOAD_IMAGE_UNCHANGED); //cout << "Image -> " << img_i.cols << ", " << img_i.rows << endl; cv::cvtColor(img_i, img_i, CV_GRAY2BGR); //cout << "Change of color in the image " << endl; vector vpMPs = pKFi->GetMapPointMatches(); int num_points = 0; for(int j=0; jisBad()) { continue; } cv::KeyPoint point_img = pKFi->mvKeysUn[j]; cv::Point2f reproj_p; float u, v; bool bIsInImage = pKFi->ProjectPointUnDistort(pMPij, reproj_p, u, v); if(bIsInImage){ //cout << "Reproj in the image" << endl; cv::circle(img_i, point_img.pt, 1/*point_img.octave*/, cv::Scalar(0, 255, 0)); cv::line(img_i, point_img.pt, reproj_p, cv::Scalar(0, 0, 255)); num_points++; } else { //cout << "Reproj out of the image" << endl; cv::circle(img_i, point_img.pt, point_img.octave, cv::Scalar(0, 0, 255)); } } //cout << "Image painted" << endl; string filename_img = path_imgs + "KF" + to_string(mpCurrentKF->mnId) + "_" + to_string(pKFi->mnId) + name + "points" + to_string(num_points) + ".png"; cv::imwrite(filename_img, img_i); } } void LoopClosing::MergeLocal2() { cout << "Merge detected!!!!" << endl; int numTemporalKFs = 11; //TODO (set by parameter): Temporal KFs in the local window if the map is inertial. //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; KeyFrameAndPose CorrectedSim3, NonCorrectedSim3; // NonCorrectedSim3[mpCurrentKF]=mg2oLoopScw; // 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; cout << "Check Full Bundle Adjustment" << endl; // 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; } cout << "Request Stop Local Mapping" << endl; mpLocalMapper->RequestStop(); // Wait until Local Mapping has effectively stopped while(!mpLocalMapper->isStopped()) { usleep(1000); } cout << "Local Map stopped" << endl; Map* pCurrentMap = mpCurrentKF->GetMap(); Map* pMergeMap = mpMergeMatchedKF->GetMap(); { float s_on = mSold_new.scale(); cv::Mat R_on = Converter::toCvMat(mSold_new.rotation().toRotationMatrix()); cv::Mat t_on = Converter::toCvMat(mSold_new.translation()); unique_lock lock(mpAtlas->GetCurrentMap()->mMutexMapUpdate); cout << "KFs before empty: " << mpAtlas->GetCurrentMap()->KeyFramesInMap() << endl; mpLocalMapper->EmptyQueue(); cout << "KFs after empty: " << mpAtlas->GetCurrentMap()->KeyFramesInMap() << endl; std::chrono::steady_clock::time_point t2 = std::chrono::steady_clock::now(); cout << "updating active map to merge reference" << endl; cout << "curr merge KF id: " << mpCurrentKF->mnId << endl; cout << "curr tracking KF id: " << mpTracker->GetLastKeyFrame()->mnId << endl; bool bScaleVel=false; if(s_on!=1) bScaleVel=true; mpAtlas->GetCurrentMap()->ApplyScaledRotation(R_on,s_on,bScaleVel,t_on); mpTracker->UpdateFrameIMU(s_on,mpCurrentKF->GetImuBias(),mpTracker->GetLastKeyFrame()); std::chrono::steady_clock::time_point t3 = std::chrono::steady_clock::now(); } const int numKFnew=pCurrentMap->KeyFramesInMap(); if((mpTracker->mSensor==System::IMU_MONOCULAR || mpTracker->mSensor==System::IMU_STEREO)&& !pCurrentMap->GetIniertialBA2()){ // Map is not completly initialized Eigen::Vector3d bg, ba; bg << 0., 0., 0.; ba << 0., 0., 0.; Optimizer::InertialOptimization(pCurrentMap,bg,ba); IMU::Bias b (ba[0],ba[1],ba[2],bg[0],bg[1],bg[2]); unique_lock lock(mpAtlas->GetCurrentMap()->mMutexMapUpdate); mpTracker->UpdateFrameIMU(1.0f,b,mpTracker->GetLastKeyFrame()); // Set map initialized pCurrentMap->SetIniertialBA2(); pCurrentMap->SetIniertialBA1(); pCurrentMap->SetImuInitialized(); } cout << "MergeMap init ID: " << pMergeMap->GetInitKFid() << " CurrMap init ID: " << pCurrentMap->GetInitKFid() << endl; // Load KFs and MPs from merge map cout << "updating current map" << endl; { // Get Merge Map Mutex (This section stops tracking!!) 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 vector vpMergeMapKFs = pMergeMap->GetAllKeyFrames(); vector vpMergeMapMPs = pMergeMap->GetAllMapPoints(); for(KeyFrame* pKFi : vpMergeMapKFs) { if(!pKFi || pKFi->isBad() || pKFi->GetMap() != pMergeMap) { continue; } // Make sure connections are updated pKFi->UpdateMap(pCurrentMap); pCurrentMap->AddKeyFrame(pKFi); pMergeMap->EraseKeyFrame(pKFi); } for(MapPoint* pMPi : vpMergeMapMPs) { if(!pMPi || pMPi->isBad() || pMPi->GetMap() != pMergeMap) continue; pMPi->UpdateMap(pCurrentMap); pCurrentMap->AddMapPoint(pMPi); pMergeMap->EraseMapPoint(pMPi); } // Save non corrected poses (already merged maps) vector vpKFs = pCurrentMap->GetAllKeyFrames(); for(KeyFrame* pKFi : vpKFs) { 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); NonCorrectedSim3[pKFi]=g2oSiw; } } cout << "MergeMap init ID: " << pMergeMap->GetInitKFid() << " CurrMap init ID: " << pCurrentMap->GetInitKFid() << endl; cout << "end updating current map" << endl; // Critical zone bool good = pCurrentMap->CheckEssentialGraph(); /*if(!good) cout << "BAD ESSENTIAL GRAPH!!" << endl;*/ cout << "Update essential graph" << endl; // mpCurrentKF->UpdateConnections(); // to put at false mbFirstConnection pMergeMap->GetOriginKF()->SetFirstConnection(false); pNewChild = mpMergeMatchedKF->GetParent(); // Old parent, it will be the new child of this KF pNewParent = mpMergeMatchedKF; // Old child, now it will be the parent of its own parent(we need eliminate this KF from children list in its old parent) mpMergeMatchedKF->ChangeParent(mpCurrentKF); 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; } cout << "MergeMap init ID: " << pMergeMap->GetInitKFid() << " CurrMap init ID: " << pCurrentMap->GetInitKFid() << endl; cout << "end update essential graph" << endl; good = pCurrentMap->CheckEssentialGraph(); if(!good) cout << "BAD ESSENTIAL GRAPH 1!!" << endl; cout << "Update relationship between KFs" << endl; vector vpCheckFuseMapPoint; // MapPoint vector from current map to allow to fuse duplicated points with the old map (merge) vector vpCurrentConnectedKFs; mvpMergeConnectedKFs.push_back(mpMergeMatchedKF); vector aux = mpMergeMatchedKF->GetVectorCovisibleKeyFrames(); mvpMergeConnectedKFs.insert(mvpMergeConnectedKFs.end(), aux.begin(), aux.end()); if (mvpMergeConnectedKFs.size()>6) mvpMergeConnectedKFs.erase(mvpMergeConnectedKFs.begin()+6,mvpMergeConnectedKFs.end()); /*mvpMergeConnectedKFs = mpMergeMatchedKF->GetVectorCovisibleKeyFrames(); mvpMergeConnectedKFs.push_back(mpMergeMatchedKF);*/ mpCurrentKF->UpdateConnections(); vpCurrentConnectedKFs.push_back(mpCurrentKF); /*vpCurrentConnectedKFs = mpCurrentKF->GetVectorCovisibleKeyFrames(); vpCurrentConnectedKFs.push_back(mpCurrentKF);*/ aux = mpCurrentKF->GetVectorCovisibleKeyFrames(); vpCurrentConnectedKFs.insert(vpCurrentConnectedKFs.end(), aux.begin(), aux.end()); if (vpCurrentConnectedKFs.size()>6) vpCurrentConnectedKFs.erase(vpCurrentConnectedKFs.begin()+6,vpCurrentConnectedKFs.end()); set spMapPointMerge; for(KeyFrame* pKFi : mvpMergeConnectedKFs) { set vpMPs = pKFi->GetMapPoints(); spMapPointMerge.insert(vpMPs.begin(),vpMPs.end()); if(spMapPointMerge.size()>1000) break; } cout << "vpCurrentConnectedKFs.size() " << vpCurrentConnectedKFs.size() << endl; cout << "mvpMergeConnectedKFs.size() " << mvpMergeConnectedKFs.size() << endl; cout << "spMapPointMerge.size() " << spMapPointMerge.size() << endl; vpCheckFuseMapPoint.reserve(spMapPointMerge.size()); std::copy(spMapPointMerge.begin(), spMapPointMerge.end(), std::back_inserter(vpCheckFuseMapPoint)); cout << "Finished to update relationship between KFs" << endl; cout << "MergeMap init ID: " << pMergeMap->GetInitKFid() << " CurrMap init ID: " << pCurrentMap->GetInitKFid() << endl; good = pCurrentMap->CheckEssentialGraph(); if(!good) cout << "BAD ESSENTIAL GRAPH 2!!" << endl; cout << "start SearchAndFuse" << endl; SearchAndFuse(vpCurrentConnectedKFs, vpCheckFuseMapPoint); cout << "end SearchAndFuse" << endl; cout << "MergeMap init ID: " << pMergeMap->GetInitKFid() << " CurrMap init ID: " << pCurrentMap->GetInitKFid() << endl; good = pCurrentMap->CheckEssentialGraph(); if(!good) cout << "BAD ESSENTIAL GRAPH 3!!" << endl; cout << "Init to update connections" << endl; for(KeyFrame* pKFi : vpCurrentConnectedKFs) { if(!pKFi || pKFi->isBad()) continue; pKFi->UpdateConnections(); } for(KeyFrame* pKFi : mvpMergeConnectedKFs) { if(!pKFi || pKFi->isBad()) continue; pKFi->UpdateConnections(); } cout << "end update connections" << endl; cout << "MergeMap init ID: " << pMergeMap->GetInitKFid() << " CurrMap init ID: " << pCurrentMap->GetInitKFid() << endl; good = pCurrentMap->CheckEssentialGraph(); if(!good) cout << "BAD ESSENTIAL GRAPH 4!!" << endl; // TODO Check: If new map is too small, we suppose that not informaiton can be propagated from new to old map if (numKFnew<10){ mpLocalMapper->Release(); return; } good = pCurrentMap->CheckEssentialGraph(); if(!good) cout << "BAD ESSENTIAL GRAPH 5!!" << endl; // Perform BA bool bStopFlag=false; KeyFrame* pCurrKF = mpTracker->GetLastKeyFrame(); cout << "start MergeInertialBA" << endl; Optimizer::MergeInertialBA(pCurrKF, mpMergeMatchedKF, &bStopFlag, pCurrentMap,CorrectedSim3); cout << "end MergeInertialBA" << endl; good = pCurrentMap->CheckEssentialGraph(); if(!good) cout << "BAD ESSENTIAL GRAPH 6!!" << endl; // Release Local Mapping. mpLocalMapper->Release(); return; } void LoopClosing::CheckObservations(set &spKFsMap1, set &spKFsMap2) { cout << "----------------------" << endl; for(KeyFrame* pKFi1 : spKFsMap1) { map mMatchedMP; set spMPs = pKFi1->GetMapPoints(); for(MapPoint* pMPij : spMPs) { if(!pMPij || pMPij->isBad()) { continue; } map> mMPijObs = pMPij->GetObservations(); for(KeyFrame* pKFi2 : spKFsMap2) { if(mMPijObs.find(pKFi2) != mMPijObs.end()) { if(mMatchedMP.find(pKFi2) != mMatchedMP.end()) { mMatchedMP[pKFi2] = mMatchedMP[pKFi2] + 1; } else { mMatchedMP[pKFi2] = 1; } } } } if(mMatchedMP.size() == 0) { cout << "CHECK-OBS: KF " << pKFi1->mnId << " has not any matched MP with the other map" << endl; } else { cout << "CHECK-OBS: KF " << pKFi1->mnId << " has matched MP with " << mMatchedMP.size() << " KF from the other map" << endl; for(pair matchedKF : mMatchedMP) { cout << " -KF: " << matchedKF.first->mnId << ", Number of matches: " << matchedKF.second << endl; } } } cout << "----------------------" << endl; } void LoopClosing::SearchAndFuse(const KeyFrameAndPose &CorrectedPosesMap, vector &vpMapPoints) { ORBmatcher matcher(0.8); int total_replaces = 0; cout << "FUSE: Initially there are " << vpMapPoints.size() << " MPs" << endl; cout << "FUSE: Intially there are " << CorrectedPosesMap.size() << " KFs" << endl; for(KeyFrameAndPose::const_iterator mit=CorrectedPosesMap.begin(), mend=CorrectedPosesMap.end(); mit!=mend;mit++) { int num_replaces = 0; KeyFrame* pKFi = mit->first; Map* pMap = pKFi->GetMap(); g2o::Sim3 g2oScw = mit->second; cv::Mat cvScw = Converter::toCvMat(g2oScw); vector vpReplacePoints(vpMapPoints.size(),static_cast(NULL)); int numFused = matcher.Fuse(pKFi,cvScw,vpMapPoints,4,vpReplacePoints); // Get Map Mutex unique_lock lock(pMap->mMutexMapUpdate); const int nLP = vpMapPoints.size(); for(int i=0; iReplace(vpMapPoints[i]); } } total_replaces += num_replaces; } cout << "FUSE: " << total_replaces << " MPs had been fused" << endl; } void LoopClosing::SearchAndFuse(const vector &vConectedKFs, vector &vpMapPoints) { ORBmatcher matcher(0.8); int total_replaces = 0; cout << "FUSE-POSE: Initially there are " << vpMapPoints.size() << " MPs" << endl; cout << "FUSE-POSE: Intially there are " << vConectedKFs.size() << " KFs" << endl; for(auto mit=vConectedKFs.begin(), mend=vConectedKFs.end(); mit!=mend;mit++) { int num_replaces = 0; KeyFrame* pKF = (*mit); Map* pMap = pKF->GetMap(); cv::Mat cvScw = pKF->GetPose(); vector vpReplacePoints(vpMapPoints.size(),static_cast(NULL)); matcher.Fuse(pKF,cvScw,vpMapPoints,4,vpReplacePoints); // Get Map Mutex unique_lock lock(pMap->mMutexMapUpdate); const int nLP = vpMapPoints.size(); for(int i=0; iReplace(vpMapPoints[i]); } } cout << "FUSE-POSE: KF " << pKF->mnId << " ->" << num_replaces << " MPs fused" << endl; total_replaces += num_replaces; } cout << "FUSE-POSE: " << total_replaces << " MPs had been fused" << endl; } void LoopClosing::RequestReset() { { unique_lock lock(mMutexReset); mbResetRequested = true; } while(1) { { unique_lock lock2(mMutexReset); if(!mbResetRequested) break; } usleep(5000); } } void LoopClosing::RequestResetActiveMap(Map *pMap) { { unique_lock lock(mMutexReset); mbResetActiveMapRequested = true; mpMapToReset = pMap; } while(1) { { unique_lock lock2(mMutexReset); if(!mbResetActiveMapRequested) break; } usleep(3000); } } void LoopClosing::ResetIfRequested() { unique_lock lock(mMutexReset); if(mbResetRequested) { cout << "Loop closer reset requested..." << endl; mlpLoopKeyFrameQueue.clear(); mLastLoopKFid=0; //TODO old variable, it is not use in the new algorithm mbResetRequested=false; mbResetActiveMapRequested = false; } else if(mbResetActiveMapRequested) { for (list::const_iterator it=mlpLoopKeyFrameQueue.begin(); it != mlpLoopKeyFrameQueue.end();) { KeyFrame* pKFi = *it; if(pKFi->GetMap() == mpMapToReset) { it = mlpLoopKeyFrameQueue.erase(it); } else ++it; } mLastLoopKFid=mpAtlas->GetLastInitKFid(); //TODO old variable, it is not use in the new algorithm mbResetActiveMapRequested=false; } } void LoopClosing::RunGlobalBundleAdjustment(Map* pActiveMap, unsigned long nLoopKF) { Verbose::PrintMess("Starting Global Bundle Adjustment", Verbose::VERBOSITY_NORMAL); const bool bImuInit = pActiveMap->isImuInitialized(); if(!bImuInit) Optimizer::GlobalBundleAdjustemnt(pActiveMap,10,&mbStopGBA,nLoopKF,false); else Optimizer::FullInertialBA(pActiveMap,7,false,nLoopKF,&mbStopGBA); int idx = mnFullBAIdx; // Optimizer::GlobalBundleAdjustemnt(mpMap,10,&mbStopGBA,nLoopKF,false); // Update all MapPoints and KeyFrames // Local Mapping was active during BA, that means that there might be new keyframes // not included in the Global BA and they are not consistent with the updated map. // We need to propagate the correction through the spanning tree { unique_lock lock(mMutexGBA); if(idx!=mnFullBAIdx) return; if(!bImuInit && pActiveMap->isImuInitialized()) return; if(!mbStopGBA) { Verbose::PrintMess("Global Bundle Adjustment finished", Verbose::VERBOSITY_NORMAL); Verbose::PrintMess("Updating map ...", Verbose::VERBOSITY_NORMAL); mpLocalMapper->RequestStop(); // Wait until Local Mapping has effectively stopped while(!mpLocalMapper->isStopped() && !mpLocalMapper->isFinished()) { usleep(1000); } // Get Map Mutex unique_lock lock(pActiveMap->mMutexMapUpdate); // cout << "LC: Update Map Mutex adquired" << endl; //pActiveMap->PrintEssentialGraph(); // Correct keyframes starting at map first keyframe list lpKFtoCheck(pActiveMap->mvpKeyFrameOrigins.begin(),pActiveMap->mvpKeyFrameOrigins.end()); while(!lpKFtoCheck.empty()) { KeyFrame* pKF = lpKFtoCheck.front(); const set sChilds = pKF->GetChilds(); //cout << "---Updating KF " << pKF->mnId << " with " << sChilds.size() << " childs" << endl; //cout << " KF mnBAGlobalForKF: " << pKF->mnBAGlobalForKF << endl; cv::Mat Twc = pKF->GetPoseInverse(); //cout << "Twc: " << Twc << endl; //cout << "GBA: Correct KeyFrames" << endl; for(set::const_iterator sit=sChilds.begin();sit!=sChilds.end();sit++) { KeyFrame* pChild = *sit; if(!pChild || pChild->isBad()) continue; if(pChild->mnBAGlobalForKF!=nLoopKF) { //cout << "++++New child with flag " << pChild->mnBAGlobalForKF << "; LoopKF: " << nLoopKF << endl; //cout << " child id: " << pChild->mnId << endl; cv::Mat Tchildc = pChild->GetPose()*Twc; //cout << "Child pose: " << Tchildc << endl; //cout << "pKF->mTcwGBA: " << pKF->mTcwGBA << endl; pChild->mTcwGBA = Tchildc*pKF->mTcwGBA;//*Tcorc*pKF->mTcwGBA; cv::Mat Rcor = pChild->mTcwGBA.rowRange(0,3).colRange(0,3).t()*pChild->GetRotation(); if(!pChild->GetVelocity().empty()){ //cout << "Child velocity: " << pChild->GetVelocity() << endl; pChild->mVwbGBA = Rcor*pChild->GetVelocity(); } else Verbose::PrintMess("Child velocity empty!! ", Verbose::VERBOSITY_NORMAL); //cout << "Child bias: " << pChild->GetImuBias() << endl; pChild->mBiasGBA = pChild->GetImuBias(); pChild->mnBAGlobalForKF=nLoopKF; } lpKFtoCheck.push_back(pChild); } //cout << "-------Update pose" << endl; pKF->mTcwBefGBA = pKF->GetPose(); //cout << "pKF->mTcwBefGBA: " << pKF->mTcwBefGBA << endl; pKF->SetPose(pKF->mTcwGBA); /*cv::Mat Tco_cn = pKF->mTcwBefGBA * pKF->mTcwGBA.inv(); cv::Vec3d trasl = Tco_cn.rowRange(0,3).col(3); double dist = cv::norm(trasl); cout << "GBA: KF " << pKF->mnId << " had been moved " << dist << " meters" << endl; double desvX = 0; double desvY = 0; double desvZ = 0; if(pKF->mbHasHessian) { cv::Mat hessianInv = pKF->mHessianPose.inv(); double covX = hessianInv.at(3,3); desvX = std::sqrt(covX); double covY = hessianInv.at(4,4); desvY = std::sqrt(covY); double covZ = hessianInv.at(5,5); desvZ = std::sqrt(covZ); pKF->mbHasHessian = false; } if(dist > 1) { cout << "--To much distance correction: It has " << pKF->GetConnectedKeyFrames().size() << " connected KFs" << endl; cout << "--It has " << pKF->GetCovisiblesByWeight(80).size() << " connected KF with 80 common matches or more" << endl; cout << "--It has " << pKF->GetCovisiblesByWeight(50).size() << " connected KF with 50 common matches or more" << endl; cout << "--It has " << pKF->GetCovisiblesByWeight(20).size() << " connected KF with 20 common matches or more" << endl; cout << "--STD in meters(x, y, z): " << desvX << ", " << desvY << ", " << desvZ << endl; string strNameFile = pKF->mNameFile; cv::Mat imLeft = cv::imread(strNameFile, CV_LOAD_IMAGE_UNCHANGED); cv::cvtColor(imLeft, imLeft, CV_GRAY2BGR); vector vpMapPointsKF = pKF->GetMapPointMatches(); int num_MPs = 0; for(int i=0; iisBad()) { continue; } num_MPs += 1; string strNumOBs = to_string(vpMapPointsKF[i]->Observations()); cv::circle(imLeft, pKF->mvKeys[i].pt, 2, cv::Scalar(0, 255, 0)); cv::putText(imLeft, strNumOBs, pKF->mvKeys[i].pt, CV_FONT_HERSHEY_DUPLEX, 1, cv::Scalar(255, 0, 0)); } cout << "--It has " << num_MPs << " MPs matched in the map" << endl; string namefile = "./test_GBA/GBA_" + to_string(nLoopKF) + "_KF" + to_string(pKF->mnId) +"_D" + to_string(dist) +".png"; cv::imwrite(namefile, imLeft); }*/ if(pKF->bImu) { //cout << "-------Update inertial values" << endl; pKF->mVwbBefGBA = pKF->GetVelocity(); if (pKF->mVwbGBA.empty()) Verbose::PrintMess("pKF->mVwbGBA is empty", Verbose::VERBOSITY_NORMAL); assert(!pKF->mVwbGBA.empty()); pKF->SetVelocity(pKF->mVwbGBA); pKF->SetNewBias(pKF->mBiasGBA); } lpKFtoCheck.pop_front(); } //cout << "GBA: Correct MapPoints" << endl; // Correct MapPoints const vector vpMPs = pActiveMap->GetAllMapPoints(); for(size_t i=0; iisBad()) continue; if(pMP->mnBAGlobalForKF==nLoopKF) { // If optimized by Global BA, just update pMP->SetWorldPos(pMP->mPosGBA); } else { // Update according to the correction of its reference keyframe KeyFrame* pRefKF = pMP->GetReferenceKeyFrame(); if(pRefKF->mnBAGlobalForKF!=nLoopKF) continue; if(pRefKF->mTcwBefGBA.empty()) continue; // Map to non-corrected camera cv::Mat Rcw = pRefKF->mTcwBefGBA.rowRange(0,3).colRange(0,3); cv::Mat tcw = pRefKF->mTcwBefGBA.rowRange(0,3).col(3); cv::Mat Xc = Rcw*pMP->GetWorldPos()+tcw; // Backproject using corrected camera cv::Mat Twc = pRefKF->GetPoseInverse(); cv::Mat Rwc = Twc.rowRange(0,3).colRange(0,3); cv::Mat twc = Twc.rowRange(0,3).col(3); pMP->SetWorldPos(Rwc*Xc+twc); } } pActiveMap->InformNewBigChange(); pActiveMap->IncreaseChangeIndex(); // TODO Check this update // mpTracker->UpdateFrameIMU(1.0f, mpTracker->GetLastKeyFrame()->GetImuBias(), mpTracker->GetLastKeyFrame()); mpLocalMapper->Release(); Verbose::PrintMess("Map updated!", Verbose::VERBOSITY_NORMAL); } mbFinishedGBA = true; mbRunningGBA = false; } } void LoopClosing::RequestFinish() { unique_lock lock(mMutexFinish); // cout << "LC: Finish requested" << endl; mbFinishRequested = true; } bool LoopClosing::CheckFinish() { unique_lock lock(mMutexFinish); return mbFinishRequested; } void LoopClosing::SetFinish() { unique_lock lock(mMutexFinish); mbFinished = true; } bool LoopClosing::isFinished() { unique_lock lock(mMutexFinish); return mbFinished; } } //namespace ORB_SLAM