ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

ORB_SLAM2关键帧数据库

2021-02-23 23:29:05  阅读:363  来源: 互联网

标签:候选 关键帧 pKFi lit SLAM2 pKF 单词 ORB


关键帧数据库

  注意区分词典和关键帧数据库的区别。
词典:事先训练好的,用来计算关键帧的词袋信息。
关键帧数据库:根据关键帧的词袋生成,在回环检测、重定位的时候使用,寻找与当前帧最相似的候选关键帧。
  在局部建图(LocalMapping)线程的ProcessNewKeyFrame函数中,计算了每个关键帧的词袋信息:

mpCurrentKeyFrame->ComputeBoW();

在这里插入图片描述

关键帧数据库中存放着所有关键帧的单词信息。形式为:
单词1:关键帧(1)(2)(3)
单词2:关键帧(3)(4)(5)
单词3:关键帧(5)(6)(7)

其作用为:在回环检测、重定位的时候,搜索与当前帧最相似的候选关键帧集合。

std::vector<list<KeyFrame*> > mvInvertedFile; 

关键帧数据库。mvInvertedFile[i]表示含有第i个单词的关键帧的id组成的链表。

DBoW2::BowVector mBowVec; 

mBowVec 内部实际存储的是std::map<WordId, WordValue>
WordId 和 WordValue 表示Word在叶子中的id 和权重

构造函数

KeyFrameDatabase::KeyFrameDatabase (const ORBVocabulary &voc):
    mpVoc(&voc)
{
    // 数据库的主要内容了
    mvInvertedFile.resize(voc.size()); // number of words
}

其中mvInvertedFile的数据类型为:

std::vector<list<KeyFrame*> > mvInvertedFile; 

mvInvertedFile[i]表示含有第i个单词的关键帧组成的链表。

关键帧插入关键帧数据库中

// 根据关键帧的BoW,更新数据库的倒排索引
void KeyFrameDatabase::add(KeyFrame *pKF)
{
    unique_lock<mutex> lock(mMutex);

    // 为每一个word添加该KeyFrame
    for(DBoW2::BowVector::const_iterator vit= pKF->mBowVec.begin(), vend=pKF->mBowVec.end(); vit!=vend; vit++)
        mvInvertedFile[vit->first].push_back(pKF);
}


关键帧数据库中删除关键帧

通过mBowVec找到该关键帧包含哪些单词,然后在mvInvertedFile中将相应的关键帧删掉。

// 关键帧被删除后,更新数据库的倒排索引
void KeyFrameDatabase::erase(KeyFrame* pKF)
{
    unique_lock<mutex> lock(mMutex);

    // Erase elements in the Inverse File for the entry
    // 每一个KeyFrame包含多个words,遍历mvInvertedFile中的这些words,然后在word中删除该KeyFrame
    for(DBoW2::BowVector::const_iterator vit=pKF->mBowVec.begin(), vend=pKF->mBowVec.end(); vit!=vend; vit++)
    {
        // List of keyframes that share the word

        list<KeyFrame*> &lKFs = mvInvertedFile[vit->first];
        // 这个效率有点低啊
        for(list<KeyFrame*>::iterator lit=lKFs.begin(), lend= lKFs.end(); lit!=lend; lit++)
        {
            if(pKF==*lit)
            {
                lKFs.erase(lit);
                break;
            }
        }
    }
}


清空关键帧数据库

void KeyFrameDatabase::clear()
{
    mvInvertedFile.clear();// mvInvertedFile[i]表示包含了第i个word id的所有关键帧
    mvInvertedFile.resize(mpVoc->size());// mpVoc:预先训练好的词典
}


获得回环检测时的候选关键帧

vector<KeyFrame*> KeyFrameDatabase::DetectLoopCandidates(KeyFrame* pKF, float minScore)

  寻找可能与当前帧构成回环的关键帧。
(1)找出与当前帧具有相同单词的关键帧。
(2)设置最多的相同单词的880%为阈值,根据词典计算当前帧与候选帧的相似度。
(3)计算上述候选帧对应的共视关键帧组的总得分,只取最高组得分75%以上的组。
(4)得到上述组中分数最高的关键帧作为闭环候选关键帧。

找出与当前帧的共视点大于15的关键帧

set<KeyFrame*> spConnectedKeyFrames = pKF->GetConnectedKeyFrames();

寻找与当前帧有相同单词的关键帧

 &emps;寻找与当前帧有相同单词的关键帧,并将其放入到链表list<KeyFrame*> lKFsSharingWords;中。其中变量mnLoopWords记录了当前帧与候选帧具有的相同的单词个数。

    // 用于保存可能与当前关键帧形成闭环的候选帧(只要有相同的word,且不属于局部相连(共视)帧)
    list<KeyFrame*> lKFsSharingWords;

    // Search all keyframes that share a word with current keyframes
    // Discard keyframes connected to the query keyframe
    // Step 1:找出和当前帧具有公共单词的所有关键帧,不包括与当前帧连接(也就是共视)的关键帧
    {
        unique_lock<mutex> lock(mMutex);

        // words是检测图像是否匹配的枢纽,遍历该pKF的每一个word
        // mBowVec 内部实际存储的是std::map<WordId, WordValue>
        // WordId 和 WordValue 表示Word在叶子中的id 和权重
        for(DBoW2::BowVector::const_iterator vit=pKF->mBowVec.begin(), vend=pKF->mBowVec.end(); vit != vend; vit++)
        {
            // 提取所有包含该word的KeyFrame
            list<KeyFrame*> &lKFs =   mvInvertedFile[vit->first];
            // 然后对这些关键帧展开遍历
            for(list<KeyFrame*>::iterator lit=lKFs.begin(), lend= lKFs.end(); lit!=lend; lit++)
            {
                KeyFrame* pKFi=*lit;
                
                if(pKFi->mnLoopQuery!=pKF->mnId)    
                {
                    // 还没有标记为pKF的闭环候选帧
                    pKFi->mnLoopWords=0;
                    // 和当前关键帧共视的话不作为闭环候选帧
                    if(!spConnectedKeyFrames.count(pKFi))
                    {
                        // 没有共视就标记作为闭环候选关键帧,放到lKFsSharingWords里
                        pKFi->mnLoopQuery=pKF->mnId;
                        lKFsSharingWords.push_back(pKFi);
                    }
                }
                pKFi->mnLoopWords++;// 记录pKFi与pKF具有相同word的个数
            }
        }
    }

