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

article/2025/5/6 5:35:02

目录

一、函数作用

二、函数流程 

三、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 通过地图点被关键帧观测来间接统计关键帧之间的共视程度// 统计每一个地图点都有多少关键帧与当前关键帧存在共视关系,统计结果放在KFcounterfor(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 counterint 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://chatgpt.dhexx.cn/article/G8Eg8T0B.shtml

相关文章

三谈属性动画——Keyframe以及ViewPropertyAnimator

Android动画和Transition系列文章 初识属性动画——使用Animator创建动画再谈属性动画——介绍以及自定义Interpolator插值器三谈属性动画——Keyframe以及ViewPropertyAnimator让View具有减速效果的动画——FlingAnimation让View具有弹性效果的动画——SpringAnimation让View…

ORB-SLAM2 --- KeyFrame::SetBadFlag函数

目录 1.函数作用 2.code 3.函数解析 1.函数作用 真正地执行删除关键帧的操作。 需要删除的是该关键帧和其他所有帧、地图点之间的连接关系。 2.code void KeyFrame::SetBadFlag() { // Step 1 首先处理一下删除不了的特殊情况{unique_lock<mutex> lock(mMutexConn…

【视觉SLAM】Fast Relocalisation and Loop Closing in Keyframe-Based SLAM

See discussions, stats, and author profiles for this publication at: https://www.researchgate.net/publication/263621033 Article in Proceedings - IEEE International Conference on Robotics and Automation June 2014 DOI: 10.1109/ICRA.2014.6906953 先读Abstrac…

Android属性动画 Keyframe

转载请标明出处&#xff1a;http://blog.csdn.net/zhaoyanjun6/article/details/118963313 本文出自【赵彦军的博客】 文章目录 KeyFrame实战 KeyFrame KeyFrame 主要用于自定义控制动画速率&#xff0c;KeyFrame 直译过来就是关键帧。 而关键帧这个概念是从动画里学来的&…

ORB-SLAM2代码详解05: 关键帧KeyFrame

pdf版本笔记的下载地址: ORB-SLAM2代码详解05_关键帧KeyFrame,排版更美观一点,这个网站的默认排版太丑了&#xff08;访问密码&#xff1a;3834&#xff09; ORB-SLAM2代码详解05: 关键帧KeyFrame 各成员函数/变量共视图: mConnectedKeyFrameWeights基于对地图点的观测重新构造…

@keyframes详解

一、transform 和keyframes动画的区别&#xff1a; keyframes动画是循环的&#xff0c;而transform 只执行一遍. 二、keyframes CSS3中添加的新属性animation是用来为元素实现动画效果的&#xff0c;但是animation无法单独担当起实现动画的效果。承载动画的另一个属性——keyfr…

KeyFrame类

关键帧类主要是进行Covisbility Graph&#xff0c; Essential&#xff0c;Spanning Tree 三个图的维护&#xff0c;更新。 其中不涉及关键帧的筛选策略问题&#xff0c;只设置了剔除某关键帧和剔除关键帧后如何更新图的方法。总体来说&#xff0c;不难理解。 PS:本文设计多线程…

自定义控件三部曲之动画篇(八)——PropertyValuesHolder与Keyframe

前言&#xff1a;只有比牛人跑的更快&#xff0c;才有可能追上他的脚步。 相关文章&#xff1a; 《Android自定义控件三部曲文章索引》:http://blog.csdn.net/harvic880925/article/details/50995268 前几篇给大家讲了ValueAnimator、ObjectAnimator的知识&#xff0c;讲解了…

ORB-SLAM2从理论到代码实现(十四):KeyFrame类

1. 原理分析 KeyFrame为关键帧&#xff0c;关键帧之所以存在是因为优化需要&#xff0c;所以KeyFrame的几乎所有内容都是位优化服务的。该类中的函数较多&#xff0c;我们需要归类梳理一下&#xff0c;明白其功能原理&#xff0c;才能真正弄懂它的内容。 图优化需要构建节点和…

css基础(九)--keyframe

33.keyframe 关键帧&#xff0c;类似于flash中的关键帧&#xff0c;以keyframes开头紧跟着动画名称加上花括号&#xff5b;。。。&#xff5d;&#xff0c;括号中表示不同时间段样式规则 keyframes changecolor{ 0%{ background: red; } 100%{ background: green; } } 样式规…

windows10 企业版 ltsc系统的激活

具体步骤参考下面的网址内容&#xff1a; https://www.landiannews.com/archives/51131.html

win10哪个版本最好用,推荐win10企业版LTSC

win10企业版LTSC又被称为win10企业版2019长期服务版本&#xff0c;这个版本小编认为是目前最好用的win10版本,在win10企业版2016长期服务版本的基础上&#xff0c;微软做了大量优化和升级。win10企业版LTSC更加快捷和轻便。 现在很多网站上都是有人修改过的win10企业版LTSC安装…

一键解决Win10 LTSC 2021官方镜像存在的问题

一键解决Win10 LTSC 2021官方镜像存在的问题 由于适用了win10 ltsc 2021之后&#xff0c;发现官方镜像存在一些致命的bug。但是本人又喜欢这个官方精简的系统&#xff0c;所以进行了一些修复。并将搜集到的办法其汇总成一个一键修复脚本 Win10_LTSC_2021_FixPacks。来让其他用户…

Windows:MULTIPROCESSOR CONFIGURATION NOT SUPPORTED蓝屏(32位(win10/LTSC 2019/LTSC 2021))

网上的答案帮助不大&#xff0c;全靠摸索&#xff0c;记录一下 Content 1.环境2.案发现场(bushi3.解决方案 1.环境 所有32位win10操作系统应该都有这个问题&#xff08;旧版没装过&#xff09;&#xff0c;我的配置: CPU: i5 2400主板:戴尔DELL H61内存8G&#xff08;没必要关…

win10ltsc转版本,win10ltsc升级win11,无损

之前电脑有些卡&#xff0c;一时兴起&#xff0c;就装了比较精简的ltsc&#xff0c;一开始还挺好用。但后来用着用着发现确实少了很多东西&#xff0c;地图&#xff0c;相机&#xff0c;商店什么的都没了。后来又索性加装了一根内存条。 但是ltsc上的文件我又不想丢&#xff0…

[篇五章二]_使用 USB 系统安装盘在真机上安装激活 Windows 10 LTSC 2021 中文企业版系统

################################################## 目录 使用系统盘在真机上安装激活 Windows 10 操作系统 启动盘真机安装 Win 10 图文教程 烧录系统盘 插入开机 安装前设置 重要的分区 安装后设置 安装成功&#xff01; 关于 Windows 10 处于通知模式如何处理 …

Windows 10 Enterprise LTSC 2019 (x64) 版本 (安装+激活+添加系统邮箱)

1. 网站 https://msdn.itellyou.cn/ 下载。 2. 制作U盘启动&#xff0c;注意采用UEFIGPT模式&#xff0c;为什么呢&#xff0c;见链接https://blog.csdn.net/yang2716210363/article/details/78581388。 3. 装好后激活。 https://pan.baidu.com/s/1Zxy-kJHNOHbvLxc47wBs0Q c…

激活出现 错误0x800706F7 占位程序接收到错误数据

KMS 错误0x800706F7 占位程序接收到错误数据 &#xff08;SWbemObjectEx&#xff09; 解决办法&#xff1a; 退出360安全卫士。

windows10 提示系统激活失败,报错为:激活错误0xcc004f012

前因 买的笔记本有正版 win10 激活&#xff0c;但因为自己要往 c 盘复制东西改了 System32 文件的一些权限&#xff0c;后发现系统显示激活失败&#xff0c;错误代码为 0xcc004f012。 做法 WIN R 输入 services.msc 找到 Software Protection 服务 当时发现服务为停止状态&…

Microsoft Office 2021 LTSC 专业激活版 win/mac版

Office 2021它包含了Word、Excel、PowerPoint、Outlook、OneNote、Publisher以及Access等应用程序。这些应用程序都是专门为帮助用户轻松处理各种办公任务而设计的。 其中&#xff0c;Word是一款强大的文字处理软件&#xff0c;可以帮助用户创建和编辑文档&#xff1b;Excel是…