建图的功能:
具体的各种地图类型与用途之间的关系:
稀疏地图只建模感兴趣的部分,也就是前面说了很久的特征点(路标点)。
稠密地图是指建模所有看到过的部分。
在稠密重建中,我们需要知道每一个像素点的距离,对此的解决方案为:
单目双目的缺点:计算量巨大,最后得到不怎么可靠的深度估计。
因此,稠密地图的重建往往选择RGB-D。
RGB-D缺点:无法被很好的应用在室外、大场景场合中。而单目、双目仍能通过立体视觉估计深度信息。
针对单目的稠密估计,需要完成的是:在给定相机轨迹的基础上,如何根据一段时间的视频序列,来估计某张图像的深度。换言之,我们不考虑 SLAM,先来考虑稍为简单的建图问题。
假定有某一段视频序列,我们通过某种算法得到了每一帧对应的轨迹(当然也很可能是由视觉里程计前端估计所得)。现在我们以第一张图像为参考帧,计算参考帧中每一个像素的深度(或者说距离)。
在使用特征点时,通过以下两步完成这个过程:
由于在在稠密深度估计中,不同之处是,在这里我们不能把每个像素当作特征点计算描述子。因此在这里匹配很重要。也就是要解决:如何确定第一幅图的某像素出现在其他图里的位置。
当我们知道了某个像素在各个图中的位置,就能像特征点那样,利用三角测量确定它的深度。不过不同的是,在这里我们要使用很多次三角测量让深度估计收敛,而不仅是一次。我们希望深度估计,能够随着测量的增加,从一个非常不确定的量,逐渐收敛到一个稳定值。这就是深度滤波器技术。
根据题目,这一部分分为极线搜索和块匹配。
极线搜索就是:在相机在第一个视角拍摄到的图像中确定一个像素 p 1 p_1 p1,根据目前相机的角度可以确定 p 1 p_1 p1像素的深度在某个区域之内,某最小值到无穷远之间。故该像素点对应的空间点就分布在某条线段上。当相机变换了另一个视角,这条线段的投影也形成图像平面上的一条线,这条线就是极线。当知道相机在这两个视角的运动时,这条极线就能够确定,最终的问题就是:极线上的哪一点是 p 1 p_1 p1。现在是根据运动来匹配像素,与之前的前端不同的是:前端是先匹配特征点而不是所有的像素(因为不能每个像素都计算描述子),然后根据匹配的特征点来估计相机的运动。这里是根据相机的运动匹配像素
这里在寻找 p 2 p_2 p2的过程中,不像特征点法那样计算描述子,这里没有计算,所以只能在极线上搜索和 p 1 p_1 p1长得比较相似的点。所以叫极线搜索。如果一个一个比较像素,有点像直接法。易受光照影响,同时如果有与 p 1 p_1 p1相似的点,但不是真的点呢。这不就有点像回环检测那一部分吗,相似性。
解决方法:在 p 1 p_1 p1 周围取一个大小为 w × w w × w w×w 的小块,然后在极线上也取很多同样大小的小块进行比
较,就可以一定程度上提高区分性。这就是所谓的块匹配。从而假设变成了从像素的灰度不变性变成了图像块的灰度不变性。
取 p 1 p_1 p1周围的小块 A ∈ R w × w \boldsymbol{A} \in \mathbb{R}^{w \times w} A∈Rw×w,极线上的 n n n个小块记成 B i , . . . , n \boldsymbol{B}_i,...,n Bi,...,n。计算相似度:
SAD(Sum of Absolute Difference)。顾名思义,即取两个小块的差的绝对值之和:
S ( A , B ) S A D = ∑ i , j ∣ A ( i , j ) − B ( i , j ) ∣ S(\boldsymbol{A}, \boldsymbol{B})_{S A D}=\sum_{i, j}|\boldsymbol{A}(i, j)-\boldsymbol{B}(i, j)| S(A,B)SAD=i,j∑∣A(i,j)−B(i,j)∣
SSD。 SSD 并不是说大家喜欢的固态硬盘,而是 Sum of Squared Distance(SSD)(平方和)的意思:
S ( A , B ) S S D = ∑ i , j ( A ( i , j ) − B ( i , j ) ) 2 S(\boldsymbol{A}, \boldsymbol{B})_{S S D}=\sum_{i, j}(\boldsymbol{A}(i, j)-\boldsymbol{B}(i, j))^{2} S(A,B)SSD=i,j∑(A(i,j)−B(i,j))2
NCC(Normalized Cross Correlation)(归一化互相关)。这种方式比前两者要复杂一些,它计算的是两个小块的相关性:
S ( A , B ) N C C = ∑ i , j A ( i , j ) B ( i , j ) ∑ i , j A ( i , j ) 2 ∑ i , j B ( i , j ) 2 S(\boldsymbol{A}, \boldsymbol{B})_{N C C}=\frac{\sum_{i, j} \boldsymbol{A}(i, j) \boldsymbol{B}(i, j)}{\sqrt{\sum_{i, j} \boldsymbol{A}(i, j)^{2} \sum_{i, j} \boldsymbol{B}(i, j)^{2}}} S(A,B)NCC=∑i,jA(i,j)2∑i,jB(i,j)2∑i,jA(i,j)B(i,j)
注意:由于这里用的是相关性,所以相关性接近 0 表示两个图像不相似,而接近1才表示相似。前面两种距离则是反过来的,接近 0 表示相似,而大的数值表示不相似。
和我们遇到过的许多情形一样,这些计算方式往往存在一个精度——效率之间的矛盾。精度好的方法往往需要复杂的计算,而简单的快速算法又往往效果不佳。这需要我们在实际工程中进行取舍。另外,除了这些简单版本之外,我们可以先把每个小块的均值去掉,称为去均值的 SSD、去均值的 NCC 等等。去掉均值之后,我们允许像“小块 B 比 A 整体上亮一些,但仍然很相似”这样的情况,因此比之前的更加可靠一些。(这样好像可以避免掉光照的影响)
就是估计深度,使之达到最优,同时计算量也不大。
第一种方法:假设深度值服从高斯分布,得到一种类卡尔曼式的方法。
第二种方法:均匀-高斯混合分布的假设,另一种形式更为复杂的深度滤波器。
这里主要介绍第一种方法:
设某个像素点的深度 d d d 服从:
P ( d ) = N ( μ , σ 2 ) P(d)=N\left(\mu, \sigma^{2}\right) P(d)=N(μ,σ2)
每当新的数据到来,我们都会观测到它的深度。同样的,假设这次观测亦是一个高斯分布:
P ( d o b s ) = N ( μ o b s , σ o b s 2 ) P\left(d_{o b s}\right)=N\left(\mu_{o b s}, \sigma_{o b s}^{2}\right) P(dobs)=N(μobs,σobs2)
于是,我们的问题是,如何使用观测的信息,更新原先 d d d 的分布。这正是一个信息融合问题。我们明白两个高斯分布的乘积依然是一个高斯分布。设融合后的 d d d的分布为 N ( μ fuse , σ fuse 2 ) N\left(\mu_{\text {fuse }}, \sigma_{\text {fuse }}^{2}\right) N(μfuse ,σfuse 2),那么根据高斯分布的乘积,有:
μ f u s e = σ o b s 2 μ + σ 2 μ o b s σ 2 + σ obs 2 , σ fuse 2 = σ 2 σ o b s 2 σ 2 + σ obs 2 \mu_{f u s e}=\frac{\sigma_{o b s}^{2} \mu+\sigma^{2} \mu_{o b s}}{\sigma^{2}+\sigma_{\text {obs }}^{2}}, \quad \sigma_{\text {fuse }}^{2}=\frac{\sigma^{2} \sigma_{o b s}^{2}}{\sigma^{2}+\sigma_{\text {obs }}^{2}} μfuse=σ2+σobs 2σobs2μ+σ2μobs,σfuse 2=σ2+σobs 2σ2σobs2
由于我们仅有观测方程而没有运动方程,所以这里深度仅用到了信息融合部分,而无须像完整的卡尔曼那样进行预测和更新。(这里也可以看成“运动方程为深度值固定不动”的卡尔曼滤波器)。那么现在的问题是:如何求 μ o b s \mu_{o b s} μobs和 σ o b s 2 \sigma_{o b s}^{2} σobs2
这里的不确定性包含两种,一种是几何不确定性和,另一种是光度不确定性。现在暂时只考虑几何不确定性。现在需要确定的是通过极线搜索和块匹配确定的某参考帧中某个像素的这个位置对深度的不确定性有多大。
以上图为例。考虑某次极线搜索,我们找到了 p 1 p_1 p1 对应的 p 2 p_2 p2 点,从而观测到了 p 1 p_1 p1的深度值,认为 p 1 p_1 p1 对应的三维点为 P P P。从而,可记 O 1 P O_1P O1P 为 p p p, O 1 O 2 O_1O_2 O1O2 为相机的平移 t t t, O 2 P O_2P O2P 记为 a a a。并且,把这个三角形的下面两个角记作 α , β α, β α,β。现在,考虑极线 l 2 l_2 l2 上存在着一个像素大小的误差,使得 β β β 角变成了 β ′ β^′ β′,而 p p p 也变成了 p ′ p^′ p′,并记上面那个角为 γ γ γ。我们要问的是,这一个像素的误差,会导致 p ′ p^′ p′ 与 p p p 产生多大的差距呢?
根据上面的图,显然有:
a = p − t α = arccos ⟨ p , t ⟩ β = arccos ⟨ a , − t ⟩ \begin{aligned} a &=\boldsymbol{p}-\boldsymbol{t} \\ \alpha &=\arccos \langle\boldsymbol{p}, \boldsymbol{t}\rangle \\ \beta &=\arccos \langle\boldsymbol{a},-\boldsymbol{t}\rangle \end{aligned} aαβ=p−t=arccos⟨p,t⟩=arccos⟨a,−t⟩
对 p 2 p_2 p2 扰动一个像素,将使得 β β β 产生一个变化量,成为 β ′ β^′ β′。根据几何关系:
β ′ = arccos ⟨ O 2 p 2 ′ , − t ⟩ γ = π − α − β ′ \begin{aligned} \beta^{\prime} &=\arccos \left\langle\boldsymbol{O}_{2} \boldsymbol{p}_{2}^{\prime},-\boldsymbol{t}\right\rangle \\ \gamma &=\pi-\alpha-\beta^{\prime} \end{aligned} β′γ=arccos⟨O2p2′,−t⟩=π−α−β′
于是,由正弦定理, p ′ p^′ p′的大小可以求得
∥ p ′ ∥ = ∥ t ∥ sin β ′ sin γ \left\|\boldsymbol{p}^{\prime}\right\|=\|\boldsymbol{t}\| \frac{\sin \beta^{\prime}}{\sin \gamma} ∥p′∥=∥t∥sinγsinβ′
因此,确定了由单个像素的不确定性引起的深度不确定性。如果认为极线搜索的块匹配仅一个像素的误差,那么就可以设:
σ o b s = ∥ p ∥ − ∥ p ′ ∥ \sigma_{\mathrm{obs}}=\|\boldsymbol{p}\|-\left\|\boldsymbol{p}^{\prime}\right\| σobs=∥p∥−∥p′∥
如果极线搜索的不确定性大于一个像素是,可以按照次推导放大这个不确定性。
在实际工程中,当不确定性小于一定阈值时,就可以认为深度数据已经收敛了。
因此,估计稠密深度的一个完整过程为:
- 假设所有像素的深度满足某个初始的高斯分布;
- 当新数据产生时,通过极线搜索和块匹配确定投影点位置;
- 根据几何关系计算三角化后的深度以及不确定性;
- 将当前观测融合进上一次的估计中。若收敛则停止计算,否则返回 2。
这些步骤组成了一套可行的深度估计方式。书上说:这里的深度值是 O 1 P O_1P O1P的长度,与针孔相机模型李提到的“深度”有少许不同,针孔模型中的深度是指像素的z值。这里有些不懂,为什么不一样?懂的朋友希望评论区教我一下,感谢!
在块匹配的过程中,过度依赖物体的纹理,即有明显梯度的小块匹配的准确性高,梯度不明显的反之。进而影响深度计算的精度。
像素梯度垂直于极线方向,以及平行于极线方向。先来看平行的情况。在平行的例子里,即使小块有明显梯度,但是当我们沿着极线去做块匹配时,会发现匹配程度都是一样的,因此得不到有效的匹配。反之,在垂直的例子里,我们能够精确地确定匹配度最高点出现在何处。而实际当中,梯度与极线的情况很可能位于二者之间:既不是完全垂直亦不是完全平行。这时,我们说,当像素梯度与极线夹角较小时,极线匹配的不确定性大;而当夹角较大时,匹配的不确定性变小。而在演示程序中,我们统一地把这些情况都当成一个像素的误差,实质是不够精细的。考虑到极线与像素梯度的关系后,应该使用更精确的不确定性模型。
假设深度值满足高斯分布 d ∼ N ( μ , σ 2 ) d \sim N\left(\mu, \sigma^{2}\right) d∼N(μ,σ2),会出现一些不合理的点:
而逆深度为高斯分布是比较有效的,逆深度就是深度的代数 d ( − 1 ) d_(-1) d(−1)
问题:我们假设了图像小块在相机运动时保持不变,而这个假设在相机平移能够保持成立,但当相机发生明显的旋转时,就难以继续保持了。特别地,当相机绕光心旋转时,一个下黑上白的图像可能会变成一个上黑下白的图像块,导致相关性直接变成了负数(尽管仍然是同样一个块)。
解决方案:将当前帧的图像小块变换到关键帧上对应的小块,使用关键帧上对应的图像小块进行匹配。也就是在块匹配之前,把参考帧与当前帧之间的运动考虑进来。
根据相机模型,参考帧上的一个像素 P R P_R PR 与真实的三维点世界坐标 P W P_W PW 有以下关系:
d R P R = K ( R R W P W + t R W ) d_{R} \boldsymbol{P}_{R}=\boldsymbol{K}\left(\boldsymbol{R}_{R W} \boldsymbol{P}_{W}+\boldsymbol{t}_{R W}\right) dRPR=K(RRWPW+tRW)
类似的,对于当前帧,它亦有 P W P_W PW 在它上边的投影,记作 P C P_C PC:
d C P C = K ( R C W P W + t C W ) d_{C} \boldsymbol{P}_{C}=\boldsymbol{K}\left(\boldsymbol{R}_{C W} \boldsymbol{P}_{W}+\boldsymbol{t}_{C W}\right) dCPC=K(RCWPW+tCW)
消去 P W P_W PW,进而得到两幅图像的像素关系:
d C P C = d R K R C W R R W T K − 1 P R + K t C W − K R C W R R W T K t R W d_{C} \boldsymbol{P}_{C}=d_{R} \boldsymbol{K} \boldsymbol{R}_{C W} \boldsymbol{R}_{R W}^{T} \boldsymbol{K}^{-1} \boldsymbol{P}_{R}+\boldsymbol{K} \boldsymbol{t}_{C W}-\boldsymbol{K} \boldsymbol{R}_{C W} \boldsymbol{R}_{R W}^{T} \boldsymbol{K} \boldsymbol{t}_{R W} dCPC=dRKRCWRRWTK−1PR+KtCW−KRCWRRWTKtRW
当知道 d R , P R d_R,P_R dR,PR 时,可以计算出 P C P_C PC 的投影位置。再给 P R P_R PR 两个分量各一个增量 d u , d v du, dv du,dv,就可以求得 P C P_C PC 的增量 d u c , d v c du_c, dv_c duc,dvc。通过这种方式,算出在局部范围内,参考帧和当前帧图像坐标变换的一个线性关系,构成仿射变换:
[ d u c d v c ] = [ d u c d u d u c d v d v c d u d v c d v ] [ d u d v ] \left[\begin{array}{l} d u_{c} \\ d v_{c} \end{array}\right]=\left[\begin{array}{ll} \frac{d u_{c}}{d u} & \frac{d u_{c}}{d v} \\ \frac{d v_{c}}{d u} & \frac{d v_{c}}{d v} \end{array}\right]\left[\begin{array}{l} d u \\ d v \end{array}\right] [ducdvc]=[duducdudvcdvducdvdvc][dudv]
根据仿射变换矩阵,我们可以把当前帧(或参考帧)的像素进行变换后,再进行块匹配,以期获得对旋转更好的效果。
点云地图为我们提供了比较基本的可视化地图,让我们能够大致了解环境的样子。它以三维方式存储,使得我们能够快速地浏览场景的各个角落,乃至在场景中进行漫游。点云的一大优势是可以直接由 RGB-D 图像高效地生成,不需要额外的处理。它的滤波操作也非常直观,且处理效率尚能接受。不过,使用点云表达地图仍然是十分初级的。
点云地图是“基础”的或“初级的”,是指它更接近于传感器读取的原始数据。它具有一些基本的功能,但通常用于调试和基本的显示,不便直接用于应用程序。如果我们希望地图有更高级的功能,点云地图是一个不错的出发点。
缺陷:
八叉树地图有点像我的世界那样,使用一个一个方块进行堆叠。是一种灵活的、压缩的、又能随时更新的地图形式。
1.视觉SLAM十四讲