目录
1. 源码下载与配置
这一课的主要内容包括:
- 数据集下载
https://vision.in.tum.de/data/datasets/rgbd-dataset/download - 代码下载
https://github.com/raulmur/ORB_SLAM2 - 注释代码下载
https://github.com/electech6/ORB_SLAM2_detailed_comments - 参照源代码github网站完成安装
- 运行代码看看效果
- associate.py 文件讲解,主要是用来对其深度和彩色图像的时间戳
- 运行效果图中,红色点是当前跟踪的点,黑色点是所有的点
- 简单看了一下Example中的mono_tum.cc主函数
- 为什么要用配置文件存储参数,因为,如果参数写在代码中,每次修改调试参数都需要重新编译代码,而通过配置文件读进来就免去了这一个过程
2. tracker构造函数
-
讲解System类,system类构造函数主要完成大框架的初始化,像是各个进程:tracking mapping等进程以及相互之间的通信
-
system类包含:
Tracking* mpTracker,用于跟踪,以及创建关键帧等工作 -
重点讲解mpTracker,在Tracking的构造函数中,会首先读取一大堆配置文件中的参数,例如相机参数,orb金字塔的一些配置等
tracking类中包含:
///作者自己编写和改良的ORB特征点提取器
ORBextractor* mpORBextractorLeft, mpORBextractorRight;
///在初始化的时候使用的特征点提取器,其提取到的特征点个数会更多
ORBextractor mpIniORBextractor; -
重点讲解tracking类构造函数中的orb特征提取类,
orb构造函数,输入是tracking中读取的金字塔配置参数
(1)orb构造函数,先初始化一些参数
(2)构造图像金字塔
(3)将提的特征点数目分配到每一层中,通过等比数列
(4)读入brief特征的描述子位置pattern,
(5)umax这里的含义:
这个是为了保证旋转不变性,用灰度质心法确定一个图像重心,将重心与中心相连获得方向角
这里是要在圆范围内取点算图像重心,而不是在正方形内取点,正方形旋转后,点的坐标不一样了
这一段代码的意思,就是圆的边界,umax中存的内容是:每个不同的v坐标下,圆边界上的u的值
为了保证对称性,用了45°的角对圆进行评分 -
orb原理简单讲解
3. orb构造
- 从tracking跳出来到system,讲完system的构造函数
- 到mono_tum.cc里,接着往下,讲,进入for循环,读图片,统计时间
- 进入SLAM.TrackMonocular(im,tframe)函数内部
(1)看看是不是单目
(2)进行模式判断,看看要不要关闭局部地图更新线程,这里就是模式的改变。
(3)重点看cv::Mat Tcw = mpTracker->GrabImageMonocular(im,timestamp)函数 - GrabImageMonocular
图片赋值,转化为灰度图
如果没初始化就要初始化,看frame构造函数 - frame 构造函数
(1)id自增
(2)读取金字塔相关参数
(3)提取特征点
这里讲一下仿函数的意思:在类里实现一个类似函数的功能。 - ORBextractor类中的()操作符,进行orb特征点提取
- 进入()中的构建金字塔ComputePyramid(image)
(1)补充边界19 (给高斯滤波用的)??
(2)补充边界3(给fast角点计算用的)
(3)大概讲一下copymakeborder的意思
4. 特征点按网格分配
- 主要讲ComputeKeyPointsOctTree(allKeypoints)
(1)划分30x30像素网格主要这里的边角值计算,可以print出来看看
(2)在每个分割的区域内调用FAST函数,如果默认阈值下,没有找到特征点,就降低阈值,重新进行FAST检测。
(3)如果有关键点,就将关键点坐标恢复到图像坐标系下。 - 看DistributeOctTree函数
(1)讲一下直观分割并选点的效果
(2)生成初始化的一个或两个节点
(3)将所有特征点都放到对应节点中
(4)标记那些不可再分裂的节点,删除那些没有分配到特征点的节点
(5)未满足停止条件时,进行循环切分
(6)满足条件就停止:条件:节点数大于特征点数,分裂之后和分裂之前的节点数目相同
(7)还差一次就满足条件:单独处理,将那些特征点多的网格先分裂,变分裂变看节点数是否超过特征点数,一旦超过,立刻停止
(8)遍历每一个节点中的特征点,找到最大响应特征点留下来
5. 求特征点方向
- 从DistributeOctTree函数退出来之后,回到ComputeKeyPointsOctTree函数,看后续操作
- 将坐标统一到最外部的大图坐标系,+19那个大图
- computeOrientation函数,计算方向
(1)讲一下灰度质心的求解原理
(2)讲加速求解的方法
(3)获得图像中心灰度指针
(4)先算最中间的线,也就是v=0的线
(5)然后对V进行循环,同时计算上下两行
(6)讲到底怎么一次算两个的原理
//在坐标范围内挨个像素遍历,实际是一次遍历2个
// 假设每次处理的两个点坐标,中心线下方为(x,y),中心线上方为(x,-y)
// 对于某次待处理的两个点:m_10 = Σ xI(x,y) = xI(x,y) + xI(x,-y) = x(I(x,y) + I(x,-y))
// 对于某次待处理的两个点:m_01 = Σ yI(x,y) = yI(x,y) - yI(x,-y) = y(I(x,y) - I(x,-y)),每次y都是一样的 所以能这样算
第六课
- 从ComputeKeyPointsOctTree函数出来,到()函数中。开始计算特征点描述子
- 统计所有关键点个数
- 描述子也在金字塔下进行处理,所以也要循环
- 对当前层进行高斯模糊,简单讲一下高斯模糊
- 计算描述子
(1)先旋转一个角度,使得坐标轴和特征点灰度角重合
做法就是直接对pattern中的点进行旋转,得到旋转后的坐标
(2)每对描述子求灰度值,然后比较,组合出描述子
(3)把所有特征点都统一到第一层中
第七课
- 总体回顾一下现在到哪了,现在讲完了Frame中的ExtractORB函数
- 计算出关键点后
- 对关键点进行去畸变UndistortKeyPoints
利用了opencv内置的去畸变函数,注意这里只对坐标去畸变,剩下的特征角度,之类的量并没有变化
看看去畸变原理 - 计算图像边界:
去畸变后,图像会发生变化,会有一些填充的值,本步骤目的是获得去畸变后的图像边界
只对原图像边角的四个点进行去畸变,然后取最小x,最大x,最小y,最大y。 - 计算去畸变后图像的宽和高,并划分网格
- 将特征点分配到图像网格中,注意这里分配的是去畸变特征点的index
第八课
- 前面讲完了单目帧的frame构造函数,本节讲一下双目帧frame构造函数,这里是Frame构造函数的不同重载
- 双目开了两个线程进行ExtractORB函数计算,因为有两张
- 提取特征和去畸变一样的,下面主要讲怎么立体匹配求深度
(1)输入矫正后的orb特征点
(2)进行行特征点统计
(3)粗匹配,在同一行上进行检索
(4)精匹配SAD
(5)亚像素精度优化
(6)最优视察深度
(7)离群点筛选 - 视差公式原理——亚像素插值里的
第九课
- 讲完了frame,接着进入track函数
- 进行单目初始化
超过100个特征点才能进行初始化
new 一个初始化器
第二帧进来,进入else
看看有没有满足100个特征点,因为这里要保证连续两帧特征点都大于100
构造一个orb matcher
开始匹配
看看匹配结果满不满足要求
用匹配结果计算FH模型,计算三维点,确定一个坐标系,整体就是这样 - 先看特征点匹配
解释匹配关系矩阵的含义,index是f1中的特征点的index,值是f2中对应点的index
(1)构造旋转直方图,这是对方向差进行统计,后面起到筛选的作用
(2)初始化只考虑第0层级的特征点
(3)进入函数GetFeaturesInArea,在以x,y为中心,
先找的是网格的index——圆外接正方形的网格的index
遍历所有的cell,找候选特征点坐标,候选特征点也要满足某些条件
这么做的优势:以网格为单位,所以特别快
第十课
- 从函数GetFeaturesInArea中退出,接下来,如果有特征点,就开始找匹配特征点
对F1中每一个特征点进行循环
找到最佳或者次佳距离
判断最佳次佳比例
处理重复匹配问题
计算匹配点方向差直方图 - 剔除直方图中非主流的匹配
第十一课
- 继续单目初始化
- 如果匹配结果大于100, 就可以进一步估计位姿
- Step 1 重新记录特征点对的匹配关系
- Step 2 在所有匹配特征点对中随机选择8对匹配特征点为一组,用于估计H矩阵和F矩阵
- Step 3 计算fundamental 矩阵 和homography 矩阵,为了加速分别开了线程计算
- Step 4 计算得分比例来判断选取哪个模型来求位姿R,t
- 计算单应矩阵H
- Step 1 将当前帧和参考帧中的特征点坐标进行归一化,详见MVG教材算法4.4
- 做归1化:可以使结果更精确,这里有一幅图
- Step 2 选择8个归一化之后的点对进行迭代
- Step 3 八点法计算单应矩阵矩阵 求单应矩阵的原理
- Step 4 利用重投影误差为当次RANSAC的结果评分
- Step 5 更新具有最优评分的单应矩阵计算结果,并且保存所对应的特征点对的内点标记
12 计算单应矩阵H和本质矩阵F
- 再将归一化后求的H矩阵,反向变换回去
- 计算重投影误差
利用所有的匹配特征点对儿计算重投影误差,
然后利用卡方检验去确定到底什么样的点是离群点。
在RANSAC循环当中,记录最小的一个H矩阵’ - 再看基础矩阵F的计算,看基础矩阵原理
先归一化
选8个点进行计算
反归一化
选最好的一组:注意选的过程,H矩阵是用重投影误差进行,F矩阵用的对极约束进行计算
注意基础矩阵的一些性质,尺度,秩=2等
所以这里一共有两遍奇异值分解
13. 卡方检验
- 卡方检验:用于剔除有问题的点。先讲为什么要用卡方检验
- 卡方检验,能检验一组数据和指定分布的吻合程度。
- 抽奖机例子
- 基础矩阵重投影误差,算的是点到直线距离,只有一个数,就是1个自由度
单应矩阵重投影的是点,所以是2个自由度?
双目情况是3个自由度,因为有一个视察自由度 - 为了能将单应矩阵与基础矩阵比较,要用统一的得分
14. 从单应矩阵恢复运动
- 根据前面算的F和H矩阵,恢复R和t。
- 从H恢复
// 1. 根据H矩阵的奇异值d’= d2 或者 d’ = -d2 分别计算 H 矩阵分解的 8 组解
// 1.1 讨论 d’ > 0 时的 4 组解
// 1.2 讨论 d’ < 0 时的 4 组解
// 2. 对 8 组解进行验证,并选择产生相机前方最多3D点的解为最优解
这里需要看一下论文
Motion and structure from motion in a piecewise plannar environment.
这里计算过程讲的不细 - 看checkRT函数
对计算结果三角化,看看结果
遍历所有特征点
对于每个特征点进行三角化
看看三角化的结果是否正常
通过三维点深度值正负、两相机光心视差角大小来检查是否合法 - Triangulate函数
三角化计算也是用的SVD,这里面全是SVD,看SVD原理
15. 从基础矩阵恢复运动
- 接着上一次讲的从H中恢复R和T矩阵,算出RT后,从8组解中,选出最优解,并且要保证满足如下条件
// Step 3 选择最优解。要满足下面的四个条件
// 1. good点数最优解明显大于次优解,这里取0.75经验值
// 2. 视角差大于规定的阈值
// 3. good点数要大于规定的最小的被三角化的点数量
// 4. good数要足够多,达到总数的90%以上 - 从F矩阵恢复RT
求RT的过程又又用了SVD分解
同样从解出的4组解中,选出一组点在正面最多的解 - 退出来到MonocularInitialization函数继续讲
设置初始化的坐标系,从第一帧开始