ORB_SLAM2 源码阅读 ORB_SLAM2::Initializer
ORB_SLAM2::Initializer 用于单目情况下的初始化。
Initializer 的构造函数中传入第一张影像,这张影像被称作 reference frame(rFrame)。在获得第二张影像时传入第二张影像,这张影像被称作 current frame(cFrame)。这一部分传入的代码可以在ORB_SLAM2::Tracking::MonocularInitialization()
中查看,要求 rFrame 与 cFrame 都至少具有 101 个特征点,而且 cFrame 与 rFrame 粗匹配结果不少于 100 个点对。这个粗匹配也很有意思,可以查看ORBmatcher::SearchForInitalization()
(粗匹配是对每一个 rFrame 的特征点选定一定大小的窗口,以该特征点在 rFrame 上的坐标为中心,在 cFrame 上提取出覆盖网格内所有的特征点,计算 ORB 描述子的距离,距离够小就说明是匹配点)。
Initializer::Initialize()
在这个函数中完成初始化。首先生成 RANSAC 需要用的最小子集的集合mvSets
。随后开两个线程同步进行FindHomography
和FindFundamental
,这两个函数分别返回SH
、SF
这两个数值用于判定是使用 H 作为初始模型更好,还是用 F 作为初始模型更好。
在用SH
、SF
判定是使用 H 还是 F 之后就是用 H (ReconstructH()
)或 F (ReconstructF()
)生成 R、t 和对应的可以三角化的点用于初始地图生成。
ReconstructH()
是使用 Motion and structure from motion in a piecewise planar
environment 生成 8 种可能结果,再使用CheckRT()
确定是哪一种最为合适。
Initializer::FindHomography()
每次使用 8 个点通过 SVD 分解计算得到 H21。值得注意的是在进行 Homography 计算之前先进行归一化过程(在函数Normalize()
中进行)。
Initializer::Normalize()
归一化过程是将所有的 KeyPoints 进行一次 Affine Transformation,使得变换后的 KeyPoints 均值为原点 \(0\),方差为单位阵 \(I\)。
变换公式为:
\[
x_i^{\prime} = {x_i - \bar{x} \over \sigma} \\
\bar{x} = {\Sigma{x_i} \over N} \\
\sigma = {\Sigma{\left|x_i - \bar{x}\right|} \over N}
\]
归一化的原因是计算出的 H 会依赖于特征点的位置,所以先归一化再计算。(MVG Page 104)
归一化的过程可以使用一个矩阵 \(T\) 表示:
\[
x^{\prime} = Tx = \begin{bmatrix} {1 \over \sigma_x} & 0 & \bar{x}{1 \over \sigma_x} \\ 0 & {1 \over \sigma_y} & \bar{y}{1 \over \sigma_y} \\ 0 & 0 & 1 \end{bmatrix} x
\]
后面的计算过程如下:
\[
x_2^{\prime} = H_{21}^{\prime}x_1^{\prime}
\]
\[
Tx_2 = H_{21}^{\prime}Tx_1
\]
\[
x_2 = T^{-1}H_{21}^{\prime}Tx_1
\]
\[
H_{21} = T^{-1}H_{21}^{\prime}T
\]
Initializer::ComputeH21()
在归一化之后,使用归一化的坐标计算 Homography。
没啥好讲,就是 Direct Linear Transformation,参考 MVG Page 88。
Initializer::CheckHomography()
用 H21 和 H12 分别将 rFrame(1) 中的特征点和 cFrame(2) 转换到另一张影像中,计算匹配点的距离误差,距离误差转换为卡方距离,卡方距离小于 5.991 说明显著性为 5%,应该认为它们匹配成功,否则不成功将这一对匹配标记为 false。注意这里有两个自由度。
https://en.wikipedia.org/wiki/Chi-squared_distribution#Table_of_.CF.872_values_vs_p-values
匹配成功能就能将于显著性相关的数值加入到评分中,评分越高说明由这八个点计算出的 Homography 越正确。即由CheckHomography()
返回的评分currentScore
,取 RANSAC 中评分最高的 Homography 作为最终选定的 Homography。
这个 score 会被传入Initialize()
函数中的 SH,用作计算 SH/(SH+SF),判断是使用 Homography 还是 Fundamental。
Initializer::ReconstructH()
用函数FindHomography()
中 RANSAC 计算得到的 Homography 分解,分解能够得到 8 种可能的 \(R, t\) 结果,用CheckRT()
判断选择哪一种结果。
好像这有点错了吧,应该用所有的 inlier 匹配计算 Homography,再用这个更靠谱的 Homography 分解计算 \(R, t\)。
函数ReconstructH()
最后也输出三角化成功的三维点。
Initializer::FindFundamental()
FindFundamental()
的计算过程与 FindHomography 类似,都是需要进行归一化操作。
函数ComputeF21()
用八点法计算 Fundamental,计算得到的实际 Fundamental 通过设置最小特征值为 0 投影到 Fundamental 空间,作为输出。
函数CheckFundamental()
是将点与线的距离作为误差,计算卡方距离,注意这里有一个自由度,所以显著性检验使用的卡方距离为 3.84。
都差不多,没啥好说的。
Initializer::CheckRT()
这个函数挺重要的,因为分解 H 和 F 都会有很多可能的结果,使用这个函数能够分辨出什么结果是靠谱的。
函数CheckRT()
接受 \(R, t\) ,一组成功的匹配。最后给出的结果是这组匹配中有多少匹配是能够在这组 \(R, t\) 下正确三角化的(即 \(Z\) 都大于0),并且输出这些三角化之后的三维点。
如果三角化生成的三维点 \(Z\) 小于等于0,且三角化的“前方交会角”(余弦是 cosParallax)不会太小,那么这个三维点三角化错误,舍弃。
通过了 \(Z\) 的检验,之后将这个三维点分别投影到两张影像上,计算投影的像素误差,误差大于2倍中误差,舍弃。
总结
ORB 里面对通过“最大值”确定的结果都非常小心。一般要求这个“最大值” outstanding,如 ORBmatcher 的构造函数中就有会传入一个 (0,1) 的数值给成员变量 mfNNratio,只有最小距离小于次小距离的 mfNNratio 倍才能算是匹配成功,不允许出现相似的匹配,而取好那么一点点的匹配作为匹配结果。
在Initializer::ReconstructH()
中最后 8 个可能结果中,最好模型 inlier 数要大于次好模型 inlier 的 1/0.75 倍。
ORB_SLAM2 源码阅读 ORB_SLAM2::Initializer的更多相关文章
- ORB_SLAM2 源码阅读 ORB_SLAM2::Initializer::ComputeF21 (OpenCV 细节)
ORB_SLAM2 计算 F21 的代码是这样的. cv::Mat Initializer::ComputeF21(const vector<cv::Point2f> &vP1,c ...
- ORB_SLAM2 源码阅读 ORB_SLAM2::ORBextractor
整体架构 构造函数进行初始化,传入设定几个重要的成员变量.nfeatures(特征点的个数).nlevels(构造金字塔的层数).scaleFactor(金字塔中相邻层图像的比例系数).iniThFA ...
- 应用监控CAT之cat-client源码阅读(一)
CAT 由大众点评开发的,基于 Java 的实时应用监控平台,包括实时应用监控,业务监控.对于及时发现线上问题非常有用.(不知道大家有没有在用) 应用自然是最初级的,用完之后,还想了解下其背后的原理, ...
- 【原】FMDB源码阅读(三)
[原]FMDB源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 FMDB比较优秀的地方就在于对多线程的处理.所以这一篇主要是研究FMDB的多线程处理的实现.而 ...
- 【原】FMDB源码阅读(二)
[原]FMDB源码阅读(二) 本文转载请注明出处 -- polobymulberry-博客园 1. 前言 上一篇只是简单地过了一下FMDB一个简单例子的基本流程,并没有涉及到FMDB的所有方方面面,比 ...
- 【原】FMDB源码阅读(一)
[原]FMDB源码阅读(一) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 说实话,之前的SDWebImage和AFNetworking这两个组件我还是使用过的,但是对于 ...
- 【原】AFNetworking源码阅读(六)
[原]AFNetworking源码阅读(六) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这一篇的想讲的,一个就是分析一下AFSecurityPolicy文件,看看AF ...
- 【原】AFNetworking源码阅读(五)
[原]AFNetworking源码阅读(五) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中提及到了Multipart Request的构建方法- [AFHTTP ...
- 【原】AFNetworking源码阅读(四)
[原]AFNetworking源码阅读(四) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇还遗留了很多问题,包括AFURLSessionManagerTaskDe ...
随机推荐
- HDU 6166 Senior Pan(二进制分组+最短路)
题意 给出一个\(n\)个点\(m\)条边的有向图\((n,m<=100000)\),从中选择\(k\)个点\((k<=n)\),问这k个点两两之间的最短路最小值是多少? 思路 直接的想法 ...
- C++模板编程中只特化模板类的一个成员函数
模板编程中如果要特化或偏特化(局部特化)一个类模板,需要特化该类模板的所有成员函数.类模板中大多数成员函数的功能可能是一模一样的,特化时我们可能只需要重新实现1.2个成员函数即可.在这种情况下,如果全 ...
- Container With Most Water - LeetCode
目录 题目链接 注意点 解法 小结 题目链接 Container With Most Water - LeetCode 注意点 没什么好注意的... 解法 解法一:暴力求解,假设任意两个端点会是最佳答 ...
- STS导入Gradle项目出现 Could not create task of type 'DependencyManagementReportTask'
解决方法: 在build.gradle文件下添加如下: classpath 'io.spring.gradle:dependency-management-plugin:0.6.1.RELEASE'
- 解题:洛谷4178 Tree
题面 重(新)学点分治中...... 普通的点分治一般这几步: 1.找重心 2.从重心开始DFS,得到信息 3.统计经过重心的路径 4.分别分治几棵子树,继续这个过程 然后是常见的(制杖的我的)一些疑 ...
- fzyjojP2931 乱搞
其实很简单(第二个不知是啥) 贡献独立 其实第一种就是考虑一个点在哈夫曼树上的期望深度是多少 因为精度要求较高 所以要高精小数加,高精小数除以低精整数
- laravel 命令行输出进度条
有时候我们想在命令行执行一些耗时的命令,我们可以利用 symfony 提供的进度条相关的类,来输出一个进度条,显示当前的处理进度. 参考:http://symfony.com/doc/current/ ...
- Python高手之路【四】python函数装饰器,迭代器
def outer(func): def inner(): print('hello') print('hello') print('hello') r = func() print('end') p ...
- P1486 [NOI2004]郁闷的出纳员
P1486 [NOI2004]郁闷的出纳员 题目描述 OIER公司是一家大型专业化软件公司,有着数以万计的员工.作为一名出纳员,我的任务之一便是统计每位员工的工资.这本来是一份不错的工作,但是令人郁闷 ...
- ios中iframe的scroll滚动事件替代方法
在公众号的开发中,遇到ios中iframe的scroll滚动事件失效,在此做下记录. 因为接口获取的数据必须放在iframe中展示,滚动到底部按钮变亮,如图: 代码如下: <!DOCTYPE h ...