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

news2024/11/7 20:59:06

目录

1.函数作用

2.code 

3.函数解析 


1.函数作用

        真正地执行删除关键帧的操作。

        需要删除的是该关键帧和其他所有帧、地图点之间的连接关系。

2.code 

void KeyFrame::SetBadFlag()
{   
    // Step 1 首先处理一下删除不了的特殊情况
    {
        unique_lock<mutex> lock(mMutexConnections);

        // 第0关键帧不允许被删除
        if(mnId==0)
            return;
        else if(mbNotErase)
        {
            // mbNotErase表示不应该删除,于是把mbToBeErased置为true,假装已经删除,其实没有删除
            mbToBeErased = true;
            return;
        }
    }

    // Step 2 遍历所有和当前关键帧相连的关键帧,删除他们与当前关键帧的联系
    for(map<KeyFrame*,int>::iterator mit = mConnectedKeyFrameWeights.begin(), mend=mConnectedKeyFrameWeights.end(); mit!=mend; mit++)
        mit->first->EraseConnection(this); // 让其它的关键帧删除与自己的联系

    // Step 3 遍历每一个当前关键帧的地图点,删除每一个地图点和当前关键帧的联系
    for(size_t i=0; i<mvpMapPoints.size(); i++)
        if(mvpMapPoints[i])
            mvpMapPoints[i]->EraseObservation(this); 

    {
        unique_lock<mutex> lock(mMutexConnections);
        unique_lock<mutex> lock1(mMutexFeatures);

        // 清空自己与其它关键帧之间的联系
        mConnectedKeyFrameWeights.clear();
        mvpOrderedConnectedKeyFrames.clear();

        // Update Spanning Tree 
        // Step 4 更新生成树,主要是处理好父子关键帧,不然会造成整个关键帧维护的图断裂,或者混乱
        // 候选父关键帧
        set<KeyFrame*> sParentCandidates;
        // 将当前帧的父关键帧放入候选父关键帧
        sParentCandidates.insert(mpParent);

        // Assign at each iteration one children with a parent (the pair with highest covisibility weight)
        // Include that children as new parent candidate for the rest
        // 每迭代一次就为其中一个子关键帧寻找父关键帧(最高共视程度),找到父的子关键帧可以作为其他子关键帧的候选父关键帧
        while(!mspChildrens.empty())
        {
            bool bContinue = false;

            int max = -1;
            KeyFrame* pC;
            KeyFrame* pP;

            // Step 4.1 遍历每一个子关键帧,让它们更新它们指向的父关键帧
            for(set<KeyFrame*>::iterator sit=mspChildrens.begin(), send=mspChildrens.end(); sit!=send; sit++)
            {
                KeyFrame* pKF = *sit;
                // 跳过无效的子关键帧
                if(pKF->isBad())    
                    continue;

                // Check if a parent candidate is connected to the keyframe
                // Step 4.2 子关键帧遍历每一个与它共视的关键帧    
                vector<KeyFrame*> vpConnected = pKF->GetVectorCovisibleKeyFrames();

                for(size_t i=0, iend=vpConnected.size(); i<iend; i++)
                {
                    // sParentCandidates 中刚开始存的是这里子关键帧的“爷爷”,也是当前关键帧的候选父关键帧
                    for(set<KeyFrame*>::iterator spcit=sParentCandidates.begin(), spcend=sParentCandidates.end(); spcit!=spcend; spcit++)
                    {
                        // Step 4.3 如果孩子和sParentCandidates中有共视,选择共视最强的那个作为新的父
                        if(vpConnected[i]->mnId == (*spcit)->mnId)
                        {
                            int w = pKF->GetWeight(vpConnected[i]);
                            // 寻找并更新权值最大的那个共视关系
                            if(w>max)
                            {
                                pC = pKF;                   //子关键帧
                                pP = vpConnected[i];        //目前和子关键帧具有最大权值的关键帧(将来的父关键帧) 
                                max = w;                    //这个最大的权值
                                bContinue = true;           //说明子节点找到了可以作为其新父关键帧的帧
                            }
                        }
                    }
                }
            }

            // Step 4.4 如果在上面的过程中找到了新的父节点
            // 下面代码应该放到遍历子关键帧循环中?
            // 回答:不需要!这里while循环还没退出,会使用更新的sParentCandidates
            if(bContinue)
            {
                // 因为父节点死了,并且子节点找到了新的父节点,就把它更新为自己的父节点
                pC->ChangeParent(pP);
                // 因为子节点找到了新的父节点并更新了父节点,那么该子节点升级,作为其它子节点的备选父节点
                sParentCandidates.insert(pC);
                // 该子节点处理完毕,删掉
                mspChildrens.erase(pC);
            }
            else
                break;
        }

        // If a children has no covisibility links with any parent candidate, assign to the original parent of this KF
        // Step 4.5 如果还有子节点没有找到新的父节点
        if(!mspChildrens.empty())
            for(set<KeyFrame*>::iterator sit=mspChildrens.begin(); sit!=mspChildrens.end(); sit++)
            {
                // 直接把父节点的父节点作为自己的父节点 即对于这些子节点来说,他们的新的父节点其实就是自己的爷爷节点
                (*sit)->ChangeParent(mpParent);
            }

        mpParent->EraseChild(this);
        // mTcp 表示原父关键帧到当前关键帧的位姿变换,在保存位姿的时候使用
        mTcp = Tcw*mpParent->GetPoseInverse();
        // 标记当前关键帧已经挂了
        mbBad = true;
    }  

    // 地图和关键帧数据库中删除该关键帧
    mpMap->EraseKeyFrame(this);
    mpKeyFrameDB->erase(this);
}

