视差与深度
视差
上篇文章中我们对双目相机的成像结果进行了校正,如下图所示,对于同名点对 ,它们的纵坐标是相同的,它们的横坐标之差 称为视差。
深度
如封面所示,将手指放在视野中间,交替闭上左右眼,观察手指相对于视野前方某个物体的位置变动,这就是视差,并且越远的物体视差越大,越近的物体视差越小。
对于左视图中的每个点,去右视图中寻找同名点,并计算视差,我们可以得到一个灰度图,称为视差图或深度图,如下图所示。
图中越亮的地方视差越小,也就是越近;越暗的地方视差越大,也就是越远。并且深度图中左侧会有一部分黑色,这表示左视图中这部分的点无法在右视图中找到同名点,因为左右相机拍摄的视野范围不同。
通过校正后的双目图像得到深度图的过程就叫做立体匹配,上面的深度图是通过 SGM 立体匹配算法得到的,下面展开来讲。
立体匹配
想获取视差图需要先找到同名点对。设 为左视图中的像素点, 为右视图中纵坐标为 的像素点。因为图像是校正过的,所以 的同名点 只需要遍历 去寻找,如下图所示。
代价空间
那么在遍历过程中,如何确定 和 是否是同名点呢?这里引入代价(Cost)的概念, 的代价为 ,代价越小, 为同名点的可能性越高。
代价函数 的计算方式不唯一,这里函数 这里表示像素点对应的颜色值,是最简单的一种代价计算。
又因为 , 表示视差,因此上面的等式还可以这样写。
这里不用太纠结是 还是 ,这里以本文第一张图片为例,使用 表示。
函数 在空间坐标系中表示一个立方体,称该立方体为代价空间,如上图所示,每个单元存储一对像素点的代价。
对于左视图中的一个像素点 ,,当 时 最小,则 的同名点为 。如此确定同名点的方式称为 Winner Takes All (WTA), 为最优代价。
代价函数
代价计算函数有很多种,上面提过的 称为 Absolute Difference (AD),这里使用 来表示。
在 的基础上还有 (Sum of Absolute Difference),用指定的像素点及其周围的像素点组成一个正方形的窗口,相比于 效果会更好一些。
上面公式中 表示一个 大小的窗口。
除了 还有其他种类的代价计算函数,不过比较复杂这里就不多说了,这里把代价计算函数当作一个黑盒就行。
代价聚合
到了这里我们通过代价计算函数得到了代价空间,有 WTA 我们可以获得所有的同名点对并计算得到深度图,但是这样子得到的深度图的效果还是不好的。
我们需要对得到的代价空间进行优化,这个优化的过程称为代价聚合,要解释代价聚合,首先明确视差平面的概念。
如上图所示,代价空间由若干个视差平面组成,代价聚合就是在视差平面上进行滤波操作,比较简单的一种是使用均值滤波,滤波可以理解为对视差平面的平滑处理。
如上图所示,遍历视差平面,对每个 取周围 窗口内均值为 处新的代价,这个过程就是均值滤波。
OpenCV 中可以使用 blur 函数进行均值滤波,滤波前后结果如下图所示。
之所以要动手跑一下主要是好奇在遍历取均值的过程中,后面的会不会使用前面计算好的值,得出的结果是不会。
上面说的是局部优化的思路,相对应的是全局优化的思路,即为每个像素寻找到一个视差值,使得整体能量 最小。
能量 由两部分组成:
- 所有像素点的代价之和。
- 对于每个像素点的领域点,如果视差连续则加上 ,如果视差不连续则加上 ,。
对于上图中标注的窗口来说,局部优化视窗口内所有像素的视差(深度)相同,而全局优化则是将视差(深度)也考量在内。至于如何寻找每个像素的视差值使得 最小则是一个困难的问题。
视差优化
通过上面说的一系列步骤,我们能得到若干个 ,将这些拼凑成图片,像素 处的值为 ,就是一张视差图。
这里获取的视差图还需要进一步优化,例如对视差图进行一次滤波操作等等。
End
最后总结一下立体匹配的流程:
- 代价计算
- 代价聚合
- 局部法 + WTA
- 局部法(可选) + 全局法
- 视差优化
立体匹配算法有很多种,但大体都是这个流程,只不过不同算法对于每一步都有不同的实现。