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時,就會真正去執行刪除該幀的代碼了。
總結一下就是,首先設置為壞幀,如果該幀不是回環幀,則可以真的刪掉;如果該幀是回環幀,怎么都刪不掉的。。。