3.函数解析 

        先介绍mbNotErase作用:表示要删除该关键帧及其连接关系但是这个关键帧有可能正在回环检测或者计算sim3操作,这时候虽然这个关键帧冗余,但是却不能删除,仅设置mbNotErase为true,这时候调用setbadflag函数时,不会将这个关键帧删除,只会把mbTobeErase变成true,代表这个关键帧可以删除但不到时候,先记下来以后处理。在闭环线程里调用 SetErase函数会根据mbToBeErased来删除之前可以删除还没删除的帧。

        ①首先处理一下删除不了的特殊情况:

        即SLAM的第一帧不能被删除,因为其他帧的定位都靠第一帧,因此把mbToBeErased置为true,假装已经删除,其实没有删除。

        ②遍历所有和当前关键帧相连的关键帧,删除他们与当前关键帧的联系:

        mConnectedKeyFrameWeights存储当前关键帧的共视信息,记录当前关键帧共视关键帧的信息(哪一帧和当前关键帧有共视,共视程度是多少),是一个map<KeyFrame*,int>型变量,关于它的具体信息请参阅我的博客:

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

    for(map<KeyFrame*,int>::iterator mit = mConnectedKeyFrameWeights.begin(), mend=mConnectedKeyFrameWeights.end(); mit!=mend; mit++)
        mit->first->EraseConnection(this); // 让其它的关键帧删除与自己的联系
void KeyFrame::EraseConnection(KeyFrame* pKF)
{
    // 其实这个应该表示是否真的是有共视关系
    bool bUpdate = false;

    {
        unique_lock<mutex> lock(mMutexConnections);
        if(mConnectedKeyFrameWeights.count(pKF))
        {
            mConnectedKeyFrameWeights.erase(pKF);
            bUpdate=true;
        }
    }

    // 如果是真的有共视关系,那么删除之后就要更新共视关系
    if(bUpdate)
        UpdateBestCovisibles();
}

        即对于每个与当前帧有共视的帧,如图中红色帧,将该红色帧中关于此帧的共视信息删掉(因为红色帧也有mConnectedKeyFrameWeights向量,保存着共视关系,其中有一条就是 <被删除帧>,共视地图点的索引,我们要删掉这条信息),同时删除之后要更新共视关系。

void KeyFrame::UpdateBestCovisibles()
{
    // 互斥锁,防止同时操作共享数据产生冲突
    unique_lock<mutex> lock(mMutexConnections);
    // http://stackoverflow.com/questions/3389648/difference-between-stdliststdpair-and-stdmap-in-c-stl (std::map 和 std::list<std::pair>的区别)
    
    vector<pair<int,KeyFrame*> > vPairs;
    vPairs.reserve(mConnectedKeyFrameWeights.size());
    // 取出所有连接的关键帧,mConnectedKeyFrameWeights的类型为std::map<KeyFrame*,int>,而vPairs变量将共视的地图点数放在前面,利于排序
    for(map<KeyFrame*,int>::iterator mit=mConnectedKeyFrameWeights.begin(), mend=mConnectedKeyFrameWeights.end(); mit!=mend; mit++)
       vPairs.push_back(make_pair(mit->second,mit->first));

    // 按照权重进行排序(默认是从小到大)
    sort(vPairs.begin(),vPairs.end());

    // 为什么要用链表保存?因为插入和删除操作方便,只需要修改上一节点位置,不需要移动其他元素
    list<KeyFrame*> lKFs;   // 所有连接关键帧
    list<int> lWs;          // 所有连接关键帧对应的权重(共视地图点数目)
    for(size_t i=0, iend=vPairs.size(); i<iend;i++)
    {
        // push_front 后变成从大到小
        lKFs.push_front(vPairs[i].second);
        lWs.push_front(vPairs[i].first);
    }

    // 权重从大到小排列的连接关键帧
    mvpOrderedConnectedKeyFrames = vector<KeyFrame*>(lKFs.begin(),lKFs.end());
    // 从大到小排列的权重,和mvpOrderedConnectedKeyFrames一一对应
    mvOrderedWeights = vector<int>(lWs.begin(), lWs.end());
}

        ③遍历每一个当前关键帧的地图点,删除每一个地图点和当前关键帧的联系:

