orbslam2代码解读(1):数据预处理过程

写orbslam2代码解读文章的初衷

首先最近陆陆续续花了一两周时间学习视觉slam,因为之前主要是做激光slam,有一定基础所以学的也比较快,也是看完了视觉14讲的后端后直接看orbslam2的课,看的cvlife的课(课里大部分是代码的解读所以还是需要一定的视觉slam基础),但是说实话课是看完了,其实还是会有一些疑问,想着通过自己整体再梳理一遍,加深印象的同时能够解决自己的疑惑。最后一点这个课的课件真的乱!!!最好通过自己的整理把整个框架串起来理解。后续的代码讲解流程主要围绕单目来展开,因为单目相对而言过程更复杂,而双目和rgbd因为有深度信息,所以了解单目之后,它们的处理过程也更容易了解

论文代码的整体框架图

原论文的插图:
在这里插入图片描述
cvlife课中转换成中文的框架图:
在这里插入图片描述
内容都是一致的,分成三个主要的线程:

  1. 跟踪tracking线程。求解当前图像帧在世界坐标系下的位姿,处理的是任意普通帧,完成定位的功能。
  2. 局部建图localmapping线程。根据产生的新关键帧产生新的地图点,以便后续跟踪的时候pnp的时候用,还有需要局部BA(只优化局部关键帧的位姿)。
  3. 回环Loop closing线程。根据localmapping线程传过来的关键帧进行回环检测,如果有候选回环帧就计算sim3,修正当前关键帧及其共视关键帧的位姿和地图点,然后进行本质图优化(仅优化位姿),最后就是全局BA(优化地图点和位姿)。

SLAM系统的运行流程

主函数

首先看mono_tum.cc 简化版的主函数(单目情况):

int main(int argc, char **argv)
{LoadImages(strFile, vstrImageFilenames, vTimestamps);ORB_SLAM2::System SLAM(argv[1],argv[2],ORB_SLAM2::System::MONOCULAR,true);for(int ni=0; ni<nImages; ni++){im = cv::imread();SLAM.TrackMonocular(im,tframe);}SLAM.Shutdown();SLAM.SaveKeyFrameTrajectoryTUM("KeyFrameTrajectory.txt");
}
  1. 声明了一个单目的SLAM对象。
  2. 遍历数据集的图像并且传给SLAM对象的单目track函数。
  3. 最后关闭SLAM并且保存关键帧的轨迹。

后续调用的函数

SLAM.TrackMonocular(im,tframe)

这个SLAM.TrackMonocular(im,tframe);函数里主要是通过mpTracker来进行跟踪并且最终返回当前图像帧的位姿估计结果。

	//获取相机位姿的估计结果cv::Mat Tcw = mpTracker->GrabImageMonocular(im,timestamp);

mpTracker->GrabImageMonocular(im,timestamp)

这个mpTracker->GrabImageMonocular(im,timestamp)函数处理过程:

  1. 如果图像是彩色图,就转成灰度图
  2. 如果当前帧是初始化的帧,那么在构建Frame的时候,提取orb特征点数量为正常的两倍(目的就是能够在初始化的时候有更多匹配点对),如果是普通帧,就正常构建Frame。
  3. 接着就是调用tracking线程中的Track()函数。(这个下一篇再描述)
  4. 返回当前图像帧的位姿估计结果。
    这个mpTracker->GrabImageMonocular(im,timestamp)函数对应第3、4步骤
    // Step 3 :跟踪Track();//返回当前帧的位姿return mCurrentFrame.mTcw.clone();

数据处理流程

数据处理主要发生在mpTracker->GrabImageMonocular(im,timestamp)函数中第2步:根据当前帧灰度图构造Frame对象。

   // Step 2 :构造Frame//判断该帧是不是初始化if(mState==NOT_INITIALIZED || mState==NO_IMAGES_YET) //没有成功初始化的前一个状态就是NO_IMAGES_YETmCurrentFrame = Frame(mImGray,timestamp,mpIniORBextractor,      //初始化ORB特征点提取器会提取2倍的指定特征点数目mpORBVocabulary,mK,mDistCoef,mbf,mThDepth);elsemCurrentFrame = Frame(mImGray,timestamp,mpORBextractorLeft,     //正常运行的时的ORB特征点提取器,提取指定数目特征点mpORBVocabulary,mK,mDistCoef,mbf,mThDepth);

Frame的构造流程

step1

普通帧的id自增,注意普通帧和关键帧的id并不一样,它们分别是Frame和KeyFrame类中的一个静态变量,普通帧的id可能只是为了统计处理了多少个图像,而关键帧的id需要结合后续的回环检测和全局BA,又或者是共视关键帧等等去使用。

