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

news2025/4/6 6:47:56

目录

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置为与当前帧共视程度最高的一级关键帧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/87090.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【在SpringBoot项目中删除相册数据--Service层】

由于AlbumMapper.java中已经实现了“根据id删除数据表中的相册数据”&#xff0c;所以&#xff0c;可以直接从Service开始开发此功能。 先在IAlbumService中添加抽象方法&#xff1a; /** * 删除相册 * param id 尝试删除的相册的id */ void delete(Long id); 然后&#xf…

[附源码]Python计算机毕业设计SSM基于web的烟草售卖系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Android Studio Gradle 无限 Build「假死」

Android Studio Gradle 无限 Build「假死」 解决方案 工程配置 Gradle 的 gradle.properties 文件中添加如下代码&#xff1a; org.gradle.daemontrue org.gradle.paralleltrue然后重新 Sync 工程&#xff0c;漫长等待依赖下载完成后&#xff0c;重新 Build 项目即可 补充 …

【Unity 3D 从入门到实战】Unity 3D 组件总结

目录 一&#xff0c;前言 二&#xff0c;常用组件 1&#xff0c;Transform 组件 2&#xff0c;Mesh Filter 组件 3&#xff0c;Box Collider 组件 4&#xff0c;Rigidbody 组件 5&#xff0c;脚本组件 三&#xff0c;总结 一&#xff0c;前言 组件是 Unity 3D 中的重要…

Attention Is All You Need

摘要 The dominant sequence transduction models 显性序列转换模型。complex recurrent 复杂的递归。convolutional neural networks 卷积神经网络。an encoder and a decoder 编码器和解码器。The best performing models性能最佳的模型the encoder →\rightarrow→ an atte…

D. Lucky Chains

传送门 题意&#xff1a;给你两个数x,y让你求满足xk,yk并且GCD(xk,yk)!1的最小值。 思路&#xff1a;要求满足xk,yk并且GCD(xk,yk)!1的最小值&#xff0c;那么就相等于存在(xk)%d(yk)%d 变化一下就等于(x-y)%d0&#xff0c;那么就是找x-y的约数&#xff0c;然后这个约数可能是…

疫情散去想看电影,用css动画模拟一个阿凡达2完美开场

在历经了艰苦卓绝的3年抗疫后&#xff0c;疫情终于还是来了&#xff0c;很多小伙伴变成了小洋人酸奶&#xff0c;我相信过不了多少天&#xff0c;疫情终将散去&#xff0c;那个时候就可以和家人走进电影院啦。 今天用css布局一个阿凡达2的影院场景&#xff0c;提前过一过瘾。 目…

ARM 异常返回地址

armv7-a armv8-a linux5.15 Preferred exception return address armv7 exception return address correction armv8-32 exception return address correction armv8-64 没有查到link value offset表 arm32 data abort exception为例 周期1周期2周期3周期4周期5指令1pc-8取指…

[附源码]Node.js计算机毕业设计电影推荐系统Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

【LSTM预测】基于鲸鱼算法优化双向长短时记忆BiLSTM(多输入单输出)航空发动机寿命预测含Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

CloudCompare算法库(CCCorelib)编译与使用

文章目录一、简介二、编译过程三、举个栗子优秀的算法库&#xff0c;值得所有点云爱好者去使用和研究*~*。 一、简介 之前一直没注意到CloudCompare的作者&#xff08;光头大佬&#xff09;已经将CloudCompare的算法库与其可视化部分进行了分离&#xff0c;今天正好有同学问了这…

以太坊地址Address介绍附代码示例

Address是什么 通常情况下&#xff0c;地址代表一外部账户或合约账户&#xff0c;它们都可以在区块链上接收&#xff08;目标地址&#xff09;或发送&#xff08;源地址&#xff09;。 更具体地说&#xff0c;它是根据ECDSA算法&#xff0c;从公钥的Keccak-256哈希值的最后20个…

MapRecuce 词频统计案例

文章目录初探MapReduce一、MapReduce核心思想二、MapReduce编程实例-词频统计思路1、map阶段(映射)2、reduce阶段(归并阶段)三、词频统计编程实现1、准备数据文件2、将文件上传到hdfs指定路径3、在java里创建词频统计映射器类4、创建词频统计驱动类5、运行词频统计驱动类&#…

【机器学习】SVM多分类问题及基于sklearn的Python代码实现

SVM多分类问题及Python代码实现1. 什么是SVM&#xff1f;2. SVM的分类3. SVM决策函数类型4. SVM多分类的Python代码实现参考资料1. 什么是SVM&#xff1f; 对于这个点已经介绍的非常多了&#xff0c;不管是西瓜书还是各种博客&#xff0c;就是需要找到一个超平面&#xff0c;用…

【Redis】Zset和Hash类型

Hash类型 Redis hash 是一个键值对集合。Redis hash是一个string类型的field和value的映射表&#xff0c;hash特别适合用于存储对象。 Hash 类型的底层数据结构是由压缩列表或哈希表实现的&#xff1a; 如果哈希类型元素小于512个&#xff0c;所有值的大小小于64字节。Redis…

一不小心手动实现了k8s的自动化构建

背景 由于公司需要对公司内部的软件需要对外部署&#xff0c;对于前端的部署&#xff0c;需要一套部署的方案。尝试了写了一个适配多模块可配置的部署脚本&#xff0c;本文对实现的过程进行一个记录。 目的 脚本采用的是node&#xff0c;前端同学的首选。脚本的目的就是实现k8s…

企业在ERP系统下的全面预算管理系统的实现

现代企业的发展离不开各种管理系统的建立和应用&#xff0c;针对于企业预算管理而言&#xff0c;全面的管理系统对于企业实现现代化管理有着较为深入的影响&#xff0c;ERP系统可以帮助企业更好的实现此项功能&#xff0c;本文就是在此背景下展开论述的。 编辑搜图 一、全面预算…

文件上传自动化测试方案

一、概述 【测试地址】&#xff1a;https://pan.baidu.com 【测试工具】selenium、requests 【脚本语言】Python 【运行环境】Windows 百度网盘作为文件存储及分享的平台&#xff0c;核心功能大部分是对文件的操作&#xff0c;如果要对它进行自动化测试&#xff0c;优先覆…

马克思主义基本原理笔记(黄色标记要求会背)

马克思主义基本组成部分 马克思主义哲学马克思主义政治经济学科学社会主义历史学、政治学、法学、文化学、新闻学、军事学等 唯物主义&#xff0c;唯心主义划分标准 唯物主义&#xff1a;把世界的本原归结为物质&#xff0c;主张物质是第一性&#xff0c;意识第二性&#xff0c…

Docker容器网络入门

1、查看默认网络模式 首先假定已经安装好docker了&#xff0c;不会安装的可以看我其他文章&#xff0c;很简单。docker安装好后默认是提供三种网络模式&#xff08;bridge、host、none&#xff09;&#xff0c;可以使用命令docker network ls查看网络状态 [rootlocalhost ~]#…