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

news2024/11/25 21:36:06

写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_YET
        mCurrentFrame = Frame(
            mImGray,
            timestamp,
            mpIniORBextractor,      //初始化ORB特征点提取器会提取2倍的指定特征点数目
            mpORBVocabulary,
            mK,
            mDistCoef,
            mbf,
            mThDepth);
    else
        mCurrentFrame = 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.coloradmin.cn/o/1795708.html

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

相关文章

规则引擎LiteFlow发布v2.12.1版本,决策路由特性

个人博客&#xff1a;无奈何杨&#xff08;wnhyang&#xff09; 个人语雀&#xff1a;wnhyang 共享语雀&#xff1a;在线知识共享 Github&#xff1a;wnhyang - Overview 简介 标题其实是不准确的&#xff0c;了解过的会知道在LiteFlow的2.12.0已经有了决策路由的特性&…

python数据文件处理库-pandas

内容目录 一、pandas介绍二、数据加载和写出三、数据清洗四、数据转换五、数据查询和筛选六、数据统计七、数据可视化 pandas 是一个 Python提供的快速、灵活的数据结构处理包&#xff0c;让“关系型”或“标记型”数据的交互既简单又直观。 官网地址: https://pandas.pydata.o…

树形表/树形数据接口的开发

数据表格式 需要返回的json格式 点击查看json数据 [{"childrenTreeNodes" : [{"childrenTreeNodes" : null,"id" : "1-1-1","isLeaf" : null,"isShow" : null,"label" : "HTML/CSS","na…

14.pinia初始与安装

pinia初始与安装 pinia官网 https://pinia.vuejs.org/ https://pinia.vuejs.org/introduction.html pinia安装 npm install pinia main.ts引入pinia import { createApp } from vue // import ./style.css import App from ./App.vue import router from ./router/index // …

LangChain :构建个人AI代理从这里开始

LangChain&#xff0c;一个强大的工具&#xff0c;允许根据用户输入创建对语言模型和其他工具的复杂调用链。就像拥有一个私人助理&#xff0c;可以根据手头的任务做出决定。本文来分享一下在 LangChain 中使用 Agents 的心路历程。 LangChain中代理的概念 在 LangChain 中&a…

网络高频攻击手段与基本防护措施总结

主要攻击手段 一、云平台攻击 云平台攻击是指针对云服务器的恶意行为&#xff0c;旨在获取非法访问权限、窃取敏感数据或者破坏服务器的正常运行。云平台攻击的形式多样&#xff0c;以下是对云平台攻击的一些主要类型和特点的详细分析&#xff1a; 攻击类型&#xff1a; 凭据…

苹果手机数据不见了怎么恢复?3个方法,搞定苹果手机数据恢复!

在许多错误的情况下&#xff0c;当你更新到最新的 iOS 版本或使用越狱来获得更多功能和权限、误删重要的手机文件时&#xff0c;苹果手机中的数据可能会丢失或被意外删除。一旦发现数据丢失&#xff0c;你就会查看 iTunes 备份或 iCloud 备份&#xff0c;并希望在其中恢复丢失的…

Rust 性能分析

都说Rust性能好,但是也得代码写得好,猜猜下面两个代码哪个快 . - 力扣&#xff08;LeetCode&#xff09; use std::collections::HashMap; use lazy_static::lazy_static;lazy_static! {static ref DIGIT: HashMap<char, usize> {let mut m HashMap::new();for c in …

electron打包时资源下载失败cannot resolve xxx/30.0.9/electron-v30.0.9-win32-ia32.zip

同学们可以私信我加入学习群&#xff01; 正文开始 问题描述解决方案总结 问题描述 最近electron更新频繁&#xff0c;而我在用electron做个人项目&#xff0c;对稳定性没有太高要求&#xff0c;希望保持着electron的最新版本&#xff0c;所以就没有固定版本。 单位网络不太好…

Jmeter性能分析及调优详解(入门)

一、系统性能理解 如果说需求、开发、DB、运维、测试是单一一门学科&#xff0c;那么性能就是综合学科&#xff0c;它包含了需求分析、DB、开发、测试、运维的所有学科。其实一般来说在实际性能分析和调优中&#xff0c;测试担任的角色就是写压测脚本并执行脚本查看结果&#…

区间预测 | Matlab实现QRCNN-GRU-Attention分位数回归卷积门控循环单元注意力机制时序区间预测

区间预测 | Matlab实现QRCNN-GRU-Attention分位数回归卷积门控循环单元注意力机制时序区间预测 目录 区间预测 | Matlab实现QRCNN-GRU-Attention分位数回归卷积门控循环单元注意力机制时序区间预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现QRCNN-GRU-…

在Linux上的Java项目导出PDF乱码问题

在Linux上的Java项目导出PDF乱码问题 场景&#xff1a;一个Java项目导出PDF&#xff0c;在我本地导出是没有问题&#xff0c;但是部署上Linux上后&#xff0c;导出就出现了乱码了。 处理方案 我这里使用的处理方案是在Linux服务器上安装一些PDF需要使用的字体 1.把字体上传到…

中国出海企业“奔赴”俄罗斯蓝海 有哪些认知需要对齐? | TopOn变现干货

中国企业加速出海已成常态化。在出海大潮席卷下&#xff0c;中国企业的身影已遍布欧美、东南亚、拉美、中东等多个成熟市场和潜力市场&#xff0c;眼下&#xff0c;这些热门市场几成红海&#xff0c;准入门槛也相对提高。而俄罗斯市场&#xff0c;作为全球TOP10的经济体之一&am…

初学者如何对大模型进行微调?

粗略地说&#xff0c;大模型训练有四个主要阶段&#xff1a;预训练、有监督微调、奖励建模、强化学习。 预训练消耗的时间占据了整个训练pipeline的99%&#xff0c;其他三个阶段是微调阶段&#xff0c;更多地遵循少量 GPU 和数小时或数天的路线。预训练对于算力和数据的要求非…

了解Kubernetes-RKE2的PKI以及证书存放位置

一、什么是PKI&#xff1f; 简称&#xff1a;证书基础设施。 可以方便理解为当你的集群有Server,Client架构&#xff0c;那么为了安全加密之间的通信&#xff0c;则需要使用证书进行交互&#xff0c;那么利用PKI架构可以安全加密组件之间的通信。 二、Kubernetes的PKI架构什…

短剧源码系统深层次解析:技术架构与实现

短剧源码系统作为短视频内容生产与分发的核心技术&#xff0c;其技术实现对于开发者和运营者至关重要。本文将深入探讨短剧源码系统的关键技术架构&#xff0c;特别是前端框架uni-app和Vue&#xff0c;以及后端框架ThinkPHP5和Workerman的应用。 前端框架&#xff1a;uni-app与…

生信学习入门常见错误可能的原因分类总结和求助指南

文件或目录找不到 这是常见问题&#xff0c;常见提示有 No such file or directory Error in file(file, “rt”)&#xff1a;无法打开链接 Fatal error: Unable to open file for reading (seq/WT1_1.fq) Fatal error: Unable to read from file (C:Program file/Git/usea…

1.Rust安装

目录 一、安装1.1 在Windows上安装1.2 在Linux下安装 二、包管理工具三、Hello World3.1 安装IDE3.2 输出Hello World 一、安装 1.1 在Windows上安装 点击页面 安装 Rust - Rust 程序设计语言 (rust-lang.org)&#xff0c;选择"下载RUSTUP-INIT.EXE(64位&#xff09;&qu…

PolygonalSurfaceContourLineInterpolator 多边形交互器

1. 效果&#xff1a; 2.简介&#xff1a; 可以实现在多边形上进行交互&#xff0c;选择&#xff1b;在多边形曲面上实现轮廓点的交互绘制。 该类的使用需要结合 vtkPolygonalSurfacePointPlacer 类&#xff0c;定位点的功能也就是拾取器。 前提&#xff1a;输入的多边形曲面…

剧本杀市场仍在快速发展,剧本杀小程序成为了新的机遇

近年来&#xff0c;剧本杀一直是年轻人的娱乐游戏方式之一&#xff0c;剧本杀行业呈现出了井喷式发展的形势&#xff0c;成为了当下爆火的娱乐方式。目前&#xff0c;剧本杀行业拥有了完善的剧本资源和呈现方式&#xff0c;发展前景非常大。 根据当下的数据显示&#xff0c;剧…