ORB-SLAM2 --- Tracking::UpdateLocalKeyFrames函数

目录

1.函数作用

2. 函数步骤

3.code 

4.函数解析 

4.1 记录共视

4.2  更新局部关键帧(mvpLocalKeyFrames)

4.3 更新当前帧的参考关键帧,与自己共视程度最高的关键帧作为参考关键帧 


1.函数作用

        跟踪局部地图函数里,更新局部关键帧。

2. 函数步骤

        方法是遍历当前帧的地图点,将观测到这些地图点的关键帧和相邻的关键帧及其父子关键帧,作为mvpLocalKeyFrames。
Step 1:遍历当前帧的地图点,记录所有能观测到当前帧地图点的关键帧 
Step 2:更新局部关键帧(mvpLocalKeyFrames),添加局部关键帧包括以下3种类型
 *      类型1:能观测到当前帧地图点的关键帧,也称一级共视关键帧
 *      类型2:一级共视关键帧的共视关键帧,称为二级共视关键帧
 *      类型3:一级共视关键帧的子关键帧、父关键帧
Step 3:更新当前帧的参考关键帧,与自己共视程度最高的关键帧作为参考关键帧

3.code 

void Tracking::UpdateLocalKeyFrames()
{
    // Each map point vote for the keyframes in which it has been observed
    // Step 1:遍历当前帧的地图点,记录所有能观测到当前帧地图点的关键帧
    map<KeyFrame*,int> keyframeCounter;
    for(int i=0; i<mCurrentFrame.N; i++)
    {
        if(mCurrentFrame.mvpMapPoints[i])
        {
            MapPoint* pMP = mCurrentFrame.mvpMapPoints[i];
            if(!pMP->isBad())
            {
                // 得到观测到该地图点的关键帧和该地图点在关键帧中的索引
                const map<KeyFrame*,size_t> observations = pMP->GetObservations();
                // 由于一个地图点可以被多个关键帧观测到,因此对于每一次观测,都对观测到这个地图点的关键帧进行累计投票
                for(map<KeyFrame*,size_t>::const_iterator it=observations.begin(), itend=observations.end(); it!=itend; it++)
                    // 这里的操作非常精彩!
                    // map[key] = value,当要插入的键存在时,会覆盖键对应的原来的值。如果键不存在,则添加一组键值对
                    // it->first 是地图点看到的关键帧,同一个关键帧看到的地图点会累加到该关键帧计数
                    // 所以最后keyframeCounter 第一个参数表示某个关键帧,第2个参数表示该关键帧看到了多少当前帧(mCurrentFrame)的地图点,也就是共视程度
                    keyframeCounter[it->first]++;      
            }
            else
            {
                mCurrentFrame.mvpMapPoints[i]=NULL;
            }
        }
    }

    // 没有当前帧没有共视关键帧,返回
    if(keyframeCounter.empty())
        return;

    // 存储具有最多观测次数(max)的关键帧
    int max=0;
    KeyFrame* pKFmax= static_cast<KeyFrame*>(NULL);

    // Step 2:更新局部关键帧(mvpLocalKeyFrames),添加局部关键帧有3种类型
    // 先清空局部关键帧
    mvpLocalKeyFrames.clear();
    // 先申请3倍内存,不够后面再加
    mvpLocalKeyFrames.reserve(3*keyframeCounter.size());

    // All keyframes that observe a map point are included in the local map. Also check which keyframe shares most points
    // Step 2.1 类型1:能观测到当前帧地图点的关键帧作为局部关键帧 (将邻居拉拢入伙)(一级共视关键帧) 
    for(map<KeyFrame*,int>::const_iterator it=keyframeCounter.begin(), itEnd=keyframeCounter.end(); it!=itEnd; it++)
    {
        KeyFrame* pKF = it->first;

        // 如果设定为要删除的,跳过
        if(pKF->isBad())
            continue;
        
        // 寻找具有最大观测数目的关键帧
        if(it->second>max)
        {
            max=it->second;
            pKFmax=pKF;
        }

        // 添加到局部关键帧的列表里
        mvpLocalKeyFrames.push_back(it->first);
        
        // 用该关键帧的成员变量mnTrackReferenceForFrame 记录当前帧的id
        // 表示它已经是当前帧的局部关键帧了,可以防止重复添加局部关键帧
        pKF->mnTrackReferenceForFrame = mCurrentFrame.mnId;
    }


    // Include also some not-already-included keyframes that are neighbors to already-included keyframes
    // Step 2.2 遍历一级共视关键帧,寻找更多的局部关键帧 
    for(vector<KeyFrame*>::const_iterator itKF=mvpLocalKeyFrames.begin(), itEndKF=mvpLocalKeyFrames.end(); itKF!=itEndKF; itKF++)
    {
        // Limit the number of keyframes
        // 处理的局部关键帧不超过80帧
        if(mvpLocalKeyFrames.size()>80)
            break;

        KeyFrame* pKF = *itKF;

        // 类型2:一级共视关键帧的共视(前10个)关键帧,称为二级共视关键帧(将邻居的邻居拉拢入伙)
        // 如果共视帧不足10帧,那么就返回所有具有共视关系的关键帧
        const vector<KeyFrame*> vNeighs = pKF->GetBestCovisibilityKeyFrames(10);
        // vNeighs 是按照共视程度从大到小排列
        for(vector<KeyFrame*>::const_iterator itNeighKF=vNeighs.begin(), itEndNeighKF=vNeighs.end(); itNeighKF!=itEndNeighKF; itNeighKF++)
        {
            KeyFrame* pNeighKF = *itNeighKF;
            if(!pNeighKF->isBad())
            {
                // mnTrackReferenceForFrame防止重复添加局部关键帧
                if(pNeighKF->mnTrackReferenceForFrame!=mCurrentFrame.mnId)
                {
                    mvpLocalKeyFrames.push_back(pNeighKF);
                    pNeighKF->mnTrackReferenceForFrame=mCurrentFrame.mnId;
                    //? 找到一个就直接跳出for循环?
                    break;
                }
            }
        }

        // 类型3:将一级共视关键帧的子关键帧作为局部关键帧(将邻居的孩子们拉拢入伙)
        const set<KeyFrame*> spChilds = pKF->GetChilds();
        for(set<KeyFrame*>::const_iterator sit=spChilds.begin(), send=spChilds.end(); sit!=send; sit++)
        {
            KeyFrame* pChildKF = *sit;
            if(!pChildKF->isBad())
            {
                if(pChildKF->mnTrackReferenceForFrame!=mCurrentFrame.mnId)
                {
                    mvpLocalKeyFrames.push_back(pChildKF);
                    pChildKF->mnTrackReferenceForFrame=mCurrentFrame.mnId;
                    //? 找到一个就直接跳出for循环?
                    break;
                }
            }
        }

        // 类型3:将一级共视关键帧的父关键帧(将邻居的父母们拉拢入伙)
        KeyFrame* pParent = pKF->GetParent();
        if(pParent)
        {
            // mnTrackReferenceForFrame防止重复添加局部关键帧
            if(pParent->mnTrackReferenceForFrame!=mCurrentFrame.mnId)
            {
                mvpLocalKeyFrames.push_back(pParent);
                pParent->mnTrackReferenceForFrame=mCurrentFrame.mnId;
                //! 感觉是个bug!如果找到父关键帧会直接跳出整个循环
                break;
            }
        }

    }

    // Step 3:更新当前帧的参考关键帧,与自己共视程度最高的关键帧作为参考关键帧
    if(pKFmax)
    {
        mpReferenceKF = pKFmax;
        mCurrentFrame.mpReferenceKF = mpReferenceKF;
    }
}

