ORB-SLAM2 --- KeyFrame::UpdateConnections 函数

news2024/9/22 9:59:09

目录

一、函数作用

二、函数流程 

三、code 

四、函数解析 


一、函数作用

        更新关键帧之间的连接图。

        更新变量

        @mConnectedKeyFrameWeights:当前关键帧的共视信息,记录当前关键帧共视关键帧的信息(哪一帧和当前关键帧有共视,共视程度是多少)

        @mvpOrderedConnectedKeyFrames:对mConnectedKeyFrameWeights中超过共视阈值的关键帧(按照共视程度从大到小排序)

        @mvOrderedWeights:对mConnectedKeyFrameWeights中超过共视阈值的关键帧的共视程度(按照共视程度从大到小排序)

        @mpParent:共视程度最高的那个关键帧

        @mspChildrens:建立双向关系,将当前帧的共视程度最高的那个关键帧的子关键帧添加当前帧

二、函数流程 

1. 首先获得该关键帧的所有MapPoint点,统计观测到这些3d点的每个关键帧与其它所有关键帧之间的共视程度对每一个找到的关键帧,建立一条边,边的权重是该关键帧与当前关键帧公共3d点的个数。
2. 并且该权重必须大于一个阈值,如果没有超过该阈值的权重,那么就只保留权重最大的边(与其它关键帧的共视程度比较高)
3. 对这些连接按照权重从大到小进行排序,以方便将来的处理。更新完covisibility图之后,如果没有初始化过,则初始化为连接权重最大的边(与其它关键帧共视程度最高的那个关键帧),类似于最大生成树。

三、code 

void KeyFrame::UpdateConnections()
{
    // 关键帧-权重,权重为其它关键帧与当前关键帧共视地图点的个数,也称为共视程度
    map<KeyFrame*,int> KFcounter; 
    vector<MapPoint*> vpMP;

    {
        // 获得该关键帧的所有地图点
        unique_lock<mutex> lockMPs(mMutexFeatures);
        vpMP = mvpMapPoints;
    }

    //For all map points in keyframe check in which other keyframes are they seen
    //Increase counter for those keyframes
    // Step 1 通过地图点被关键帧观测来间接统计关键帧之间的共视程度
    // 统计每一个地图点都有多少关键帧与当前关键帧存在共视关系,统计结果放在KFcounter
    for(vector<MapPoint*>::iterator vit=vpMP.begin(), vend=vpMP.end(); vit!=vend; vit++)
    {
        MapPoint* pMP = *vit;

        if(!pMP)
            continue;

        if(pMP->isBad())
            continue;

        // 对于每一个地图点,observations记录了可以观测到该地图点的所有关键帧
        map<KeyFrame*,size_t> observations = pMP->GetObservations();

        for(map<KeyFrame*,size_t>::iterator mit=observations.begin(), mend=observations.end(); mit!=mend; mit++)
        {
            // 除去自身,自己与自己不算共视
            if(mit->first->mnId==mnId)
                continue;
            // 这里的操作非常精彩!
            // map[key] = value,当要插入的键存在时,会覆盖键对应的原来的值。如果键不存在,则添加一组键值对
            // mit->first 是地图点看到的关键帧,同一个关键帧看到的地图点会累加到该关键帧计数
            // 所以最后KFcounter 第一个参数表示某个关键帧,第2个参数表示该关键帧看到了多少当前帧的地图点,也就是共视程度
            KFcounter[mit->first]++;
        }
    }

    // This should not happen
    // 没有共视关系,直接退出 
    if(KFcounter.empty())
        return;

    // If the counter is greater than threshold add connection
    // In case no keyframe counter is over threshold add the one with maximum counter
    int nmax=0; // 记录最高的共视程度
    KeyFrame* pKFmax=NULL;
    // 至少有15个共视地图点才会添加共视关系
    int th = 15;

    // vPairs记录与其它关键帧共视帧数大于th的关键帧
    // pair<int,KeyFrame*>将关键帧的权重写在前面,关键帧写在后面方便后面排序
    vector<pair<int,KeyFrame*> > vPairs;
    vPairs.reserve(KFcounter.size());
    // Step 2 找到对应权重最大的关键帧(共视程度最高的关键帧)
    for(map<KeyFrame*,int>::iterator mit=KFcounter.begin(), mend=KFcounter.end(); mit!=mend; mit++)
    {
        if(mit->second>nmax)
        {
            nmax=mit->second;
            pKFmax=mit->first;
        }

        // 建立共视关系至少需要大于等于th个共视地图点
        if(mit->second>=th)
        {
            // 对应权重需要大于阈值,对这些关键帧建立连接
            vPairs.push_back(make_pair(mit->second,mit->first));
            // 对方关键帧也要添加这个信息
            // 更新KFcounter中该关键帧的mConnectedKeyFrameWeights
            // 更新其它KeyFrame的mConnectedKeyFrameWeights,更新其它关键帧与当前帧的连接权重
            (mit->first)->AddConnection(this,mit->second);
        }
    }

    //  Step 3 如果没有超过阈值的权重,则对权重最大的关键帧建立连接
    if(vPairs.empty())
    {
	    // 如果每个关键帧与它共视的关键帧的个数都少于th,
        // 那就只更新与其它关键帧共视程度最高的关键帧的mConnectedKeyFrameWeights
        // 这是对之前th这个阈值可能过高的一个补丁
        vPairs.push_back(make_pair(nmax,pKFmax));
        pKFmax->AddConnection(this,nmax);
    }

    //  Step 4 对满足共视程度的关键帧对更新连接关系及权重(从大到小)
    // vPairs里存的都是相互共视程度比较高的关键帧和共视权重,接下来由大到小进行排序
    sort(vPairs.begin(),vPairs.end());         // sort函数默认升序排列
    // 将排序后的结果分别组织成为两种数据类型
    list<KeyFrame*> lKFs;
    list<int> lWs;
    for(size_t i=0; i<vPairs.size();i++)
    {
        // push_front 后变成了从大到小顺序
        lKFs.push_front(vPairs[i].second);
        lWs.push_front(vPairs[i].first);
    }

    {
        unique_lock<mutex> lockCon(mMutexConnections);

        // mspConnectedKeyFrames = spConnectedKeyFrames;
        // 更新当前帧与其它关键帧的连接权重
        // ?bug 这里直接赋值,会把小于阈值的共视关系也放入mConnectedKeyFrameWeights,会增加计算量
        // ?但后续主要用mvpOrderedConnectedKeyFrames来取共视帧,对结果没影响
        mConnectedKeyFrameWeights = KFcounter;
        mvpOrderedConnectedKeyFrames = vector<KeyFrame*>(lKFs.begin(),lKFs.end());
        mvOrderedWeights = vector<int>(lWs.begin(), lWs.end());

        // Step 5 更新生成树的连接
        if(mbFirstConnection && mnId!=0)
        {
            // 初始化该关键帧的父关键帧为共视程度最高的那个关键帧
            mpParent = mvpOrderedConnectedKeyFrames.front();
            // 建立双向连接关系,将当前关键帧作为其子关键帧
            mpParent->AddChild(this);
            mbFirstConnection = false;
        }
    }
}

