构建关键帧数据库,可以联系链表等常用数据结构的构建过程:创建、增加元素、删除元素、清理。
首先需要明确数据存储的数据类型:以关键帧作为数据库的元素。
这个地方需要理解两个概念:单词(词袋)和关键帧。
单词(词袋):预先构建好的,离线词典(ORBvoc.txt):它是DBoW2作者使用orb特征,使用大量图片训练的结果。
std::vector> mvInvertedFile;
vector的索引是预先训练好的特征词汇,词汇的值就是0到n,所以可以当作索引来使用。
vector对应的索引处存放包含该词汇的所有关键帧。
| 函数 | 用途 |
|---|---|
| KeyFrameDatabase(const ORBVocabulary &voc); | 构造函数 |
| void add(KeyFrame* pKF); | 根据关键帧的词汇,把关键帧添加到数据库 |
| void erase(KeyFrame* pKF); | 从数据库中删除相应的关键帧 |
| void clear(); | 清空关键帧数据库 |
| 函数 | 用途 |
|---|---|
| std::vector | 在闭环检测中找到与该关键帧可能闭环的关键帧 |
| std::vector | 在重定位中找到与该帧相似的关键帧 |
- // Loop Detection
- std::vector
DetectLoopCandidates(KeyFrame* pKF, float minScore) ;
这个函数的流程和DetectRelocalizationCandidates唯一的区别是忽略和自己已有共视关系的关键帧,所以我们此处不复制全部的代码了,只重点强调不同的地方
- for(DBoW2::BowVector::const_iterator vit=pKF->mBowVec.begin(), vend=pKF->mBowVec.end(); vit != vend; vit++)
- {
- list
&lKFs = mvInvertedFile[vit->first]; - for(list
::iterator lit=lKFs.begin(), lend= lKFs.end(); lit!=lend; lit++) - {
- KeyFrame* pKFi=*lit;
- if(pKFi->mnLoopQuery!=pKF->mnId)
- {
- pKFi->mnLoopWords=0;
- // 此处如果if条件成立,代表没有共视关系,此时才会进入执行语句
- // 换言之,如果有共视关系,就直接忽略了,这是它和DetectRelocalizationCandidates唯一的区别
- if(!spConnectedKeyFrames.count(pKFi))
- {
- pKFi->mnLoopQuery=pKF->mnId;
- lKFsSharingWords.push_back(pKFi);
- }
- }
- pKFi->mnLoopWords++;
- }
- }
- // Relocalization
- std::vector
DetectRelocalizationCandidates(Frame* F) ;
检测的主要步骤如下:
1)找出与当前帧pKF有公共单词的所有关键帧pKFi,不包括与当前帧相连的关键帧。
2)统计所有闭环候选帧中与pKF具有共同单词最多的单词数,只考虑共有单词数大于0.8*maxCommonWords以及匹配得分大于给定的minScore的关键帧,存入lScoreAndMatch。
3)对于第二步中筛选出来的pKFi,每一个都要抽取出自身的共视(共享地图点最多的前10帧)关键帧分为一组,计算该组整体得分(与pKF比较的),记为bestAccScore。所有组得分大于0.75*bestAccScore的,均当作闭环候选帧。
- vector
KeyFrameDatabase::DetectRelocalizationCandidates(Frame *F) - {
- list
lKFsSharingWords; -
- // Search all keyframes that share a word with current frame
- //搜索所有和和F有着相同单词的keyframe存储在lKFsSharingWords
- //并且更新keyframe中mnRelocWords,表示和此F有多少共同的单词
- {
- unique_lock
lock(mMutex) ; -
- for(DBoW2::BowVector::const_iterator vit=F->mBowVec.begin(), vend=F->mBowVec.end(); vit != vend; vit++)
- {
- list
&lKFs = mvInvertedFile[vit->first]; -
- for(list
::iterator lit=lKFs.begin(), lend= lKFs.end(); lit!=lend; lit++) - {
- KeyFrame* pKFi=*lit;
- if(pKFi->mnRelocQuery!=F->mnId)
- {
- pKFi->mnRelocWords=0;
- pKFi->mnRelocQuery=F->mnId;
- lKFsSharingWords.push_back(pKFi);
- }
- pKFi->mnRelocWords++;
- }
- }
- }
- if(lKFsSharingWords.empty())
- return vector
(); -
- // Only compare against those keyframes that share enough words
- //在lKFsSharingWords中,寻找mnRelocWords的最大值存入maxCommonWords
- int maxCommonWords=0;
- for(list
::iterator lit=lKFsSharingWords.begin(), lend= lKFsSharingWords.end(); lit!=lend; lit++) - {
- if((*lit)->mnRelocWords>maxCommonWords)
- maxCommonWords=(*lit)->mnRelocWords;
- }
-
- int minCommonWords = maxCommonWords*0.8f;
-
- list
float,KeyFrame*> > lScoreAndMatch; -
- int nscores=0;
-
- // Compute similarity score.
- //遍历lKFsSharingWords中的keyframe,当其中的keyframe的mRelocScore大于阈值minCommonWords则计算相似度后放入lScoreAndMatch中
- for(list
::iterator lit=lKFsSharingWords.begin(), lend= lKFsSharingWords.end(); lit!=lend; lit++) - {
- KeyFrame* pKFi = *lit;
-
- if(pKFi->mnRelocWords>minCommonWords)
- {
- nscores++;
- float si = mpVoc->score(F->mBowVec,pKFi->mBowVec);
- pKFi->mRelocScore=si;
- lScoreAndMatch.push_back(make_pair(si,pKFi));
- }
- }
-
- if(lScoreAndMatch.empty())
- return vector
(); -
- list
float,KeyFrame*> > lAccScoreAndMatch; - float bestAccScore = 0;
-
- // Lets now accumulate score by covisibility
- //遍历lScoreAndMatch中的keyframe,找出其共视图中与此keyframe连接的权值前N的节点,加上原keyframe总共11个keyframe
- //累加这11个keyframe的相似度得分,然后在11个keyframe中选择相似度得分最高的那个放入lAccScoreAndMatch中
- //在遍历过程中计算bestAccScore,也就是AccScore的最大值,后面的再次筛选有用
- for(list
float,KeyFrame*> >::iterator it=lScoreAndMatch.begin(), itend=lScoreAndMatch.end(); it!=itend; it++) - {
- KeyFrame* pKFi = it->second;
- //返回共视图中与此keyframe连接的权值前10的节点keyframe
- vector
vpNeighs = pKFi->GetBestCovisibilityKeyFrames(10); -
- float bestScore = it->first;
- float accScore = bestScore;
- KeyFrame* pBestKF = pKFi;
- for(vector
::iterator vit=vpNeighs.begin(), vend=vpNeighs.end(); vit!=vend; vit++) - {
- KeyFrame* pKF2 = *vit;
- //说明pKF2与F没有共同的单词,就放弃此循环的关键帧
- if(pKF2->mnRelocQuery!=F->mnId)
- continue;
-
- accScore+=pKF2->mRelocScore;
- if(pKF2->mRelocScore>bestScore)
- {
- pBestKF=pKF2;
- bestScore = pKF2->mRelocScore;
- }
-
- }
- lAccScoreAndMatch.push_back(make_pair(accScore,pBestKF));
- if(accScore>bestAccScore)
- bestAccScore=accScore;
- }
-
- // Return all those keyframes with a score higher than 0.75*bestScore
- //返回lAccScoreAndMatch中所有得分超过0.75*bestAccScore的keyframe集合
- float minScoreToRetain = 0.75f*bestAccScore;
- set
spAlreadyAddedKF; - vector
vpRelocCandidates; - vpRelocCandidates.reserve(lAccScoreAndMatch.size());
- for(list
float,KeyFrame*> >::iterator it=lAccScoreAndMatch.begin(), itend=lAccScoreAndMatch.end(); it!=itend; it++) - {
- const float &si = it->first;
- if(si>minScoreToRetain)
- {
- KeyFrame* pKFi = it->second;
- if(!spAlreadyAddedKF.count(pKFi))
- {
- vpRelocCandidates.push_back(pKFi);
- spAlreadyAddedKF.insert(pKFi);
- }
- }
- }
-
- return vpRelocCandidates;
- }
ORB-SLAM2之KeyFrameDataBase_菜菜的阿远的博客-CSDN博客
ORB SLAM2源码解读(五):KeyFrame DataBase类 - 古月居
【SLAM学习笔记】3-ORB_SLAM3关键源码分析① KeyFrameDatabase(二)_口哨糖youri的博客-CSDN博客