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