四、函数解析 

        先获得该关键帧的所有地图点vpMP = mvpMapPoints,用vpMP变量存储。

        遍历该关键帧的所有地图点。对于每一个地图点,observations记录了可以观测到该地图点的所有关键帧。observations的类型为 map<KeyFrame*,size_t>,它存储着可以看到该地图点的所有关键帧KeyFrame和该地图点在该关键帧中的索引size_t。

        遍历observations变量获得KFcounter。首先判断该帧的ID是不是调用该函数的帧ID,因为自己和自己不存在共视关系。我们遍历observations中每一帧,因为该帧和当前帧(调用这个函数的帧)存在共视地图点,我们将KFcounter[该帧]++,表示这一帧和调用函数的帧的共视关系。最终得到的KFcounter变量是[关键帧,int]的形式,对应了与当前关键帧共视关键帧的共视程度。

        用nmax变量记录最高的共视程度,设置添加共视关系的阈值th

        遍历当前帧的共视关系向量KFcounter

        得到与当前关键帧最大的共视程度的关键帧pKFmax及共视程度nmax

        同时,若共视程度大于阈值th,则将该关键帧与共视程度放入变量vector<pair<int,KeyFrame*> > vPairs中,并更新当前帧的共视帧与当前帧的共视程度。

ORB-SLAM2 --- KeyFrame::AddConnection函数解析icon-default.png?t=MBR7https://blog.csdn.net/qq_41694024/article/details/128537286        

        如果没有超过阈值th的权重,则对权重最大的关键帧建立连接。

        对满足共视程度的关键帧对更新连接关系及权重(从大到小):

        vPairs里面的内容是和当前局部建图线程调用帧的共视程度超过我们指定阈值th的关键帧和共视程度。

        我们将共视程度和关键帧分开存储在两个列表容器lKFs、lWs中并按照共视程度从大到小排序。

        最后我们更新当前帧与其它关键帧的连接权重:

        将共视关系存储给我们更新的最终变量:

        将KFcounter赋值给mConnectedKeyFrameWeights,存储着当前局部线程调用关键帧的共视关键帧及共视程度。

        将lKFs赋值给mvpOrderedConnectedKeyFrames,存储着与当前局部线程调用关键帧的共视关键帧信息(按共视程度从大到小)。(相比于mConnectedKeyFrameWeights中存储的信息,这里的关键帧与局部线程调用关键帧的共视程度大于阈值th

        将lWs赋值给mvOrderedWeights,存储着与当前局部线程调用关键帧的共视关键帧的共视程度信息(按共视程度从大到小)。(相比于mConnectedKeyFrameWeights中存储的信息,这里的关键帧与局部线程调用关键帧的共视程度大于阈值th

        最后我们更新生成树的连接:

        初始化该关键帧的父关键帧mpParent为共视程度最高的那个关键帧,建立双向连接关系,将当前关键帧作为其子关键帧。将是否是第一次生成标志位mbFirstConnection设为false。

// 添加子关键帧(即和子关键帧具有最大共视关系的关键帧就是当前关键帧)
void KeyFrame::AddChild(KeyFrame *pKF)
{
    unique_lock<mutex> lockCon(mMutexConnections);
    mspChildrens.insert(pKF);
}
    bool mbFirstConnection;                     // 是否是第一次生成树
    KeyFrame* mpParent;                         // 当前关键帧的父关键帧 (共视程度最高的)
    std::set<KeyFrame*> mspChildrens;           // 存储当前关键帧的子关键帧

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

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

相关文章

用C++实现十大经典排序算法

作者&#xff1a;billy 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 简介 排序算法可以分为内部排序和外部排序&#xff0c;内部排序是数据记录在内存中进行排序&#xff0c;而外部排序是因排序的数据很大…

喜报|知道创宇连续两年获评北京市企业创新信用领跑企业!

近日&#xff0c;2022年度北京市企业创新信用领跑名单正式发布。知道创宇凭借过硬的技术实力、创新能力及良好的企业信用记录成功入选2022年度北京市企业创新信用领跑企业。值得一提的是&#xff0c;这是知道创宇继2021年以来&#xff0c;连续两年获得此项殊荣。连续两年蝉联双…

CPU是如何执行程序的?

CPU是如何执行程序的&#xff1f;1、硬件结构介绍1.1、CPU1.2、内存1.3、总线1.4、输入/输出设备2、程序执行的基本过程3、a11执行的详细过程现代计算机的基本结构为五个部分&#xff1a;CPU、内存、总线、输入/输出设备。或许你了解了这些概念&#xff0c;但是你知道a11在计算…

【Kubernetes | Pod 系列】Pod 的镜像下载策略和 Pod 的生命周期 Ⅰ—— 理论

目录4. 镜像下载策略5. Pod 的生命周期5.1 Pod 生命期与特性说明5.2 Pod Phase 阶段说明备注5.3 容器状态说明&#xff08;1&#xff09;Waiting &#xff08;等待&#xff09;&#xff08;2&#xff09;Running&#xff08;运行中&#xff09;&#xff08;3&#xff09;Termin…

【回答问题】ChatGPT上线了!给我推荐20个比较流行的nlp预训练模型

目录给我推荐20个比较流行的nlp预训练模型给我推荐20个比较流行的nlp预训练模型源码给我推荐20个比较流行的nlp预训练模型 BERT (谷歌) GPT-2 (OpenAI) RoBERTa (Facebook) ALBERT (谷歌) ELECTRA (谷歌) XLNet (谷歌/纽约大学) T5 (OpenAI) Transformer-XL (谷歌/香港中文大学…

Qt音视频开发09-ffmpeg内核音视频同步

一、前言 用ffmpeg来做音视频同步&#xff0c;个人认为这个是ffmpeg基础处理中最难的一个&#xff0c;无数人就卡在这里&#xff0c;怎么也不准&#xff0c;本人也是尝试过网上各种demo&#xff0c;基本上都是渣渣&#xff0c;要么仅仅支持极其少量的视频文件比如收到的数据包…

【EdgeBox_tx1_tx2_E100】 PyTorch v1.8.0 torchvision v0.9.0 环境部署

简介&#xff1a;介绍PyTorch 环境 在 EHub_tx1_tx2_E100载板&#xff0c;TX1核心模块环境&#xff08;Ubuntu18.04&#xff09;下如何实现部署和测试&#xff0c;准备安装的环境是&#xff08;PyTorch v1.8.0 torchvision v0.9.0&#xff09;。 关于测试硬件EHub_tx1_tx2_E1…

文献学习04_Deep contextualized word representations 深度语境化的单词表示_20230102

论文信息 Subjects: Computation and Language (cs.CL) &#xff08;1&#xff09;题目&#xff1a;Deep contextualized word representations &#xff08;深度语境化的单词表示&#xff09; &#xff08;2&#xff09;文章下载地址&#xff1a; https://doi.org/10.48550/…

Telemetry网络监控技术讲解

目录 Telemetry基本概念 设备监测数据的数据类型 为么要提出Telemetry Telemetry网络模型 广义Telemetry 狭义Telemetry 狭义Telemetry框架 数据源&#xff08;Yang&#xff09; 数据生成&#xff08;GPB&#xff09; 数据订阅&#xff08;gRPC、UDP&#xff09; 数…

跟着开源项目学java7-从操作日志排除敏感字段的提交看基于注解的日志记录实现

这次 commit 主要解决日志信息中可能存在 password 等敏感字段&#xff0c;需要在保存前排除掉 主要涉及两个类的修改&#xff0c;添加实现了一个 PropertyPreExcludeFilter&#xff0c;集成 fastjson2 的 SimplePropertyPreFilter 实现 /*** 排除JSON敏感属性* * author ruo…

两种方法设置Word文档的“只读模式”

防止Word文档被意外更改&#xff0c;我们可以将Word设置成“只读模式”来保护文档。根据需要&#xff0c;还可以将Word可以设置成无密码和有密码的“只读模式”&#xff0c;下面来说说具体方法。 方法一&#xff1a;无密码的“只读模式” 打开Word文档后&#xff0c;点击菜单…

C进阶_C语言_大小端_C语言大小端

现在调试以下代码&#xff0c;并对变量a和b进行监视&#xff1a; #include <stdio.h> int main() {int a 20;int b -10;return 0; } 右键&#xff0c;勾选十六进制显示&#xff1a; 可以看到&#xff0c;变量a和变量b的十六进制值分别为0x00000014和0xfffffff6。 那么…

MySQL之数据库设计范式

数据库设计范式&#xff1a; 第一范式&#xff1a; 要求任何一张表必须有主键&#xff0c;每一个字段原子性不可再分&#xff0c;第一范式是最核心&#xff0c;最重要的范式&#xff0c;所有的表的设计都需要满足 举例&#xff1a; 第二范式&#xff1a; 建立在第一范式的基…

一款基于SSH的反向Shell工具

一款基于SSH的反向Shell工具。 Reverse_SSH上一款基于SSH的反向Shell工具&#xff0c;在该工具的帮助下&#xff0c;广大研究人员可以使用SSH来实现反向Shell&#xff0c;并同时拥有下列功能&#xff1a; 1、使用原生SSH语句管理和连接反向Shell&#xff1b; 2、动态、本地和…

<UDP网络编程>——《计算机网络》

目录 1. 网络基础知识 1.1 理解源IP地址和目的IP地址 1.2 认识端口号 1.3 理解 "端口号" 和 "进程ID" 1.3.1 理解源端口号和目的端口号 1.4 认识TCP协议 1.5 认识UDP协议 1.6 网络字节序 2. socket编程接口 2.1 socket 常见API 2.2 sockaddr结构 2.3 socka…

广告刷屏世界杯,联想Filez助力海信全球营销运营

相信每个世界杯球迷在看球的同时也被世界杯球场上不断滚动的“Hisense&#xff0c;世界第二&#xff0c;中国第一”的广告牌吸引目光。在这28天&#xff0c;64场比赛中&#xff0c;卡塔尔的比赛场地不仅随处可见海信的围栏广告&#xff0c;同时场外也随处可见海信的身影。从备受…

ARM寄存器

1.ARM 工作状态 arm 支持大小端&#xff08;默认小端 &#xff0c;大端格式&#xff1a;高字节在低地址&#xff0c;低字节在高地址&#xff1b;小端格式&#xff1a;高字节在高地址&#xff0c;低字节在低地址&#xff1b;&#xff09;、arm支持16bit thumb指令和32bit arm指…

景联文科技:探究人工智能在智慧医疗中的应用及作用|数据标注

智慧医疗中的人工智能具有难以想象的潜力。人工智能在智慧医疗中的未来从通过协助重复性工作到药物管理或药物创造的治疗计划设计开始&#xff0c;人工智能已经在多个医学领域发挥作用。更好地组织智慧医疗物流智慧医疗和医学领域的人工智能可以更好地组织患者路线或治疗计划&a…

RabbitMQ 队列参数

RabbitMQ 队列参数 RabbitMQ在申明队列的时候&#xff0c;可以指定一些参数&#xff1a; /*** Declare a queue* see com.rabbitmq.client.AMQP.Queue.Declare* see com.rabbitmq.client.AMQP.Queue.DeclareOk* param queue the name of the queue* param durable true if we…

内核源码解读之内存管理(10)percpu_page_set分析

文章目录背景数据结构pcp的初始化流程背景 Linux系统中0阶内存分配需求是最多的&#xff0c; 而且经常存在频繁分配释放的行为&#xff0c;如果每次都去伙伴系统中申请&#xff0c;会经常需要获取zone->lock锁住整个zone区域。随着CPU核心数的增加&#xff0c;伙伴系统锁竞…