void MapPoint::EraseObservation(KeyFrame* pKF)
{
    bool bBad=false;
    {
        unique_lock<mutex> lock(mMutexFeatures);
        // 查找这个要删除的观测,根据单目和双目类型的不同从其中删除当前地图点的被观测次数
        if(mObservations.count(pKF))
        {
            int idx = mObservations[pKF];
            if(pKF->mvuRight[idx]>=0)
                nObs-=2;
            else
                nObs--;

            mObservations.erase(pKF);

            // 如果该keyFrame是参考帧,该Frame被删除后重新指定RefFrame
            if(mpRefKF==pKF)
                mpRefKF=mObservations.begin()->first;

            // If only 2 observations or less, discard point
            // 当观测到该点的相机数目少于2时,丢弃该点
            if(nObs<=2)
                bBad=true;
        }
    }

    if(bBad)
        // 告知可以观测到该MapPoint的Frame,该MapPoint已被删除
        SetBadFlag();
}

        ④更新生成树,主要是处理好父子关键帧,不然会造成整个关键帧维护的图断裂,或者混乱

        ⑤地图和关键帧数据库中删除该关键帧

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

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

相关文章

visual studio控制台中文乱码问题

踩坑经历&#xff1a; 这是一个向文件中写入文字&#xff08;中文&#xff0c;在将文件中文字读取出来的代码。读取出来的结果是符号码。 这个问题在网上找了很多的解决办法&#xff0c;都没有完全解决。 其实要不出现乱码&#xff0c;就要保持控制台编码和源代码编码相一致就可…

照片恢复软件哪个好用?5个好用的照片恢复软件推荐

照片可以勾起回忆&#xff0c;让我们想起与最亲近的人一起度过的时光&#xff0c;这就是为什么仅仅丢失一张重要照片就会让人感觉完全毁灭性的——几乎就像你失去了记忆本身一样。好消息是&#xff0c;大多数丢失或意外删除的照片都可以使用照片恢复软件恢复&#xff0c;而且我…

XILLINX时钟约束相关说明

时钟说明 primary clock 基准时钟&#xff0c;primary这里有“基本的”更贴切&#xff0c;虽然其有“主要的、首要的、初级的、原发性的”意思。 提供的基准时钟有俩种情况&#xff1a; 时钟由外部时钟源提供&#xff0c;通过输入引脚进入FPGA&#xff1b;高速收发器(GT)的时…

Google编程规范

Google编程规范总结一、前言二、头文件三、类构造函数可拷贝类型和可移动类型struct和class接口存取控制和声明顺序四、来自Google的奇技所有权与智能指针cpplint五、其他C特性六、命名约定(重点)七、注释(重点)八、代码格式(重点)九、结束语一、前言 昨天听了雷军的演讲&#…

猪齿鱼知识沉淀

数据源知识沉淀 1、下拉框与Lov的注意点&#xff1a; 下拉框 {name: intertradeType,lookupCode: "HCMS.INTERTRADE_TYPE",label: intl.get(${prefix}.table.intertradeType).d(贸易性质),required: true, }, {name: intertradeTypeMeaning,label: intl.get(${pref…

2023 docker安装gitlab-ce

公司搭建私服git来管理代码。这里使用docker来安装gitlab&#xff0c;过程相对简单。需要服务器至少有4g内存。这里安装的gitlab-ce社区版。前提是安装了docker 安装gitlab-ce 下载镜像&#xff0c;等待下载… docker pull gitlab/gitlab-ce:latest建立了目录 /opt/docker/git…

基础知识一览

这里写目录标题1.类1.1 类和对象的关系1.2 构造函数1.2.1 概念1.2.1.1 修饰符1.2.1.2 返回值类型1.2.1.3 函数名1.2.1.4 参数列表1.2.1.5 return语句1.2.1.6 扩展2.继承2.1 继承的好处和限制2.2 子父类中定义了一模一样的成员变量1.类 1.1 类和对象的关系 类是对象的抽象,对象…

利用宝塔配置jdk环境

