LocalMapping线程负责对新加入的KeyFrames和MapPoints筛选融合,剔除冗余的KeyFrames和MapPoints,维护稳定的KeyFrame集合,传给后续的LoopClosing线程。
主要的功能点在:
处理新的关键帧ProcessNewKeyFrame()
剔除不合格地图点MapPointCulling()
三角化恢复新地图点CreateNewMapPoints()
融合当前帧与相邻帧重复的地图点SearchInNeighbors()
局部BA优化LocalBundleAdjustment()
剔除冗余关键帧KeyFrameCulling()

入口函数是LocalMapping.cc中的Run()函数,Run()函数也相当于LocalMapping的主函数。这个线程在系统运行起来的时候处于休眠或者等待状态。当有新的关键帧加入的时候线程就将自己设置为繁忙状态(告诉Tracking线程我很忙,暂时不接受新的关键帧)并立刻处理新的关键帧;当处理完一个关键帧后,就会将自己设置为空闲状态(告诉Tracking线程,我可以接受新的关键帧了)并进入睡眠状态3毫秒。当代码如下:
- void LocalMapping::Run()
- {
-
- mbFinished = false;
-
- while(1)
- {
- // Tracking will see that Local Mapping is busy
- // 告诉Tracking,LocalMapping正处于繁忙状态,
- // LocalMapping线程处理的关键帧都是Tracking线程发过的
- // 在LocalMapping线程还没有处理完关键帧之前Tracking线程最好不要发送太快
- SetAcceptKeyFrames(false);
-
- // Check if there are keyframes in the queue
- // 等待处理的关键帧列表不为空
- if(CheckNewKeyFrames())
- {
- // BoW conversion and insertion in Map
- ProcessNewKeyFrame();
-
- // Check recent MapPoints
- // 剔除ProcessNewKeyFrame函数中引入的不合格MapPoints
- MapPointCulling();
-
- // Triangulate new MapPoints
- // 相机运动过程中与相邻关键帧通过三角化恢复出一些MapPoints
- CreateNewMapPoints();
-
- // 已经处理完队列中的最后的一个关键帧
- if(!CheckNewKeyFrames())
- {
- // Find more matches in neighbor keyframes and fuse point duplications
- // 检查并融合当前关键帧与相邻帧(两级相邻)重复的MapPoints
- SearchInNeighbors();
- }
-
- mbAbortBA = false;
-
- if(!CheckNewKeyFrames() && !stopRequested())
- {
- // Local BA
- if(mpMap->KeyFramesInMap()>2)
- Optimizer::LocalBundleAdjustment(mpCurrentKeyFrame,&mbAbortBA, mpMap);
-
- // Check redundant local Keyframes
- // 检测并剔除当前帧相邻的关键帧中冗余的关键帧
- // 剔除的标准是:该关键帧的90%的MapPoints可以被其它关键帧观测到
- // trick!
- // Tracking中先把关键帧交给LocalMapping线程
- // 并且在Tracking中InsertKeyFrame函数的条件比较松,交给LocalMapping线程的关键帧会比较密
- // 在这里再删除冗余的关键帧
- KeyFrameCulling();
- }
-
- // 将当前帧加入到闭环检测队列中
- mpLoopCloser->InsertKeyFrame(mpCurrentKeyFrame);
- }
- else if(Stop())
- {
- // Safe area to stop
- while(isStopped() && !CheckFinish())
- {
- usleep(3000);
- }
- if(CheckFinish())
- break;
- }
-
- ResetIfRequested();
-
- // Tracking will see that Local Mapping is busy
- SetAcceptKeyFrames(true);
-
- if(CheckFinish())
- break;
-
- usleep(3000);
- }
-
- SetFinish();
- }
告诉Tracking线程,LocalMapping是否处于繁忙状态。如果处于繁忙状态则不要再添加新的关键帧了,否则可以添加关键帧。
- void LocalMapping::SetAcceptKeyFrames(bool flag)
- {
- unique_lock
lock(mMutexAccept) ; - mbAcceptKeyFrames=flag;
- }
作用是,从队列取出第一个关键帧(该关键帧时队列中按时间上最旧的关键帧),计算该关键帧的Bow特征,更新关键帧观测到的地图点的信息,并且将该关键帧新生成的地图点添加进mlpRecentAddedMapPoints,等待后续检测。然后将该关键帧插入地图。
- void LocalMapping::ProcessNewKeyFrame()
- {
- {
- unique_lock
lock(mMutexNewKFs) ; - mpCurrentKeyFrame = mlNewKeyFrames.front();
- mlNewKeyFrames.pop_front();
- }
-
- // Compute Bags of Words structures
- mpCurrentKeyFrame->ComputeBoW();
-
- // Associate MapPoints to the new keyframe and update normal and descriptor
- const vector
vpMapPointMatches = mpCurrentKeyFrame->GetMapPointMatches(); -
- for(size_t i=0; i
size(); i++) - {
- MapPoint* pMP = vpMapPointMatches[i];
- if(pMP)
- {
- if(!pMP->isBad())
- {
- // 如果该地图点没有记录该帧,则添加上这个记录。
- if(!pMP->IsInKeyFrame(mpCurrentKeyFrame))
- {
- // i记录了地图点在关键帧中的索引
- pMP->AddObservation(mpCurrentKeyFrame, i);
- // 因为地图点增加了新的观测,而法向量是所有观测到该点的关键帧都求一个法徽号向量之后求均,所以需要更新
- pMP->UpdateNormalAndDepth();
- // 从所有关键帧的观测点中选择一个作为该点的描述子
- pMP->ComputeDistinctiveDescriptors();
- }
- else // this can only happen for new stereo points inserted by the Tracking
- {
- mlpRecentAddedMapPoints.push_back(pMP);
- }
- }
- }
- }
-
- // Update links in the Covisibility Graph
- mpCurrentKeyFrame->UpdateConnections();
-
- // Insert Keyframe in Map
- mpMap->AddKeyFrame(mpCurrentKeyFrame);
- }
主要作用是筛选mlpRecentAddedMapPoints里的点,对于不好的点标记为bad。
已经是坏点的MapPoints直接从检查链表中删除;
跟踪到该MapPoint的Frame中被判定为内点的比例须大于25%,注意不一定是关键帧。
从该点建立开始,到现在已经过了不小于2个关键帧,但是观测到该点的关键帧数却不超过cnThObs帧,那么该点检验不合格。
从建立该点开始,已经过了3个关键帧而没有被剔除,则认为是质量高的点,因此没有SetBadFlag(),仅从队列中删除,放弃继续对该MapPoint的检测mlpRecentAddedMapPoints剩下的点,需要继续经过以后的检测。
- void LocalMapping::MapPointCulling()
- {
- // Check Recent Added MapPoints
- list
::iterator lit = mlpRecentAddedMapPoints.begin(); - const unsigned long int nCurrentKFid = mpCurrentKeyFrame->mnId;
-
- int nThObs;
- if(mbMonocular)
- nThObs = 2;
- else
- nThObs = 3;
- const int cnThObs = nThObs;
-
- while(lit!=mlpRecentAddedMapPoints.end())
- {
- MapPoint* pMP = *lit;
- if(pMP->isBad())
- {
- lit = mlpRecentAddedMapPoints.erase(lit);
- }
- else if(pMP->GetFoundRatio()<0.25f )
- {
- pMP->SetBadFlag();
- lit = mlpRecentAddedMapPoints.erase(lit);
- }
- else if(((int)nCurrentKFid-(int)pMP->mnFirstKFid)>=2 && pMP->Observations()<=cnThObs)
- {
- pMP->SetBadFlag();
- lit = mlpRecentAddedMapPoints.erase(lit);
- }
- else if(((int)nCurrentKFid-(int)pMP->mnFirstKFid)>=3)
- lit = mlpRecentAddedMapPoints.erase(lit);
- else
- lit++;
- }
- }
-
利用三角化新建一些地图点
在当前关键帧的共视关键帧中找到共视程度最高的nn帧相邻帧vpNeighKFs
遍历相邻关键帧vpNeighKFs,得到基线向量vBaseline = Ow2-Ow1
判断相机运动的基线是不是足够长,邻接关键帧的场景深度中值medianDepthKF2,baseline与景深的比例,如果特别远(比例特别小),那么不考虑当前邻接的关键帧,不生成3D点
根据两个关键帧的位姿计算它们之间的基本矩阵F
通过极线约束限制匹配时的搜索范围,对满足对极约束的特征点进行特征点匹配
对每对匹配通过三角化生成3D点,和Triangulate函数差不多
接着分别检查新得到的点在两个平面上的重投影误差,如果大于一定的值,直接抛弃该点。
检查尺度连续性
如果满足对极约束则建立当前帧的地图点及其属性(a.观测到该MapPoint的关键帧 b.该MapPoint的描述子 c.该MapPoint的平均观测方向和深度范围)
将地图点加入关键帧,加入全局map
- void LocalMapping::CreateNewMapPoints()
- {
- // Retrieve neighbor keyframes in covisibility graph
- int nn = 10;
- if(mbMonocular)
- nn=20;
- //在当前关键帧的共视关键帧中找到共视程度最高的nn帧相邻帧vpNeighKFs
- const vector
vpNeighKFs = mpCurrentKeyFrame->GetBestCovisibilityKeyFrames(nn); -
- ORBmatcher matcher(0.6,false);
-
- cv::Mat Rcw1 = mpCurrentKeyFrame->GetRotation();
- cv::Mat Rwc1 = Rcw1.t();
- cv::Mat tcw1 = mpCurrentKeyFrame->GetTranslation();
- cv::Mat Tcw1(3,4,CV_32F);
- Rcw1.copyTo(Tcw1.colRange(0,3));
- tcw1.copyTo(Tcw1.col(3));
- cv::Mat Ow1 = mpCurrentKeyFrame->GetCameraCenter();
-
- const float &fx1 = mpCurrentKeyFrame->fx;
- const float &fy1 = mpCurrentKeyFrame->fy;
- const float &cx1 = mpCurrentKeyFrame->cx;
- const float &cy1 = mpCurrentKeyFrame->cy;
- const float &invfx1 = mpCurrentKeyFrame->invfx;
- const float &invfy1 = mpCurrentKeyFrame->invfy;
-
- const float ratioFactor = 1.5f*mpCurrentKeyFrame->mfScaleFactor;
-
- int nnew=0;
-
- // Search matches with epipolar restriction and triangulate
- for(size_t i=0; i
size(); i++) - {
- // 基于实时性考虑,如果已经处理了与新关键帧最邻近的一个关键帧,但又来了更新的关键帧,则停止立即返回,去处理更新的关键帧。
- if(i>0 && CheckNewKeyFrames())
- return;
-
- KeyFrame* pKF2 = vpNeighKFs[i];
-
- // Check first that baseline is not too short
- cv::Mat Ow2 = pKF2->GetCameraCenter();
- cv::Mat vBaseline = Ow2-Ow1;
- const float baseline = cv::norm(vBaseline);
-
- if(!mbMonocular)
- {
- if(baseline
mb) - continue;
- }
- else
- {
- const float medianDepthKF2 = pKF2->ComputeSceneMedianDepth(2);
- const float ratioBaselineDepth = baseline/medianDepthKF2;
-
- if(ratioBaselineDepth<0.01)
- continue;
- }
-
- // Compute Fundamental Matrix
- //根据两个关键帧的位姿计算它们之间的基本矩阵F
- cv::Mat F12 = ComputeF12(mpCurrentKeyFrame,pKF2);
-
- // Search matches that fullfil epipolar constraint
- vector
size_t,size_t> > vMatchedIndices; - matcher.SearchForTriangulation(mpCurrentKeyFrame,pKF2,F12,vMatchedIndices,false);
-
- cv::Mat Rcw2 = pKF2->GetRotation();
- cv::Mat Rwc2 = Rcw2.t();
- cv::Mat tcw2 = pKF2->GetTranslation();
- cv::Mat Tcw2(3,4,CV_32F);
- Rcw2.copyTo(Tcw2.colRange(0,3));
- tcw2.copyTo(Tcw2.col(3));
-
- const float &fx2 = pKF2->fx;
- const float &fy2 = pKF2->fy;
- const float &cx2 = pKF2->cx;
- const float &cy2 = pKF2->cy;
- const float &invfx2 = pKF2->invfx;
- const float &invfy2 = pKF2->invfy;
-
- // Triangulate each match
- const int nmatches = vMatchedIndices.size();
- for(int ikp=0; ikp
- {
- const int &idx1 = vMatchedIndices[ikp].first;
- const int &idx2 = vMatchedIndices[ikp].second;
-
- const cv::KeyPoint &kp1 = mpCurrentKeyFrame->mvKeysUn[idx1];
- const float kp1_ur=mpCurrentKeyFrame->mvuRight[idx1];
- bool bStereo1 = kp1_ur>=0; // 在上一帧中被双目观测到
-
- const cv::KeyPoint &kp2 = pKF2->mvKeysUn[idx2];
- const float kp2_ur = pKF2->mvuRight[idx2];
- bool bStereo2 = kp2_ur>=0; // 在当前帧中被双目观测到
-
- // Check parallax between rays
- cv::Mat xn1 = (cv::Mat_<float>(3,1) << (kp1.pt.x-cx1)*invfx1, (kp1.pt.y-cy1)*invfy1, 1.0);
- cv::Mat xn2 = (cv::Mat_<float>(3,1) << (kp2.pt.x-cx2)*invfx2, (kp2.pt.y-cy2)*invfy2, 1.0);
-
- cv::Mat ray1 = Rwc1*xn1;
- cv::Mat ray2 = Rwc2*xn2;
- const float cosParallaxRays = ray1.dot(ray2)/(cv::norm(ray1)*cv::norm(ray2));
-
- float cosParallaxStereo = cosParallaxRays+1;
- float cosParallaxStereo1 = cosParallaxStereo;
- float cosParallaxStereo2 = cosParallaxStereo;
-
- if(bStereo1)
- cosParallaxStereo1 = cos(2*atan2(mpCurrentKeyFrame->mb/2,mpCurrentKeyFrame->mvDepth[idx1]));
- else if(bStereo2)
- cosParallaxStereo2 = cos(2*atan2(pKF2->mb/2,pKF2->mvDepth[idx2]));
-
- cosParallaxStereo = min(cosParallaxStereo1,cosParallaxStereo2);
-
- cv::Mat x3D;
- if(cosParallaxRays
0 && (bStereo1 || bStereo2 || cosParallaxRays<0.9998)) - {
- // Linear Triangulation Method
- cv::Mat A(4,4,CV_32F);
- A.row(0) = xn1.at<float>(0)*Tcw1.row(2)-Tcw1.row(0);
- A.row(1) = xn1.at<float>(1)*Tcw1.row(2)-Tcw1.row(1);
- A.row(2) = xn2.at<float>(0)*Tcw2.row(2)-Tcw2.row(0);
- A.row(3) = xn2.at<float>(1)*Tcw2.row(2)-Tcw2.row(1);
-
- cv::Mat w,u,vt;
- cv::SVD::compute(A,w,u,vt,cv::SVD::MODIFY_A| cv::SVD::FULL_UV);
-
- x3D = vt.row(3).t();
-
- if(x3D.at<float>(3)==0)
- continue;
-
- // Euclidean coordinates
- x3D = x3D.rowRange(0,3)/x3D.at<float>(3);
-
- }
- else if(bStereo1 && cosParallaxStereo1
- {
- x3D = mpCurrentKeyFrame->UnprojectStereo(idx1);
- }
- else if(bStereo2 && cosParallaxStereo2
- {
- x3D = pKF2->UnprojectStereo(idx2);
- }
- else
- continue; //No stereo and very low parallax
-
- cv::Mat x3Dt = x3D.t();
-
- //Check triangulation in front of cameras
- float z1 = Rcw1.row(2).dot(x3Dt)+tcw1.at<float>(2);
- if(z1<=0)
- continue;
-
- float z2 = Rcw2.row(2).dot(x3Dt)+tcw2.at<float>(2);
- if(z2<=0)
- continue;
-
- //Check reprojection error in first keyframe
- const float &sigmaSquare1 = mpCurrentKeyFrame->mvLevelSigma2[kp1.octave];
- const float x1 = Rcw1.row(0).dot(x3Dt)+tcw1.at<float>(0);
- const float y1 = Rcw1.row(1).dot(x3Dt)+tcw1.at<float>(1);
- const float invz1 = 1.0/z1;
-
- if(!bStereo1)
- {
- float u1 = fx1*x1*invz1+cx1;
- float v1 = fy1*y1*invz1+cy1;
- float errX1 = u1 - kp1.pt.x;
- float errY1 = v1 - kp1.pt.y;
- if((errX1*errX1+errY1*errY1)>5.991*sigmaSquare1)
- continue;
- }
- else
- {
- float u1 = fx1*x1*invz1+cx1;
- float u1_r = u1 - mpCurrentKeyFrame->mbf*invz1;
- float v1 = fy1*y1*invz1+cy1;
- float errX1 = u1 - kp1.pt.x;
- float errY1 = v1 - kp1.pt.y;
- float errX1_r = u1_r - kp1_ur;
- if((errX1*errX1+errY1*errY1+errX1_r*errX1_r)>7.8*sigmaSquare1)
- continue;
- }
-
- //Check reprojection error in second keyframe
- const float sigmaSquare2 = pKF2->mvLevelSigma2[kp2.octave];
- const float x2 = Rcw2.row(0).dot(x3Dt)+tcw2.at<float>(0);
- const float y2 = Rcw2.row(1).dot(x3Dt)+tcw2.at<float>(1);
- const float invz2 = 1.0/z2;
- if(!bStereo2)
- {
- float u2 = fx2*x2*invz2+cx2;
- float v2 = fy2*y2*invz2+cy2;
- float errX2 = u2 - kp2.pt.x;
- float errY2 = v2 - kp2.pt.y;
- if((errX2*errX2+errY2*errY2)>5.991*sigmaSquare2)
- continue;
- }
- else
- {
- float u2 = fx2*x2*invz2+cx2;
- float u2_r = u2 - mpCurrentKeyFrame->mbf*invz2;
- float v2 = fy2*y2*invz2+cy2;
- float errX2 = u2 - kp2.pt.x;
- float errY2 = v2 - kp2.pt.y;
- float errX2_r = u2_r - kp2_ur;
- if((errX2*errX2+errY2*errY2+errX2_r*errX2_r)>7.8*sigmaSquare2)
- continue;
- }
-
- //Check scale consistency
- cv::Mat normal1 = x3D-Ow1;
- float dist1 = cv::norm(normal1);
-
- cv::Mat normal2 = x3D-Ow2;
- float dist2 = cv::norm(normal2);
-
- if(dist1==0 || dist2==0)
- continue;
-
- const float ratioDist = dist2/dist1;
- const float ratioOctave = mpCurrentKeyFrame->mvScaleFactors[kp1.octave]/pKF2->mvScaleFactors[kp2.octave];
-
- /*if(fabs(ratioDist-ratioOctave)>ratioFactor)
- continue;*/
- if(ratioDist*ratioFactor
ratioOctave*ratioFactor) - continue;
-
- // Triangulation is succesfull
- MapPoint* pMP = new MapPoint(x3D,mpCurrentKeyFrame,mpMap);
-
- pMP->AddObservation(mpCurrentKeyFrame,idx1);
- pMP->AddObservation(pKF2,idx2);
-
- mpCurrentKeyFrame->AddMapPoint(pMP,idx1);
- pKF2->AddMapPoint(pMP,idx2);
-
- pMP->ComputeDistinctiveDescriptors();
-
- pMP->UpdateNormalAndDepth();
-
- mpMap->AddMapPoint(pMP);
- mlpRecentAddedMapPoints.push_back(pMP);
-
- nnew++;
- }
- }
- }
6. void LocalMapping::SearchInNeighbors().
检查并融合当前关键帧与相邻帧(两级相邻)重复的MapPoints,更新当前关键帧的连接关系。
获得当前关键帧在covisibility图中权重排名前nn的邻接关键帧,找到当前帧一级相邻与二级相邻关键帧
将当前帧的MapPoints分别与一级二级相邻帧(的MapPoints)进行融合
matcher.Fuse(pKFi,vpMapPointMatches);
投影当前帧的MapPoints到相邻关键帧pKFi中,并判断是否有重复的MapPoints
如果MapPoint能匹配关键帧的特征点,并且该点有对应的MapPoint,那么将两个MapPoint合并(选择观测数多的)
如果MapPoint能匹配关键帧的特征点,并且该点没有对应的MapPoint,那么为该点添加MapPoint
将一级二级相邻帧的MapPoints分别与当前帧(的MapPoints)进行融合
更新当前帧MapPoints的描述子,深度,观测主方向等属性
在这里插入代码片
更新当前帧的MapPoints后更新与其它帧的连接关系
- void LocalMapping::SearchInNeighbors()
- {
- // Retrieve neighbor keyframes
- int nn = 10;
- if(mbMonocular)
- nn=20;
- const vector
vpNeighKFs = mpCurrentKeyFrame->GetBestCovisibilityKeyFrames(nn); - vector
vpTargetKFs; - for(vector
::const_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
- const vector
vpSecondNeighKFs = pKFi->GetBestCovisibilityKeyFrames(5); - for(vector
::const_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;
- vector
vpMapPointMatches = mpCurrentKeyFrame->GetMapPointMatches(); - for(vector
::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
vpFuseCandidates; - vpFuseCandidates.reserve(vpTargetKFs.size()*vpMapPointMatches.size());
-
- for(vector
::iterator vitKF=vpTargetKFs.begin(), vendKF=vpTargetKFs.end(); vitKF!=vendKF; vitKF++) - {
- KeyFrame* pKFi = *vitKF;
-
- vector
vpMapPointsKFi = pKFi->GetMapPointMatches(); -
- for(vector
::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
- {
- MapPoint* pMP=vpMapPointMatches[i];
- if(pMP)
- {
- if(!pMP->isBad())
- {
- pMP->ComputeDistinctiveDescriptors();
- pMP->UpdateNormalAndDepth();
- }
- }
- }
-
- // Update connections in covisibility graph
- mpCurrentKeyFrame->UpdateConnections();
- }
-
7. void Optimizer::LocalBundleAdjustment()
优化的顶点是包括局部地图帧的位姿,概念lLocalKeyFrames,指的是当前关键帧和其相连接的关键帧组成的集合。还包括这些关键帧可以观测到的所有地图点,地图点的位置也会优化。还有一些帧,这些帧能够观测到这些地图点,但却不是局部地图里,这些帧的位姿也作为顶点添加进图中,但是却固定不动,不会被优化。
- void Optimizer::LocalBundleAdjustment(KeyFrame *pKF, bool* pbStopFlag, Map* pMap)
- {
- // Local KeyFrames: First Breath Search from Current Keyframe
- list
lLocalKeyFrames; -
- lLocalKeyFrames.push_back(pKF);
- pKF->mnBALocalForKF = pKF->mnId;
-
- const vector
vNeighKFs = pKF->GetVectorCovisibleKeyFrames(); - for(int i=0, iend=vNeighKFs.size(); i
- {
- KeyFrame* pKFi = vNeighKFs[i];
- pKFi->mnBALocalForKF = pKF->mnId;
- if(!pKFi->isBad())
- lLocalKeyFrames.push_back(pKFi);
- }
-
- // Local MapPoints seen in Local KeyFrames
- list
lLocalMapPoints; - for(list
::iterator lit=lLocalKeyFrames.begin() , lend=lLocalKeyFrames.end(); lit!=lend; lit++) - {
- vector
vpMPs = (*lit)->GetMapPointMatches(); - for(vector
::iterator vit=vpMPs.begin(), vend=vpMPs.end(); vit!=vend; vit++) - {
- MapPoint* pMP = *vit;
- if(pMP)
- if(!pMP->isBad())
- if(pMP->mnBALocalForKF!=pKF->mnId)
- {
- lLocalMapPoints.push_back(pMP);
- pMP->mnBALocalForKF=pKF->mnId;
- }
- }
- }
-
- // Fixed Keyframes. Keyframes that see Local MapPoints but that are not Local Keyframes
- list
lFixedCameras; - for(list
::iterator lit=lLocalMapPoints.begin(), lend=lLocalMapPoints.end(); lit!=lend; lit++) - {
- map
size_t> observations = (*lit)->GetObservations(); - for(map
size_t>::iterator mit=observations.begin(), mend=observations.end(); mit!=mend; mit++) - {
- KeyFrame* pKFi = mit->first;
-
- if(pKFi->mnBALocalForKF!=pKF->mnId && pKFi->mnBAFixedForKF!=pKF->mnId)
- {
- pKFi->mnBAFixedForKF=pKF->mnId;
- if(!pKFi->isBad())
- lFixedCameras.push_back(pKFi);
- }
- }
- }
-
- // Setup optimizer
- g2o::SparseOptimizer optimizer;
- g2o::BlockSolver_6_3::LinearSolverType * linearSolver;
-
- linearSolver = new g2o::LinearSolverEigen
(); -
- g2o::BlockSolver_6_3 * solver_ptr = new g2o::BlockSolver_6_3(linearSolver);
-
- g2o::OptimizationAlgorithmLevenberg* solver = new g2o::OptimizationAlgorithmLevenberg(solver_ptr);
- optimizer.setAlgorithm(solver);
-
- if(pbStopFlag)
- optimizer.setForceStopFlag(pbStopFlag);
-
- unsigned long maxKFid = 0;
-
- // Set Local KeyFrame vertices
- for(list
::iterator lit=lLocalKeyFrames.begin(), lend=lLocalKeyFrames.end(); lit!=lend; lit++) - {
- KeyFrame* pKFi = *lit;
- g2o::VertexSE3Expmap * vSE3 = new g2o::VertexSE3Expmap();
- vSE3->setEstimate(Converter::toSE3Quat(pKFi->GetPose()));
- vSE3->setId(pKFi->mnId);
- vSE3->setFixed(pKFi->mnId==0);
- optimizer.addVertex(vSE3);
- if(pKFi->mnId>maxKFid)
- maxKFid=pKFi->mnId;
- }
-
- // Set Fixed KeyFrame vertices
- for(list
::iterator lit=lFixedCameras.begin(), lend=lFixedCameras.end(); lit!=lend; lit++) - {
- KeyFrame* pKFi = *lit;
- g2o::VertexSE3Expmap * vSE3 = new g2o::VertexSE3Expmap();
- vSE3->setEstimate(Converter::toSE3Quat(pKFi->GetPose()));
- vSE3->setId(pKFi->mnId);
- vSE3->setFixed(true);
- optimizer.addVertex(vSE3);
- if(pKFi->mnId>maxKFid)
- maxKFid=pKFi->mnId;
- }
-
- // Set MapPoint vertices
- const int nExpectedSize = (lLocalKeyFrames.size()+lFixedCameras.size())*lLocalMapPoints.size();
-
- vector
vpEdgesMono; - vpEdgesMono.reserve(nExpectedSize);
-
- vector
vpEdgeKFMono; - vpEdgeKFMono.reserve(nExpectedSize);
-
- vector
vpMapPointEdgeMono; - vpMapPointEdgeMono.reserve(nExpectedSize);
-
- vector
vpEdgesStereo; - vpEdgesStereo.reserve(nExpectedSize);
-
- vector
vpEdgeKFStereo; - vpEdgeKFStereo.reserve(nExpectedSize);
-
- vector
vpMapPointEdgeStereo; - vpMapPointEdgeStereo.reserve(nExpectedSize);
-
- const float thHuberMono = sqrt(5.991);
- const float thHuberStereo = sqrt(7.815);
-
- for(list
::iterator lit=lLocalMapPoints.begin(), lend=lLocalMapPoints.end(); lit!=lend; lit++) - {
- MapPoint* pMP = *lit;
- g2o::VertexSBAPointXYZ* vPoint = new g2o::VertexSBAPointXYZ();
- vPoint->setEstimate(Converter::toVector3d(pMP->GetWorldPos()));
- int id = pMP->mnId+maxKFid+1;
- vPoint->setId(id);
- vPoint->setMarginalized(true);
- optimizer.addVertex(vPoint);
-
- const map
size_t> observations = pMP->GetObservations(); -
- //Set edges
- for(map
size_t>::const_iterator mit=observations.begin(), mend=observations.end(); mit!=mend; mit++) - {
- KeyFrame* pKFi = mit->first;
-
- if(!pKFi->isBad())
- {
- const cv::KeyPoint &kpUn = pKFi->mvKeysUn[mit->second];
-
- // Monocular observation
- if(pKFi->mvuRight[mit->second]<0)
- {
- Eigen::Matrix<double,2,1> obs;
- obs << kpUn.pt.x, kpUn.pt.y;
-
- g2o::EdgeSE3ProjectXYZ* e = new g2o::EdgeSE3ProjectXYZ();
-
- e->setVertex(0, dynamic_cast
(optimizer.vertex(id))); - e->setVertex(1, dynamic_cast
(optimizer.vertex(pKFi->mnId))); - e->setMeasurement(obs);
- const float &invSigma2 = pKFi->mvInvLevelSigma2[kpUn.octave];
- e->setInformation(Eigen::Matrix2d::Identity()*invSigma2);
-
- g2o::RobustKernelHuber* rk = new g2o::RobustKernelHuber;
- e->setRobustKernel(rk);
- rk->setDelta(thHuberMono);
-
- e->fx = pKFi->fx;
- e->fy = pKFi->fy;
- e->cx = pKFi->cx;
- e->cy = pKFi->cy;
-
- optimizer.addEdge(e);
- vpEdgesMono.push_back(e);
- vpEdgeKFMono.push_back(pKFi);
- vpMapPointEdgeMono.push_back(pMP);
- }
- else // Stereo observation
- {
- Eigen::Matrix<double,3,1> obs;
- const float kp_ur = pKFi->mvuRight[mit->second];
- obs << kpUn.pt.x, kpUn.pt.y, kp_ur;
-
- g2o::EdgeStereoSE3ProjectXYZ* e = new g2o::EdgeStereoSE3ProjectXYZ();
-
- e->setVertex(0, dynamic_cast
(optimizer.vertex(id))); - e->setVertex(1, dynamic_cast
(optimizer.vertex(pKFi->mnId))); - e->setMeasurement(obs);
- const float &invSigma2 = pKFi->mvInvLevelSigma2[kpUn.octave];
- Eigen::Matrix3d Info = Eigen::Matrix3d::Identity()*invSigma2;
- e->setInformation(Info);
-
- g2o::RobustKernelHuber* rk = new g2o::RobustKernelHuber;
- e->setRobustKernel(rk);
- rk->setDelta(thHuberStereo);
-
- e->fx = pKFi->fx;
- e->fy = pKFi->fy;
- e->cx = pKFi->cx;
- e->cy = pKFi->cy;
- e->bf = pKFi->mbf;
-
- optimizer.addEdge(e);
- vpEdgesStereo.push_back(e);
- vpEdgeKFStereo.push_back(pKFi);
- vpMapPointEdgeStereo.push_back(pMP);
- }
- }
- }
- }
-
- if(pbStopFlag)
- if(*pbStopFlag)
- return;
-
- optimizer.initializeOptimization();
- optimizer.optimize(5);
-
- bool bDoMore= true;
-
- if(pbStopFlag)
- if(*pbStopFlag)
- bDoMore = false;
-
- if(bDoMore)
- {
-
- // Check inlier observations
- for(size_t i=0, iend=vpEdgesMono.size(); i
- {
- g2o::EdgeSE3ProjectXYZ* e = vpEdgesMono[i];
- MapPoint* pMP = vpMapPointEdgeMono[i];
-
- if(pMP->isBad())
- continue;
-
- if(e->chi2()>5.991 || !e->isDepthPositive())
- {
- e->setLevel(1);
- }
-
- e->setRobustKernel(0);
- }
-
- for(size_t i=0, iend=vpEdgesStereo.size(); i
- {
- g2o::EdgeStereoSE3ProjectXYZ* e = vpEdgesStereo[i];
- MapPoint* pMP = vpMapPointEdgeStereo[i];
-
- if(pMP->isBad())
- continue;
-
- if(e->chi2()>7.815 || !e->isDepthPositive())
- {
- e->setLevel(1);
- }
-
- e->setRobustKernel(0);
- }
-
- // Optimize again without the outliers
-
- optimizer.initializeOptimization(0);
- optimizer.optimize(10);
-
- }
-
- vector
> vToErase; - vToErase.reserve(vpEdgesMono.size()+vpEdgesStereo.size());
-
- // Check inlier observations
- for(size_t i=0, iend=vpEdgesMono.size(); i
- {
- g2o::EdgeSE3ProjectXYZ* e = vpEdgesMono[i];
- MapPoint* pMP = vpMapPointEdgeMono[i];
-
- if(pMP->isBad())
- continue;
-
- if(e->chi2()>5.991 || !e->isDepthPositive())
- {
- KeyFrame* pKFi = vpEdgeKFMono[i];
- vToErase.push_back(make_pair(pKFi,pMP));
- }
- }
-
- for(size_t i=0, iend=vpEdgesStereo.size(); i
- {
- g2o::EdgeStereoSE3ProjectXYZ* e = vpEdgesStereo[i];
- MapPoint* pMP = vpMapPointEdgeStereo[i];
-
- if(pMP->isBad())
- continue;
-
- if(e->chi2()>7.815 || !e->isDepthPositive())
- {
- KeyFrame* pKFi = vpEdgeKFStereo[i];
- vToErase.push_back(make_pair(pKFi,pMP));
- }
- }
-
- // Get Map Mutex
- unique_lock
lock(pMap->mMutexMapUpdate) ; -
- if(!vToErase.empty())
- {
- for(size_t i=0;i
size();i++) - {
- KeyFrame* pKFi = vToErase[i].first;
- MapPoint* pMPi = vToErase[i].second;
- pKFi->EraseMapPointMatch(pMPi);
- pMPi->EraseObservation(pKFi);
- }
- }
-
- // Recover optimized data
-
- //Keyframes
- for(list
::iterator lit=lLocalKeyFrames.begin(), lend=lLocalKeyFrames.end(); lit!=lend; lit++) - {
- KeyFrame* pKF = *lit;
- g2o::VertexSE3Expmap* vSE3 = static_cast
(optimizer.vertex(pKF->mnId)); - g2o::SE3Quat SE3quat = vSE3->estimate();
- pKF->SetPose(Converter::toCvMat(SE3quat));
- }
-
- //Points
- for(list
::iterator lit=lLocalMapPoints.begin(), lend=lLocalMapPoints.end(); lit!=lend; lit++) - {
- MapPoint* pMP = *lit;
- g2o::VertexSBAPointXYZ* vPoint = static_cast
(optimizer.vertex(pMP->mnId+maxKFid+1)); - pMP->SetWorldPos(Converter::toCvMat(vPoint->estimate()));
- pMP->UpdateNormalAndDepth();
- }
- }
8. void LocalMapping::KeyFrameCulling()

在Covisibility Graph,也就是局部地图中的关键帧,一个关键帧的90%以上的MapPoints能被其他关键帧(至少3个,这里的其他关键帧不特指当前的局部地图关键帧)观测到,则认为该关键帧为冗余关键帧。
根据Covisibility Graph提取当前帧的共视关键帧
对所有的局部关键帧进行遍历,提取每个共视关键帧的MapPoints
遍历该局部关键帧的MapPoints,判断是否90%以上的MapPoints能被其它关键帧(至少3个)观测到
该局部关键帧90%以上的MapPoints能被其它关键帧(至少3个)观测到,则认为是冗余关键帧
- void LocalMapping::KeyFrameCulling()
- {
- // Check redundant keyframes (only local keyframes)
- // A keyframe is considered redundant if the 90% of the MapPoints it sees, are seen
- // in at least other 3 keyframes (in the same or finer scale)
- // We only consider close stereo points
- // 检查冗余关键帧。一个关键帧的地图点中90%的地图点可以被至少其他3个相同或者更小等级(这里的等级是图像金字塔的层数)关键帧看到,则认为这个关键帧是冗余的。
- vector
vpLocalKeyFrames = mpCurrentKeyFrame->GetVectorCovisibleKeyFrames(); -
- for(vector
::iterator vit=vpLocalKeyFrames.begin(), vend=vpLocalKeyFrames.end(); vit!=vend; vit++) - {
- KeyFrame* pKF = *vit;
- if(pKF->mnId==0)
- continue;
- const vector
vpMapPoints = pKF->GetMapPointMatches(); -
- int nObs = 3;
- const int thObs=nObs;
- int nRedundantObservations=0;
- int nMPs=0;
- for(size_t i=0, iend=vpMapPoints.size(); i
- {
- MapPoint* pMP = vpMapPoints[i];
- if(pMP)
- {
- if(!pMP->isBad())
- {
- if(!mbMonocular)
- {
- if(pKF->mvDepth[i]>pKF->mThDepth || pKF->mvDepth[i]<0)
- continue;
- }
- //MapPoint计数
- nMPs++;
- //该pMP是否可以被大于3个的关键帧看到
- if(pMP->Observations()>thObs)
- {
- const int &scaleLevel = pKF->mvKeysUn[i].octave;
- const map
size_t> observations = pMP->GetObservations(); - int nObs=0;
- for(map
size_t>::const_iterator mit=observations.begin(), mend=observations.end(); mit!=mend; mit++) - {
- KeyFrame* pKFi = mit->first;
- if(pKFi==pKF)
- continue;
- // 获取关键点在金字塔图像中所处的层数
- const int &scaleLeveli = pKFi->mvKeysUn[mit->second].octave;
- const int &scaleLeveli = pKFi->mvKeysUn[mit->second].octave;
- //pKFi的关键点所处的层数<=scaleLevel+1
- //为什么要用层数来判断呢?还没有想明白
- if(scaleLeveli<=scaleLevel+1)
- {
- //共视关键帧计数
- nObs++;
- if(nObs>=thObs)
- break;
- }
- }
- //共视关键帧大于等于3判断
- if(nObs>=thObs)
- {
- nRedundantObservations++;
- }
- }
- }
- }
- }
-
- if(nRedundantObservations>0.9*nMPs)
- //关键帧设置为bag,即被认为是冗余关键帧
- pKF->SetBadFlag();
- }
- }
-
参考文献
主要内容来自下文,重写了一说说明,添加了一些注释
-
相关阅读:
vue-router的基本用法
Camera2 OpenCamera流程
【欧拉函数】CF1731E
基于springboot实现校园在线拍卖系统项目【项目源码】计算机毕业设计
教程更新 | RK3568驱动指南第六篇-平台总线
HTML5期末考核大作业,电影网站——橙色国外电影 web期末作业设计网页
python基于django学生成绩管理系统o8mkp
numpy 和 tensorflow 中的各种乘法(点乘和矩阵乘)
朝阳药品数据分析案例
Mybatis-Plus 条件构造器Wrapper
-
原文地址:https://blog.csdn.net/xhtchina/article/details/126678378
-
最新文章
-
沪漂五周年了:我越来越迷茫了
Agentic Skill Routing 实战:别再把所有 Skill 塞进 AI Agent 上下文
MySQL-Seconds_behind_master的精度误差
[MAF预定义ChatClient中间件-03]CachingChatClient——利用缓存省钱省时间
AI的至暗历史:从万众期待到被政府撤资,AI的两次死亡徘徊
Agent OS :五种驯服不确定性的范式
PortSwigger SQL注入LAB11
数据库即时编译JIT
[Begin]AI Learn Data Day 0
深度学习进阶(二十七)现代 LLM 的核心架构设计其二:SwiGLU