设置最少单词阈值

    // Only compare against those keyframes that share enough words
    // Step 2:统计上述所有闭环候选帧中与当前帧具有共同单词最多的单词数,用来决定相对阈值 
    int maxCommonWords=0;
    for(list<KeyFrame*>::iterator lit=lKFsSharingWords.begin(), lend= lKFsSharingWords.end(); lit!=lend; lit++)
    {
        if((*lit)->mnLoopWords>maxCommonWords)
            maxCommonWords=(*lit)->mnLoopWords;
    }

    // 确定最小公共单词数为最大公共单词数目的0.8倍
    int minCommonWords = maxCommonWords*0.8f;

挑选候选关键帧

list<pair<float,KeyFrame*> > lScoreAndMatch;存放所有满足条件的候选关键帧。其中float变量是计算出来的两帧的词典相似度,KeyFrame*为候选关键帧。

    // Compute similarity score. Retain the matches whose score is higher than minScore
    // Step 3:遍历上述所有闭环候选帧,挑选出共有单词数大于minCommonWords且单词匹配度大于minScore存入lScoreAndMatch
    for(list<KeyFrame*>::iterator lit=lKFsSharingWords.begin(), lend= lKFsSharingWords.end(); lit!=lend; lit++)
    {
        KeyFrame* pKFi = *lit;

        // pKF只和具有共同单词较多(大于minCommonWords)的关键帧进行比较
        if(pKFi->mnLoopWords>minCommonWords)
        {
            nscores++;// 这个变量后面没有用到

            // 用mBowVec来计算两者的相似度得分
            float si = mpVoc->score(pKF->mBowVec,pKFi->mBowVec);

            pKFi->mLoopScore = si;
            if(si>=minScore)
                lScoreAndMatch.push_back(make_pair(si,pKFi));
        }
    }

计算候选关键帧的共视关键帧与当前帧的得分

  单单计算当前帧和某一候选关键帧的相似性是不够的,这里将与候选关键帧相连(权值最高,共视程度最高)的前十个关键帧归为一组,计算累计得分。在这十个关键帧中选择一个最好的插入到lAccScoreAndMatch中。

    for(list<pair<float,KeyFrame*> >::iterator it=lScoreAndMatch.begin(), itend=lScoreAndMatch.end(); it!=itend; it++)
    {
        KeyFrame* pKFi = it->second;
        vector<KeyFrame*> vpNeighs = pKFi->GetBestCovisibilityKeyFrames(10);

        float bestScore = it->first; // 该组最高分数
        float accScore = it->first;  // 该组累计得分
        KeyFrame* pBestKF = pKFi;    // 该组最高分数对应的关键帧
        // 遍历共视关键帧,累计得分 
        for(vector<KeyFrame*>::iterator vit=vpNeighs.begin(), vend=vpNeighs.end(); vit!=vend; vit++)
        {
            KeyFrame* pKF2 = *vit;
            // 只有pKF2也在闭环候选帧中,且公共单词数超过最小要求,才能贡献分数
            if(pKF2->mnLoopQuery==pKF->mnId && pKF2->mnLoopWords>minCommonWords)
            {
                accScore+=pKF2->mLoopScore;
                // 统计得到组里分数最高的关键帧
                if(pKF2->mLoopScore>bestScore)
                {
                    pBestKF=pKF2;
                    bestScore = pKF2->mLoopScore;
                }
            }
        }

        lAccScoreAndMatch.push_back(make_pair(accScore,pBestKF));
        // 记录所有组中组得分最高的组,用于确定相对阈值
        if(accScore>bestAccScore)
            bestAccScore=accScore;
    }

获得闭环候选关键帧

将lAccScoreAndMatch中的得分大于阈值的关键帧插入vpLoopCandidates中并返回。

    for(list<pair<float,KeyFrame*> >::iterator it=lAccScoreAndMatch.begin(), itend=lAccScoreAndMatch.end(); it!=itend; it++)
    {
        if(it->first>minScoreToRetain)
        {
            KeyFrame* pKFi = it->second;
            // spAlreadyAddedKF 是为了防止重复添加
            if(!spAlreadyAddedKF.count(pKFi))
            {
                vpLoopCandidates.push_back(pKFi);
                spAlreadyAddedKF.insert(pKFi);
            }
        }
    }

在这里插入图片描述


获得重定位时的候选关键帧

  过程与回环检测时的类似,但没有设置最小得分。
(1)找到与当前帧具有相同的单词的所有关键帧。
(2)根据阈值进行筛选,使用词典计算当前帧和重定位候选关键帧的相似度得分。
(3)将与重定位候选关键帧相连的十个关键帧看为一组,计算总得分,并选出其中得分最高的关键帧
(4)返回累计得分最高的几个组中的最好的关键帧

vector<KeyFrame*> KeyFrameDatabase::DetectRelocalizationCandidates(Frame *F)

标签:候选,关键帧,pKFi,lit,SLAM2,pKF,单词,ORB
来源: https://blog.csdn.net/weixin_43828675/article/details/113995402

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有