4.函数解析 

4.1 记录共视

        我们遍历当前帧的所有地图点mCurrentFrame.mvpMapPoints[i]

        若地图点存在,用临时变量pMP存储该地图点,若该地图点没有被标记为isBad,则我们用一个map<KeyFrame*, size_t>型变量observations存储能观测到该地图点的关键帧及该地图点在关键帧中的索引,我们遍历每一个能看见该地图点的关键帧:

        我们维护了一个map<KeyFrame*,int> keyframeCounter变量存储了当前帧与当前帧有共视地图点的帧的共视程度,比如keyframeCounter[KeyFrame5,10]表示关键帧5和当前帧有的共视地图点为5个。

        如下图:我们这步做的是找到所有红色关键帧和当前帧mCurrentFrame的共视地图点的数目并存储在keyframeCounter中。

         如果keyframeCounter为空,则表示和当前关键帧有共视程度的关键帧为0,追踪局部地图失败!

4.2  更新局部关键帧(mvpLocalKeyFrames)

        添加局部关键帧有3种类型:

        1.能观测到当前帧地图点的关键帧作为局部关键帧 (将邻居拉拢入伙)(一级共视关键帧)

        这个就是我们如上图所示的红色关键帧,红色关键帧我们存储在了keyframeCounter的第一维中,我们遍历它的第一维,将和当前关键帧有共视的关键帧添加到局部关键帧的列表里,并将该一级共视帧的mnTrackReferenceForFrame标记置为当前帧的ID避免在接下来两步重复添加,并在keyframeCounter中寻找具有最大观测数目的共视关键帧pKFmax和它的得分max

        2.遍历一级共视关键帧,寻找更多的局部关键帧 

        我们遍历当前关键帧mCurrentFrame的一级关键帧的共视关键帧(和当前关键帧mCurrentFrame有共视的二级关键帧,如上图中绿色帧所示)

        如果这个二级共视关键帧没有被添加过(mnTrackReferenceForFrame标记不是mCurrentFrame的ID),则也将这个关键帧添加到局部关键帧mvpLocalKeyFrames中。

        3.将一级共视关键帧的子关键帧、父关键帧作为局部关键帧(将邻居的孩子们拉拢入伙、将邻居的父母们拉拢入伙)

        这里没有过多的筛选条件,只要这个关键帧没有被添加过就可以。(mnTrackReferenceForFrame标记不是mCurrentFrame的ID

4.3 更新当前帧的参考关键帧,与自己共视程度最高的关键帧作为参考关键帧 

        至此,我们的局部地图的关键帧mvpLocalKeyFrames更新完了。

        最后,我们将当前帧的参考关键帧mCurrentFrame.mpReferenceKF置为与当前帧共视程度最高的一级关键帧。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

APS2023

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值