首先&#xff0c;下载linux对应版本jdk(注意是Linux版本)&#xff0c;然后通过宝塔传到服务器上。选择相应的jdk文件&#xff0c;将其从本机上传到服务器上的对应文件夹。 上传后可以看到对应如下&#xff1a; 然后将其解压到对应的目录下&#xff0c;使用如下命令&#xff1…

使用Jmeter轻松实现AES加密测试

大家在自己公司做接口测试的时候&#xff0c;有没有遇到过接口做加密处理的情况呢&#xff1f;相信我们的读者朋友们都有一定的概率会遇到这种情况&#xff0c;尤其是对接口数据安全有一定要求的公司接口数据一定会做加密处理。那么遇到加密情况&#xff0c;大家使用工具JMeter…

CAD快速看图怎么转换成PDF格式?这一款软件就足够

CAD快速看图怎么转换成PDF格式&#xff1f;CAD文件是一种比较专业的文件&#xff0c;一般用于设计、绘图等&#xff0c;这种文件需要特殊的软件才可以打开&#xff0c;不过对于大多数人来说&#xff0c;都很少会安装这种软件&#xff0c;因为下载和安装软件需要很多时间&#x…

【计算机视觉】OpenCV 4高级编程与项目实战(Python版)【2】:操作像素

像素是构成图像的基本单位。现在看图1所示的花卉图像,这幅图看着很细腻,不过将图像的白框区域放大,会看到如图2所示的效果,细腻的图像不见了,取而代之的是一个一个的小方块,每一个小方块就是一个像素。 图6 花卉 图7 放大的花卉局部

分库分表解决方案

前言 因为每个学校学生用餐人数太多&#xff0c;一天订单20万量增长&#xff0c;而且学校使用也在不停的增多&#xff0c;公司最近在搞分库分表&#xff0c;数据分离到不同的库中或表中&#xff0c; 所以这段时间了解过数据库的分库分表&#xff0c;也读过很多大神写的博文&…

elasticsearch 7.9.3知识归纳整理(五)之es的索引生命周期管理

es的索引生命周期管理 一、常见概念及命令 1.1、概念 ILM定义了四个生命周期阶段&#xff1a; Hot&#xff1a;正在积极地更新和查询索引。 Warm&#xff1a;不再更新索引&#xff0c;但仍在查询。 cold&#xff1a;不再更新索引&#xff0c;很少查询。信息仍然需要可搜索&…

【蓝桥杯基础题】2021年省赛填空题—卡片

&#x1f451;专栏内容&#xff1a;蓝桥杯刷题⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录一、题目背景二、题目描述三、题目分析1.检查思路2.思路优化四、代码汇总1.C语言代码2. C代码3.运行结果五、总结1.枚举思…

“强鹰”会议纪要发布,不会停止加息?风险资产恐将承压

美联储发布了去年12月的会议纪要&#xff0c;详细揭露了货币政策制定者对经济和加息路径的最新研判&#xff0c;继续展现偏鹰的论调和立场。 纪要显示&#xff0c;美联储官员致力于抗击通货膨胀&#xff0c;并预计在取得更多进展之前利率将继续上升&#xff0c;并维持“一段时间…

82.Zabbix之Linux服务器agent监控(不需要联网)

Zabbix版本:6.2.3 官网上下载对应的agent

C#中的多线程(一)

一、多线程的术语 在学习多线程之前需要先理解有关多线程的术语。 CPU&#xff08;中央处理器&#xff09;或内核/核心是实际执行程序的硬件单元。许多现代CPU都支持同时多线程&#xff08;Intel称之为超线程&#xff09;&#xff0c;即使一个CPU能表现为多个「虚拟」CPU。进…

Linux安装xFormers教程

参考文章&#xff1a;手把手教你在linux中手动编译并安装xformers 作者&#xff1a;青空朝颜モ出处&#xff1a;bilibili 官方安装方法 官方仓库传送门&#xff1a;https://github.com/facebookresearch/xformers.git 官方给了两种方式安装xFormers&#xff0c;这里给出官方仓…

数字化转型之数字化和业务化论证

引言 数据业务化的本质是数据的产品化、商业化与价值化。主要强调产品化、新业务和专业化运作,也就是以数据为主要内容和生产原料,打造数据产品,按照产品定义、研发、定价、包装和推广的套路进行商业化运作,把数据产品打造成能为企业创收的新兴业务。 数字化是信息技术发…

NodeJs中使用Express开发web项目

文章目录1. web开发模式1.1 服务端渲染的Web开发模式1.2 前后端分离的Web开发模式1.3 如何选择web开发模式2. 身份认证2.1 Session认证机制2.1.1 cookie2.1.2 cookie认证2.1.3 在Express中使用Session认证2.1.4 Session认证的局限性2.2 JWT认证机制2.2.1 JWT组成部分2.2.2 在No…