ORB-SLAM 代码笔记(四)tracking代码结构
首先要清楚ORB-SLAM视觉跟踪的原理,然后对tracking.cc中的函数逐个讲解
代码的前面部分是从配置文件中读取校准好的相机参数(内参和畸变参数,以及双目的深度测量设定),并且加载ORB特征点提取的参数(特征点数,金字塔层数,变化尺度,以及提取Fast关键点的阈值);以及四个线程之间锁的代码。
接下来是将从摄像头或者数据集读入的图像封装成Frame类型对象:
这里以单目为例,无论图片是RGB,BGR, 还是RGBA,BGRA,均转化为灰度图,放弃彩色信息。然后将当前读入帧封装为Frame类型的mCurrentFrame对象;为了让单目成功初始化(单目的初始化需要通过平移运动归一化尺度因子),初始化时mpIniORBextractor提取的特征点数量设定为普通帧的2倍。
mpIniORBextractor = new ORBextractor(*nFeatures,fScaleFactor,nLevels,fIniThFAST,fMinThFAST);
有了mCurrentFrame对象,就可以进入跟踪模块(tracking()函数), 经过一系列跟踪处理(重点),可以返回当前帧的位姿。
cv::Mat Tracking::GrabImageMonocular(const cv::Mat &im, const double ×tamp)
{
mImGray = im; if(mImGray.channels()==)
{
if(mbRGB)
cvtColor(mImGray,mImGray,CV_RGB2GRAY);
else
cvtColor(mImGray,mImGray,CV_BGR2GRAY);
}
else if(mImGray.channels()==)
{
if(mbRGB)
cvtColor(mImGray,mImGray,CV_RGBA2GRAY);
else
cvtColor(mImGray,mImGray,CV_BGRA2GRAY);
} if(mState==NOT_INITIALIZED || mState==NO_IMAGES_YET)
mCurrentFrame = Frame(mImGray,timestamp,mpIniORBextractor,mpORBVocabulary,mK,mDistCoef,mbf,mThDepth);
else
mCurrentFrame = Frame(mImGray,timestamp,mpORBextractorLeft,mpORBVocabulary,mK,mDistCoef,mbf,mThDepth); Track(); return mCurrentFrame.mTcw.clone();
}
具体的tracking步骤如下:
(1)初始化
首先是初始化,分为双目、RGBD,以及单目两种不同的初始化过程,单目的初始化需要注意的较多,可以先看void Tracking::StereoInitialization()函数:
如果检测到的特征点数 N>500,则将当前帧构建为第一个关键帧,关键帧通过当前帧,地图点,以及BoW构建;同时,mpMap地图中也需要加入该关键帧(mpMap包含图像帧以及相应的地图点), 对每一个特征点,通过反投影得出3D地图点,地图点中通过3D位置信息,对应的关键帧,以及mpMap构建。
这其中的关系有点让人混乱,其实直观地去理解很容易:
mpMap就是我们整个位姿与地图(可以想象成ORB-SLAM运行时的那个界面世界),MapPoint和KeyFrame都被包含在这个mpMap中。因此创建这三者对象(地图,地图点,关键帧)时,三者之间的关系在构造函数中不能缺少。另外,由于一个关键帧提取出的特征点对应一个地图点集,因此需要记下每个地图点的在该帧中的编号;同理,一个地图点会被多帧关键帧观测到,也需要几下每个关键帧在该点中的编号。
地图点,还需要完成两个运算,第一个是在观测到该地图点的多个特征点中(对应多个关键帧),挑选出区分度最高的描述子,作为这个地图点的描述子;
pNewMP->ComputeDistinctiveDescriptors();
第二个是更新该地图点平均观测方向与观测距离的范围,这些都是为了后面做描述子融合做准备。
pNewMP->UpdateNormalAndDepth();
最后,还需要在局部地图中(为了实现局部Bundle Adjustment)插入当前关键帧,还需要初始化局部关键帧和局部地图点。将上一帧,上一关键帧,以及上一关键帧Id号初始化为当前帧。参考关键帧,当前帧的参考关键帧,当前地图中的参考地图点(集),并初始化当前相机姿态。
void Tracking::StereoInitialization()
{
if(mCurrentFrame.N>)
{
// Set Frame pose to the origin
mCurrentFrame.SetPose(cv::Mat::eye(,,CV_32F)); // Create KeyFrame
KeyFrame* pKFini = new KeyFrame(mCurrentFrame,mpMap,mpKeyFrameDB); // Insert KeyFrame in the map
mpMap->AddKeyFrame(pKFini); // Create MapPoints and asscoiate to KeyFrame
for(int i=; i<mCurrentFrame.N;i++)
{
float z = mCurrentFrame.mvDepth[i];
if(z>)
{
cv::Mat x3D = mCurrentFrame.UnprojectStereo(i);
MapPoint* pNewMP = new MapPoint(x3D,pKFini,mpMap);
pNewMP->AddObservation(pKFini,i);
pKFini->AddMapPoint(pNewMP,i);
pNewMP->ComputeDistinctiveDescriptors();
pNewMP->UpdateNormalAndDepth();
mpMap->AddMapPoint(pNewMP); mCurrentFrame.mvpMapPoints[i]=pNewMP;
}
} cout << "New map created with " << mpMap->MapPointsInMap() << " points" << endl; mpLocalMapper->InsertKeyFrame(pKFini); mLastFrame = Frame(mCurrentFrame);
mnLastKeyFrameId=mCurrentFrame.mnId;
mpLastKeyFrame = pKFini; mvpLocalKeyFrames.push_back(pKFini);
mvpLocalMapPoints=mpMap->GetAllMapPoints();
mpReferenceKF = pKFini;
mCurrentFrame.mpReferenceKF = pKFini; mpMap->SetReferenceMapPoints(mvpLocalMapPoints); mpMap->mvpKeyFrameOrigins.push_back(pKFini); mpMapDrawer->SetCurrentCameraPose(mCurrentFrame.mTcw); mState=OK;
}
}
单目的初始化则相对负责,放在下一篇笔记中去讲。
ORB-SLAM 代码笔记(四)tracking代码结构的更多相关文章
- SQL COOKBOOK SQL经典实例代码 笔记第一章代码
-- SQL COOKBOOK CHAPTER1 -- 查看所有内容 select * from emp; -- 可以单列 select empno,ename,job,sal,mgr,hiredat ...
- HTML5学习笔记四:html5结构
一.大纲:大纲即文档中各内容区块的结构编排 1. 显示编排内容区块:使用section等元素创建文档结构,每个内容区块使用标题(h1~h6,hgroup); 2. 隐式编排内容区块:根据页面所书写的各 ...
- 《OOC》笔记(4)——自动化地将C#代码转化为C代码(结构版)
<OOC>笔记(4)——自动化地将C#代码转化为C代码(结构版) 我在<C表达面向对象语言的机制——C#版>中已经说明了从C#到C的转换方法.这次看<OOC>也是想 ...
- jQuery 学习笔记:jQuery 代码结构
jQuery 学习笔记:jQuery 代码结构 这是我学习 jQuery 过程中整理的笔记,这一部分主要包括 jQuery 的代码最外层的结构,写出来整理自己的学习成果,有错误欢迎指出. jQuery ...
- 【hadoop代码笔记】Mapreduce shuffle过程之Map输出过程
一.概要描述 shuffle是MapReduce的一个核心过程,因此没有在前面的MapReduce作业提交的过程中描述,而是单独拿出来比较详细的描述. 根据官方的流程图示如下: 本篇文章中只是想尝试从 ...
- 【hadoop代码笔记】hadoop作业提交之汇总
一.概述 在本篇博文中,试图通过代码了解hadoop job执行的整个流程.即用户提交的mapreduce的jar文件.输入提交到hadoop的集群,并在集群中运行.重点在代码的角度描述整个流程,有些 ...
- Python 1行代码实现文本分类(实战笔记),含代码详细说明及运行结果
Python 1行代码实现文本分类(实战笔记),含代码详细说明及运行结果 一.详细说明及代码 tc.py =============================================== ...
- 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁
什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...
- 【Hadoop代码笔记】目录
整理09年时候做的Hadoop的代码笔记. 开始. [Hadoop代码笔记]Hadoop作业提交之客户端作业提交 [Hadoop代码笔记]通过JobClient对Jobtracker的调用看详细了解H ...
- <Python Text Processing with NLTK 2.0 Cookbook>代码笔记
如下是<Python Text Processing with NLTK 2.0 Cookbook>一书部分章节的代码笔记. Tokenizing text into sentences ...
随机推荐
- Spring+Velocity+Mybatis入门
转自:https://blog.csdn.net/duqi_2009/article/details/47752169 一.开发工具 开发过程中使用的操作系统是OS X,关于软件安装的问题请大家移步高 ...
- [19/03/23-星期六] 容器_ 泛型Generics
一.概念 生活中的容器不难理解,是用来容纳物体的,程序中的“容器”也有类似的功能,就是用来容纳和管理数据. 数组就是一种容器,可以在其中放置对象或基本类型数据. ---优势:是一种简单的线性序列,可以 ...
- SpringBoot非官方教程 | 第十七篇:上传文件
转载请标明出处: 原文首发于:https://www.fangzhipeng.com/springboot/2017/07/11/springboot14-upload/ 本文出自方志朋的博客 这篇文 ...
- ccenteros 部署 redis
step one : yum install redis -- 安装redis数据库 step two:安装完成之后开启redis 服务 service redis start syste ...
- TCP协议与UDP协议
网络通信协议规定了网络通信时,数据必须采用的格式.常见的协议有TCP协议,UDP协议. TCP协议 :(Transmission Control Protocol)传输控制协议. TCP是一种面向连接 ...
- spring mvc 数据绑定入门
1:基本数据类型key 是必传的并且必须类型一致 // http://localhost:8080/bind/baseType.do?xage=10 @RequestMapping(value = & ...
- LeetCode 中级 - 翻转矩阵后的得分(861)
有一个二维矩阵 A 其中每个元素的值为 0 或 1 . 移动是指选择任一行或列,并转换该行或列中的每一个值:将所有 0 都更改为 1,将所有 1 都更改为 0. 在做出任意次数的移动后,将该矩阵的每一 ...
- ABAP术语-LUW (Logical Unit of Work)
LUW (Logical Unit of Work) 原文:http://www.cnblogs.com/qiangsheng/archive/2008/03/04/1089637.html Logi ...
- 使用第三方工具连接docker数据库
一.背景 为了把测试环境迁移至docker上,我在centos7上安装了docker,具体安装方法可参考<CentOS7下安装docker>本文不再论述.有些同学可能会有疑问,为什么要 ...
- 邮件发送失败问题:Sending the email to the following server failed : smtp.qiye.163.com:25
[邮件发送错误] : Sending the email to the following server failed : smtp.qiye.163.com:25, {}org.apache.com ...