KeyFrame中维护了一个map,保存了与当前帧共视的KeyFrame*与权重(共视MapPonits数量)。对关键帧之间关系是用加权有向图来完成的,那么理解其spanning tree生成树的原理就颇有必要了。后端
KeyFrame中比较难理解的是SetBagFlag()函数,真实删除当前关键帧以前,须要处理好父亲和儿子关键帧关系,否则会形成整个关键帧维护的图断裂,或者混乱,不可以为后端提供较好的初值。函数
理解起来就是父亲挂了,儿子须要找新的父亲,在候选父亲里找,当前帧的父亲(mpParent)确定在候选父亲中的;oop
1. 首先将当前帧的父亲,放入候选父亲中spa
sParentCandidates.insert(mpParent);
2. 遍历当前帧的全部儿子,而后遍历儿子A的每一个共视帧,若是其中有候选父亲,则将A的父亲更新为该候选父亲,而且将A放入候选父亲中(由于这时候A已经将整个图联系起来了);若是没有,break。若是遍历一圈下来,发现有的儿子尚未找到新父亲,例如儿子B的共视帧不是候选父亲里的任何一个。这种状况出如今,B和当前帧的父亲不存在共视关系(速度太快,旋转太急,匹配跟丢)。而且B与当前帧的儿子之间也没有共视关系:当前帧不是一个好的关键帧,原本就没有多少儿子;或者B自己是个例外,恩,反正B是个孤家寡人。。。那么直接将B的父亲设置为当前帧的父亲,交给爷爷去管。code
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(!mspChildrens.empty()) for(set<KeyFrame*>::iterator sit=mspChildrens.begin(); sit!=mspChildrens.end(); sit++) { (*sit)->ChangeParent(mpParent); }
3. 具体删除一个关键帧的步骤是这样的:blog
1) 初始mbNotErase状态是true,那么调用SetBadFlag后,将mbToBeErased状态置为true,而后return,并无执行SetBadFlag()中后面的代码。ci
2) 而后调用SetErase(),这时首先要检查mspLoopEdges是不是空的!由于若是当前帧维护了一个回环,删了该关键帧回环就没了。。。一般状况下是空的,那么把mbNotErase置为false,此时再在SetErase()中调用SetBagFlag时,就会真正去执行删除该帧的代码了。it
总结一下就是,首先设置为坏帧,若是该帧不是回环帧,则能够真的删掉;若是该帧是回环帧,怎么都删不掉的。。。class