void MapPoint::ComputeDistinctiveDescriptors() { // Retrieve all observed descriptors vector<cv::Mat> vDescriptors; map<KeyFrame*,size_t> observations; { boost::mutex::scoped_lock lock1(mMutexFeatures); if(mbBad) return; observations=mObservations; } if(observations.empty()) return; vDescriptors.reserve(observations.size()); for(map<KeyFrame*,size_t>::iterator mit=observations.begin(), mend=observations.end(); mit!=mend; mit++) { KeyFrame* pKF = mit->first; if(!pKF->isBad()) vDescriptors.push_back(pKF->GetDescriptor(mit->second)); } if(vDescriptors.empty()) return; // Compute distances between them const size_t N = vDescriptors.size(); float Distances[N][N]; for(size_t i=0;i<N;i++) { Distances[i][i]=0; for(size_t j=i+1;j<N;j++) { int distij = ORBmatcher::DescriptorDistance(vDescriptors[i],vDescriptors[j]); Distances[i][j]=distij; Distances[j][i]=distij; } } // Take the descriptor with least median distance to the rest int BestMedian = INT_MAX; int BestIdx = 0; for(size_t i=0;i<N;i++) { vector<int> vDists(Distances[i],Distances[i]+N); sort(vDists.begin(),vDists.end()); int median = vDists[0.5*(N-1)]; if(median<BestMedian) { BestMedian = median; BestIdx = i; } } { boost::mutex::scoped_lock lock(mMutexFeatures); mDescriptor = vDescriptors[BestIdx].clone(); } }
bool LoopClosing::DetectLoop() { { boost::mutex::scoped_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 10KF have passed from last loop detection if(mpCurrentKF->mnId<mLastLoopKFid+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 vector<KeyFrame*> vpConnectedKeyFrames = mpCurrentKF->GetVectorCovisibleKeyFrames(); DBoW2::BowVector CurrentBowVec = mpCurrentKF->GetBowVector(); float minScore = 1; for(size_t i=0; i<vpConnectedKeyFrames.size(); i++) { KeyFrame* pKF = vpConnectedKeyFrames[i]; if(pKF->isBad()) continue; DBoW2::BowVector BowVec = pKF->GetBowVector(); float score = mpORBVocabulary->score(CurrentBowVec, BowVec); if(score<minScore) minScore = score; } // Query the database imposing the minimum score vector<KeyFrame*> 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 keyframe to accept it mvpEnoughConsistentCandidates.clear(); vector<ConsistentGroup> vCurrentConsistentGroups; vector<bool> vbConsistentGroup(mvConsistentGroups.size(),false); for(size_t i=0, iend=vpCandidateKFs.size(); i<iend; i++) { KeyFrame* pCandidateKF = vpCandidateKFs[i]; set<KeyFrame*> spCandidateGroup = pCandidateKF->GetConnectedKeyFrames(); spCandidateGroup.insert(pCandidateKF); bool bEnoughConsistent = false; bool bConsistentForSomeGroup = false; for(size_t iG=0, iendG=mvConsistentGroups.size(); iG<iendG; iG++) { set<KeyFrame*> sPreviousGroup = mvConsistentGroups[iG].first; bool bConsistent = false; for(set<KeyFrame*>::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 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; }
void LocalMapping::SearchInNeighbors() { // Retrieve neighbor keyframes vector<KeyFrame*> vpNeighKFs = mpCurrentKeyFrame->GetBestCovisibilityKeyFrames(20); vector<KeyFrame*> vpTargetKFs; for(vector<KeyFrame*>::iterator vit=vpNeighKFs.begin(), vend=vpNeighKFs.end(); vit!=vend; vit++) { KeyFrame* pKFi = *vit; if(pKFi->isBad() || pKFi->mnFuseTargetForKF == mpCurrentKeyFrame->mnId) continue; vpTargetKFs.push_back(pKFi); pKFi->mnFuseTargetForKF = mpCurrentKeyFrame->mnId; // Extend to some second neighbors vector<KeyFrame*> vpSecondNeighKFs = pKFi->GetBestCovisibilityKeyFrames(5); for(vector<KeyFrame*>::iterator vit2=vpSecondNeighKFs.begin(), vend2=vpSecondNeighKFs.end(); vit2!=vend2; vit2++) { KeyFrame* pKFi2 = *vit2; if(pKFi2->isBad() || pKFi2->mnFuseTargetForKF==mpCurrentKeyFrame->mnId || pKFi2->mnId==mpCurrentKeyFrame->mnId) continue; vpTargetKFs.push_back(pKFi2); } } // Search matches by projection from current KF in target KFs ORBmatcher matcher(0.6); vector<MapPoint*> vpMapPointMatches = mpCurrentKeyFrame->GetMapPointMatches(); for(vector<KeyFrame*>::iterator vit=vpTargetKFs.begin(), vend=vpTargetKFs.end(); vit!=vend; vit++) { KeyFrame* pKFi = *vit; matcher.Fuse(pKFi,vpMapPointMatches); } // Search matches by projection from target KFs in current KF vector<MapPoint*> vpFuseCandidates; vpFuseCandidates.reserve(vpTargetKFs.size()*vpMapPointMatches.size()); for(vector<KeyFrame*>::iterator vitKF=vpTargetKFs.begin(), vendKF=vpTargetKFs.end(); vitKF!=vendKF; vitKF++) { KeyFrame* pKFi = *vitKF; vector<MapPoint*> vpMapPointsKFi = pKFi->GetMapPointMatches(); for(vector<MapPoint*>::iterator vitMP=vpMapPointsKFi.begin(), vendMP=vpMapPointsKFi.end(); vitMP!=vendMP; vitMP++) { MapPoint* pMP = *vitMP; if(!pMP) continue; if(pMP->isBad() || pMP->mnFuseCandidateForKF == mpCurrentKeyFrame->mnId) continue; pMP->mnFuseCandidateForKF = mpCurrentKeyFrame->mnId; vpFuseCandidates.push_back(pMP); } } matcher.Fuse(mpCurrentKeyFrame,vpFuseCandidates); // Update points vpMapPointMatches = mpCurrentKeyFrame->GetMapPointMatches(); for(size_t i=0, iend=vpMapPointMatches.size(); i<iend; i++) { MapPoint* pMP=vpMapPointMatches[i]; if(pMP) { if(!pMP->isBad()) { pMP->ComputeDistinctiveDescriptors(); pMP->UpdateNormalAndDepth(); } } } // Update connections in covisibility graph mpCurrentKeyFrame->UpdateConnections(); }
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<Sim3Solver*> vpSim3Solvers; vpSim3Solvers.resize(nInitialCandidates); vector<vector<MapPoint*> > vvpMapPointMatches; vvpMapPointMatches.resize(nInitialCandidates); vector<bool> vbDiscarded; vbDiscarded.resize(nInitialCandidates); int nCandidates=0; //candidates with enough matches for(int i=0; i<nInitialCandidates; i++) { KeyFrame* pKF = mvpEnoughConsistentCandidates[i]; // avoid that local mapping erase it while it is being processed in this thread pKF->SetNotErase(); if(pKF->isBad()) { vbDiscarded[i] = true; continue; } int nmatches = matcher.SearchByBoW(mpCurrentKF,pKF,vvpMapPointMatches[i]); if(nmatches<20) { vbDiscarded[i] = true; continue; } else { Sim3Solver* pSolver = new Sim3Solver(mpCurrentKF,pKF,vvpMapPointMatches[i]); pSolver->SetRansacParameters(0.99,20,300); vpSim3Solvers[i] = pSolver; } nCandidates++; } bool bMatch = false; // Perform alternatively RANSAC iterations for each candidate // until one is succesful or all fail while(nCandidates>0 && !bMatch) { for(int i=0; i<nInitialCandidates; i++) { if(vbDiscarded[i]) continue; KeyFrame* pKF = mvpEnoughConsistentCandidates[i]; // Perform 5 Ransac Iterations vector<bool> vbInliers; int nInliers; bool bNoMore; Sim3Solver* pSolver = vpSim3Solvers[i]; cv::Mat Scm = pSolver->iterate(5,bNoMore,vbInliers,nInliers); // If Ransac reachs max. iterations discard keyframe if(bNoMore) { vbDiscarded[i]=true; nCandidates--; } // If RANSAC returns a Sim3, perform a guided matching and optimize with all correspondences if(!Scm.empty()) { vector<MapPoint*> vpMapPointMatches(vvpMapPointMatches[i].size(), static_cast<MapPoint*>(NULL)); for(size_t j=0, jend=vbInliers.size(); j<jend; j++) { if(vbInliers[j]) vpMapPointMatches[j]=vvpMapPointMatches[i][j]; } cv::Mat R = pSolver->GetEstimatedRotation(); cv::Mat t = pSolver->GetEstimatedTranslation(); const float s = pSolver->GetEstimatedScale(); matcher.SearchBySim3(mpCurrentKF,pKF,vpMapPointMatches,s,R,t,7.5); g2o::Sim3 gScm(Converter::toMatrix3d(R),Converter::toVector3d(t),s); const int nInliers = Optimizer::OptimizeSim3(mpCurrentKF, pKF, vpMapPointMatches, gScm, 10); // If optimization is succesful stop ransacs and continue if(nInliers>=20) { bMatch = true; mpMatchedKF = pKF; g2o::Sim3 gSmw(Converter::toMatrix3d(pKF->GetRotation()),Converter::toVector3d(pKF->GetTranslation()),1.0); mg2oScw = gScm*gSmw; mScw = Converter::toCvMat(mg2oScw); mvpCurrentMatchedPoints = vpMapPointMatches; break; } } } } if(!bMatch) { for(int i=0; i<nInitialCandidates; i++) mvpEnoughConsistentCandidates[i]->SetErase(); mpCurrentKF->SetErase(); return false; } // Retrieve MapPoints seen in Loop Keyframe and neighbors vector<KeyFrame*> vpLoopConnectedKFs = mpMatchedKF->GetVectorCovisibleKeyFrames(); vpLoopConnectedKFs.push_back(mpMatchedKF); mvpLoopMapPoints.clear(); for(vector<KeyFrame*>::iterator vit=vpLoopConnectedKFs.begin(); vit!=vpLoopConnectedKFs.end(); vit++) { KeyFrame* pKF = *vit; vector<MapPoint*> vpMapPoints = pKF->GetMapPointMatches(); for(size_t i=0, iend=vpMapPoints.size(); i<iend; 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 matcher.SearchByProjection(mpCurrentKF, mScw, mvpLoopMapPoints, mvpCurrentMatchedPoints,10); // If enough matches accept Loop int nTotalMatches = 0; for(size_t i=0; i<mvpCurrentMatchedPoints.size(); i++) { if(mvpCurrentMatchedPoints[i]) nTotalMatches++; } if(nTotalMatches>=40) { for(int i=0; i<nInitialCandidates; i++) if(mvpEnoughConsistentCandidates[i]!=mpMatchedKF) mvpEnoughConsistentCandidates[i]->SetErase(); return true; } else { for(int i=0; i<nInitialCandidates; i++) mvpEnoughConsistentCandidates[i]->SetErase(); mpCurrentKF->SetErase(); return false; } }
void KeyFrame::SetBadFlag() { { boost::mutex::scoped_lock lock(mMutexConnections); if(mnId==0) return; else if(mbNotErase) { mbToBeErased = true; return; } } for(map<KeyFrame*,int>::iterator mit = mConnectedKeyFrameWeights.begin(), mend=mConnectedKeyFrameWeights.end(); mit!=mend; mit++) mit->first->EraseConnection(this); for(size_t i=0; i<mvpMapPoints.size(); i++) if(mvpMapPoints[i]) mvpMapPoints[i]->EraseObservation(this); { boost::mutex::scoped_lock lock(mMutexConnections); boost::mutex::scoped_lock lock1(mMutexFeatures); mConnectedKeyFrameWeights.clear(); mvpOrderedConnectedKeyFrames.clear(); // Update Spanning Tree set<KeyFrame*> sParentCandidates; sParentCandidates.insert(mpParent); // Assign at each iteration one children with a parent (the pair with highest covisibility weight) // Include that children as new parent candidate for the rest while(!mspChildrens.empty()) { bool bContinue = false; int max = -1; KeyFrame* pC; KeyFrame* pP; for(set<KeyFrame*>::iterator sit=mspChildrens.begin(), send=mspChildrens.end(); sit!=send; sit++) { KeyFrame* pKF = *sit; if(pKF->isBad()) continue; // Check if a parent candidate is connected to the keyframe vector<KeyFrame*> vpConnected = pKF->GetVectorCovisibleKeyFrames(); for(size_t i=0, iend=vpConnected.size(); i<iend; i++) { for(set<KeyFrame*>::iterator spcit=sParentCandidates.begin(), spcend=sParentCandidates.end(); spcit!=spcend; spcit++) { if(vpConnected[i]->mnId == (*spcit)->mnId) { int w = pKF->GetWeight(vpConnected[i]); if(w>max) { pC = pKF; pP = vpConnected[i]; max = w; bContinue = true; } } } } } if(bContinue) { pC->ChangeParent(pP); sParentCandidates.insert(pC); mspChildrens.erase(pC); } else break; } // If a children has no covisibility links with any parent candidate, assign to the original parent of this KF if(!mspChildrens.empty()) for(set<KeyFrame*>::iterator sit=mspChildrens.begin(); sit!=mspChildrens.end(); sit++) { (*sit)->ChangeParent(mpParent); } mpParent->EraseChild(this); mbBad = true; } mpMap->EraseKeyFrame(this); mpKeyFrameDB->erase(this); }