step2

设置图像金字塔的参数。根据配置文件去设置,我看到TUM1.yaml是金字塔层级8,缩放系数是1.2。

step3

根据灰度图提取ORB特征点。提取的代码主要是在ORBextractor.cc中的括号运算符重载函数中。

/*** @brief 用仿函数(重载括号运算符)方法来计算图像特征点* * @param[in] _image                    输入原始图的图像* @param[in] _mask                     掩膜mask* @param[in & out] _keypoints                存储特征点关键点的向量* @param[in & out] _descriptors              存储特征点描述子的矩阵*/
void ORBextractor::operator()( InputArray _image, InputArray _mask, 
vector<KeyPoint>& _keypoints,OutputArray _descriptors)

这个函数的步骤如下:

  1. 检查图像有效性,如果为空就退出。
  2. 构建图像金字塔:
    2.1按照缩放系数获得本级金字塔的图像,并且扩展图像的边界。(图中说的比较清晰了,一个是为了提取fast特征点预留的,另一个是为了高斯模糊预留的)
    在这里插入图片描述
    2.2最终得到8个图像vector<Mat>的容器,用于后续提取特征点。
  3. 计算图像的特征点
    3.1 将每个金字塔图层的图像按照网格一个一个来进行FAST点的提取,如下面红色网格。
    在这里插入图片描述
    提取FAST角点的过程可以看下面的图:
    在这里插入图片描述
  • 选取像素点p,灰度为 I p I_p Ip

  • 设定一个阈值T

  • 以像素点p为圆心,选择半径为3圆上的16个像素点

  • 遍历16个像素点,如果有连续N个点的亮度大于 I p + T I_p+T Ip+T I p − T I_p-T IpT,认为p是特征点

  • 对每一个像素重复上面的操作

    3.2 将每个金字塔图层的特征点数量根据四叉树的方法,平均分布特征点,四叉树的结果如下图所示:(最后每个网格留下质量最好的点)
    在这里插入图片描述
    需要注意的是,每个金字塔因为长宽不同,所以根据金字塔图像长度的不同,将每次普通帧需要提取设定的点数(配置文件中是1000个),按照公式设定每个金字塔图像保留多少个特征点:
    在这里插入图片描述
    3.3 根据灰度质心法,计算每个金字塔图层中特征点的方向。这个是为了让后面的brief描述子具有旋转不变性的。去特征点周围半径为HALF_PATCH_SIZE的像素来计算局部图像的质心,公式如下:
    m 10 = ∑ x = − R R ∑ y = − R R x I ( x , y ) m 01 = ∑ x = − R R ∑ y = − R R y I ( x , y ) \begin{aligned} & m_{10}=\sum_{x=-R}^R \sum_{y=-R}^R x I(x, y) \\ & m_{01}=\sum_{x=-R}^R \sum_{y=-R}^R y I(x, y)\end{aligned} m10=x=RRy=RRxI(x,y)m01=x=RRy=RRyI(x,y)
    m 00 = ∑ x = − R R ∑ y = − R R I ( x , y ) m_{00}=\sum_{x=-R}^R \sum_{y=-R}^R I(x, y) m00=x=RRy=RRI(x,y)
    得到质心的位置:
    C = ( c x , c y ) = ( m 10 m 00 , m 01 m 00 ) C=\left(c_x, c_y\right)=\left(\frac{m_{10}}{m_{00}}, \frac{m_{01}}{m_{00}}\right) C=(cx,cy)=(m00m10,m00m01)
    最后根据质心位置计算当前特征点的方向:
    θ = arctan ⁡ 2 ( c y , c x ) = arctan ⁡ 2 ( m 01 , m 10 ) \theta=\arctan 2\left(c_y, c_x\right)=\arctan 2\left(m_{01}, m_{10}\right) θ=arctan2(cy,cx)=arctan2(m01,m10)
    在这部分的计算中,为了加快计算质心的位置,程序里是根据提前算好的u_max同时对两行进行计算,具体的代码就不详细展开了。
    这里留下一个疑问:这里计算质心的PATCH的大小,每个金字塔图层都用了一样的大小,PATCH的大小是30。这应该需要根据金字塔图层来改变?

  • 将每一层金字塔图像进行高斯模糊后(高斯模糊有利于减少噪点的影响),将保留下来的每个特征点,都计算它们的brief描述子,这个描述子结合了特征点的方向,所以最终具备旋转不变性。
    在这里插入图片描述
    brief描述子的计算过程:

  • 在关键点的周围以一定模式选取N个点对(在代码中是256个点对)

  • τ ( p ; x , y ) : = { 1 if  p ( x ) < p ( y ) 0 otherwise  \tau(\mathbf{p} ; \mathbf{x}, \mathbf{y}):= \begin{cases}1 & \text { if } \mathbf{p}(\mathbf{x})<\mathbf{p}(\mathbf{y}) \\ 0 & \text { otherwise }\end{cases} τ(p;x,y):={10 if p(x)<p(y) otherwise  根据公式计算每一个位的数,一共是256位描述子
    注意:这256个点对是预先设定好在程序中的,应该是orbslam2作者通过机器学习得到的结果。最后就是根据这256对索引取值的时候,需要用上前面计算的特征点角度,具体操作如下图所示,256对点本来在图像的坐标系上,需要转到PQ为X轴的坐标系上,再计算brief描述子,这时候的brief描述子就具备了旋转不变性。在这里插入图片描述

step4

通过opencv,对图像进行畸变矫正。其实就是根据标定好的畸变矫正系数 k 1 k_1 k1 k 2 k_2 k2 p 1 p_1 p1 p 2 p_2 p2来对图像进行去畸变。
在这里插入图片描述
最后用校正后的特征点像素坐标覆盖特征点的老像素坐标。

step5

将特征点分配到图像网格中。目的应该就是为了方便后续两个图像帧之间的特征点配对。

总结

至此,就已经讲完了数据预处理的过程,主要的处理代码都是在Frame的构造函数当中,根据一帧图像,构建了一个Frame对象,里面存储着很多关键信息:每个金字塔图层的特征点及其对应旋转不变性的Rotated BRIEF,还对特征点进行去畸变。

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

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

相关文章

jenkins的简单使用

2.1.简介 Jenkins是一个开源软件项目&#xff0c;是基于Java开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件的持续集成变成可能。 2.4.Jenkins安装 1.下载安装包jenkins.war&#xff1b; 2.在安装…

笔记 | 软件工程04:软件项目管理

1 软件项目及其特点 1.1 什么是项目 1.2 项目特点 1.3 影响项目成功的因素 1.4 什么是软件项目 针对软件这一特定产品和服务的项目努力开展“软件开发活动",&#xff08;理解&#xff1a;软件项目是一种活动&#xff09; 1.5 软件项目的特点 1.6 军用软件项目的特点 2 …

怎么用电脑把图片转换二维码?图片在线生成二维码的步骤内容

现在很多人会通过二维码来存储物品的信息图片&#xff0c;其他人可以通过扫描二维码的方式来查看对应的图片内容&#xff0c;那么当我们需要将一批图片每个单独生成二维码&#xff0c;该如何操作能够快速将图片转换二维码呢&#xff1f; 今天&#xff0c;小编来分享给大家一个…

CNN卷积神经网络

一、概述 卷积神经网络&#xff08;CNN&#xff09;是深度学习领域的重要算法&#xff0c;特别适用于处理具有网格结构的数据&#xff0c;比如说图像和音频。它起源于二十世纪80至90年代&#xff0c;但真正得到快速发展和应用是在二十一世纪&#xff0c;随着深度学习理论的兴起…

【ai】phc:安装issac环境且fix libstdc++.so 版本报错

Pycharm远程连接服务器(2023-11-9) 大神分享了pycharm远程连接ubuntu工作站的方法。 https://github.com/ZhengyiLuo/PHC 给出的操作同样适用: 参考 Pycharm远程连接服务器(2023-11-9) :前提是一样的 PHC的要求:isaac 创建 conda activate isaac

【Vue】scoped解决样式冲突

默认情况下写在组件中的样式会 全局生效 → 因此很容易造成多个组件之间的样式冲突问题。 全局样式: 默认组件中的样式会作用到全局&#xff0c;任何一个组件中都会受到此样式的影响 局部样式: 可以给组件加上scoped 属性,可以让样式只作用于当前组件 一、代码示例 BaseOne…

RocketMQ可视化界面安装

RocketMQ可视化界面安装 **起因&#xff1a;**访问rocketmq-externals项目的git地址&#xff0c;下载了源码&#xff0c;在目录中并没有找到rocketmq-console文件夹。 git下面文档提示rocketMQ的仪表板转移到了新的项目中&#xff0c;点击仪表板到新项目地址&#xff1b; 下载…

搜索与图论:宽度优先搜索

搜索与图论&#xff1a;宽度优先搜索 题目描述参考代码 题目描述 输入样例 5 5 0 1 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0输出样例 8参考代码 #include <iostream> #include <algorithm> #include <cstring> using namespace std;const int N …

VsQt单元测试目录的管理方式

正常项目的文件管理方式 正常项目的目录&#xff0c;是由文件系统中实际的文件夹进行分类管理的。 但是如果单元测试用实际文件夹管理的话&#xff0c;会出现问题&#xff0c;就是被测类太多了&#xff0c;用文件系统管理的话&#xff0c;不太方面查看&#xff0c;如下图所示。…

contentType 与 dataType

contentType 与 dataType contentType contentType&#xff1a;发送的数据格式&#xff08;请求方发送给服务器的数据格式&#xff09;&#xff0c;这个内容会放在请求方的 请求头中 application/x-www-form-urlencoded 这个是默认的请求格式。 提交给后台的数据会按照 KV&am…

创新实训2024.06.06日志:部署web服务

1. 运行web项目前后端服务 首先我们要先在服务器上运行客户端以及服务端的应用程序。随后再考虑如何通过公网/局域网访问的问题。 如何启动服务在仓库对应分支下的Readme文件中已经有详细描述了。 1.1. 启动服务端 对于服务端&#xff0c;即&#xff08;要求你在服务端子项…

SCARA机器人中旋转花键的维护和保养方法!

作为精密传动元件的一种&#xff0c;旋转花键在工作过程中承受了较大的负荷。在自动化设备上运用广泛&#xff0c;如&#xff1a;水平多关节机械手臂&#xff08;SCARA&#xff09;、产业用机器人、自动装载机、雷射加工机、搬运装置、机械加工中心的ATC装置等&#xff0c;最适…

如何在Windows 10和11上修复DISM错误87?这里提供办法

​在电脑上运行DISM命令时&#xff0c;是否收到“错误代码87”消息&#xff1f;这是一个非常常见的错误&#xff0c;你可以轻松地修复它。我们将向你展示在Windows 11或Windows 10计算机上解决此问题的多种方法。 确保键入正确的命令 运行DISM命令时出现错误代码87的最常见原…

优雅谈大模型10:MoE

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;则提…

应对800G以太网挑战:数据中心迁移

在过去几年中&#xff0c;云基础设施和服务的大规模使用推动了对更多带宽、更快速度和更低延迟性能的需求。交换机和服务器技术的改进要求布线和架构随之调整。因此&#xff0c;800G以太网对数据中心迁移的需求&#xff0c;特别是对速率&#xff08;包括带宽、光纤密度和通道速…

进程和计划任务以及步骤

进程 进程和程序有关&#xff0c;把该文件放到内存里&#xff0c;进程是动态的&#xff0c;不同时刻的状态不一样 内存&#xff1a;放置正在运行的程序和所需数据的位置 程序启动 ——》将相关文件和数据放到内存里 ——》进程&#xff08;processes&#xff09; 进程相关命令 …

【Python】教你彻底认识Python中的Web开发

​​​​ 文章目录 一、Web开发的基本概念1. Web服务器2. 客户端-服务器模型3. HTTP协议4. 前端与后端 二、常用的Web开发框架1. Django1.1 安装Django1.2 创建Django项目1.3 定义模型1.4 定义视图1.5 定义URL路由1.6 模板 2. Flask2.1 安装Flask2.2 创建Flask应用2.3 模板2.4…

达梦8 并行查询

达梦数据库可以通过在查询语句加/* parallel */ HINT并行执行。下面以一个排序语句为例 我们通过一条监控语句查询排序区的使用情况。 最初没有查询结果&#xff08;no rows&#xff09; 伴随着语句insert /* parallel(4) */ into d select * from a order by 2执行&#xff…

计算机组成结构—总线

目录 一、总线的基本概念 1.分散连接和总线连接 2.总线的特点和组成 二、总线的分类 1. 片内总线 2. 系统总线 3. 通信总线 三、总线结构 1. 单总线结构 2. 双总线结构 3. 三总线结构 四、总线的性能指标 五、总线标准 六、总线判优&#xff08;总线仲裁&#xf…

【List,ArrayList与顺序表】

目录 1&#xff0c;什么是List 2&#xff0c;List的使用 3&#xff0c;线性表 4&#xff0c;顺序表 4.1 接口的实现 5&#xff0c; ArrayList简介 6&#xff0c;ArrayList的使用 6.1 ArrayList的构造方法 6.2 ArrayList的常见操作 6.3 ArrayList的遍历 7&#xff0c;…