提出的背景是一方面高效率的局部算法由于所基于的局部窗口视差相同的假设在很多情况下并不成立导致匹配效果较差;而另一方面全局算法虽然通过二维相邻像素视差之间的约束(如平滑性约束)而得到更好的匹配效果,但是对内存的占用量大,速度慢。
由于代价计算步骤只考虑了局部的相关性(也就是比较两个像素邻域像素的关系,如果邻域内某一像素受到噪声影响,则该像素的像素值和原来的像素值相差很大,这会使得两个像素在计算相关性的时候和原来的相关性相差很大,这就会导致正确的同名点对应的视差代价不是最小的,也就无法找出正确的同名点。)对噪声非常敏感,无法直接用来计算最优视差,所以SGM算法通过代价聚合步骤(将更多的像素考虑进来),使聚合后的代价值能够更准确的反应像素之间的相关性,如图1所示。聚合后的新的代价值保存在与匹配代价空间C同样大小的聚合代价空间S中,且元素位置一一对应。
SGM算法依旧采用全局框架,即全局能量最优化策略,简单来说就是寻找每个像素的最优视差使得整张影像的全局能量函数最小。但是在计算能量函数最小化的步骤时使用高效率的一维路径聚合方法来代替全局算法中的二维最小化算法。
为了让视差图满足某些条件假设的约束,如场景表面的连续性假设,平滑项会对相邻像素视差变化超过一定像素的情况进行惩罚(惩罚一般就是加大能量值)。
解释下视差连续与视差非连续,视差连续代表局部范围内的像素的视差相差很小(1个像素内),是一个连续的变化趋势;而非连续是指局部范围内的像素视差相差很大(超过1个像素),是一个突变的变化趋势。这个局部范围往往是一个矩形的窗口(比如3x3、5x5)。由于视差和深度某种程度上其实是等价的(视差和深度的关系可以查看博客双目立体匹配中的核线约束[极线约束]),所以视差连续性背后表达的是空间中目标表面离相.机的距离的连续性,如果是目标是连续的表面在影像上成像,则成像范围内视差也是连续的;而如果目标有前景和背景在影像上成像,则前景和背景的交界处,在局部窗口内会是一部分属于前景一部分属于背景,前景和背景离相机的距离就可能相差很大了,视差也会相差很大,即不连续。
代价聚合
某像素在某视差下沿某路径的路径代价为(动态规划问题,该像素的路径代价使用上一个像素的路径代价计算)
第三项为保证路径代价不会超过一定的数值上限。
对上式做出说明:
- 以左侧路径为例,也就是从左向右聚合,p-r就是左侧相邻像素,它们行号相等列号相差1
- 关于i的说明,i可以取任何可能的视差即便最终求得的i=d+1或则d-1,那么此时最小值一定不会是
-
我之前一直不明白这个聚合的具体怎么进行的,看了很多文章后有了自己的理解(有误恳请指出),现通过一些图示进行解释。比如,我们现在需要计算p点的聚合代价值,那么我们在计算之前肯定是知道p的各个视差下的初始匹配代价(就存储在立体匹配之匹配代价计算求出来的矩阵C里面嘛),所有对于p来说,要求p的路径聚合代价,那么根据公式4,其第一部分C(p,d)肯定是已知的。这里理解了的话,那么其实对于图像中的任意一个像素,都可以在初始代价矩阵C中找到各个视差下的初始匹配代价,也就是对于公式4来说,第一部分的C(p,d)都是已知的。再来看公式4的第二项,不难看出,这一项主要是要寻找前一个像素的路径聚合代价。前一个像素的路径代价???我们根本不知道啊,其实这里就是一个不断递归的过程,不断寻找前一个像素的路径代价直到一个特定的像素为止。这里特定的像素可以理解为我设置一个递归次数,递归次数到了,此时递归到了某个像素p,那么这个p像素就是所谓的特定的像素。有人要问了,p像素我也不知道它的路径聚合代价啊,对了,这时我们就要对其进行初始化,让p像素的聚合代价值L等于它的初始代价值C。这时我们有了第一个像素路径聚合代价,那么我们就可以一步步的算出其它像素的路径聚合代价,当然目标像素的路径聚合代价也可以算出。下面用一张图表示一个方向上某个像素的代价聚合过程。
上图表示的是各个视差下某个方向上的一个像素的代价聚合过程。
我们常用的路径就是4,8,16,这样我们从常用情况出发,就可以说路径数不超过16
至此我们已经求到了每个像素的最佳的视差,因此以下都是得到最佳视差之后的优化。
实际代码中优化顺序
其中视差计算先进行了唯一性约束,然后进行了子像素拟合
注意进行以下视差优化的时候最佳视差已经是子像素的了(不一定是整数视差)
左右一致性检测
左视图某像素p的视差为d,它在右视图的同名点为q,则q的视差和d相差小于1个像素的话,那么左视图的视差就是d否则为无效值。
一致性检查有两种策略,一种是内部型,一种是外部型。
内部型就是直接通过左影像的代价数组,来推算右影像的代价数组,从而可以将右影像每个像素的所有候选视差d的代价值Cost(i,j,d)都得到,进而寻找最小代价值对应的视差,并做子像素优化,得到右影像视差图(所以你只用代价聚合一次就可以做一致性检查)。然后将左侧的(i,j)像素点的最佳视差和右侧(i,j-d)像素点的最佳视差进行比较,如果小于1个像素则保留左像素点的最佳视差,否则将它置为一个无效值。(先计算候选视差,再比较最佳视差)
通过左影像的代价数组,来推算右影像的代价数组的方法是:
右影像(i,j)视差为d的代价 = 左影像(i,j+d)视差为d的代价。
剔除小连通区域
连通区域像素个数如果小于某一阈值就将这个连通区域内所有像素置为无效值
中值滤波
由于邻域中一个非常不具有代表性的像素不会对中值产生显著影响(噪声会使得领域中的某个值特别大或则特别小,因此不可能是中值)因此可以剔除一些离群点
领域中的中值必须是领域中的某个像素值,不可能创建出新的像素值,因此该像素点被中值替代后和原有的像素值很接近(除了噪声点)因此中值滤波还能够很好的保持图像的边缘特征。
视差填充
无效区域分为遮挡区和误匹配区。
误匹配区
位于视差连续的区域,因此填补时可以考虑邻域内所有的像素
遮挡区
位于视差非连续区域,导致背景区域被前景遮挡,遮挡区中的像素在左视图可见,在右视图不可见。因此它这块区域的像素理应和背景区域更加接近,因此填补时尽量选择邻域内背景像素的视差。
这里讲下为什么遮挡区域在左视图可见右视图不可见:
因为求的是左视差图,则默认左图像的所有像素点都可见,而左图像上有的右图像上没有的区域认为是遮挡区域。
遮挡区和误匹配区的判别
- 在一致性检测之前计算视差的时候已经通过唯一性检测和子像素拟合操作(它们都在计算视差的函数中)判定了一些无效像素,将这些像素即为误匹配像素
- 遮挡区的判断:某像素p的(最佳)视差为d,则它的同名点q在右图像上的坐标xq为xp-d,它的同名点的(最佳)视差为dr,则q在左图像的同名点p'的坐标为xq+dr,p'的最佳视差为d',如果d'>d,就表明p像素位于遮挡区域(左像素遮挡区一般会匹配到右图像中前景像素,因此右像素的同名点的视差值要大于左像素的)
- 具体步骤为:
- 视差计算,视差计算的同时进行唯一性检测,子像素拟合,不满足唯一性检测的像素置为无效视差,最佳视差为最小视差因为左侧没有视差无法进行子像素拟合就将这个最佳视差即为无效,最佳视差为最大视差减一(视差实际只取到最佳视差减1,前闭后开)因为右侧没有视差无法进行子像素拟合就将这个最佳视差即为无效
- 进行一致性检测,一致性检测函数中首先将第一步的无效像素位置记录在误匹配像素的容器中,然后找左像素的同名点,如果找不到(减去视差值之后得到的位置小于0或则大于图像的宽度)则将左像素的位置记录在误匹配容器中,然后判断左像素和它同名点的视差差值是否满足一致性约束,不满足就进行是误匹配点还是遮挡点的判断
- 右侧的同名点再在左图像上找同名点如果找不到则将左像素的位置记录在误匹配容器中,左侧同名的视差如果大于左像素的视差则判定为遮挡区像素,否则就是误匹配区像素
- 将上述所有操作的像素点的视差值置为无效
有效像素的选取步骤为:
选取0~max(abs(最小视差),(最大视差))这段长度设为L,选取8个方向
首先浏览一个方向,计算各段长度的横坐标和纵坐标(xx=x+lround(l*cosβ),yy=x+lround(l*sinβ)),如果这个像素的视差有效就装进有效视差容器
弱纹理优化
弱纹理优化的三个假设:
- 弱纹理区域视差均连续(非连续区域的亮度一般是不一致的,因此不是弱纹理)
- 弱纹理区域存在一些可见纹理
- 可以用一个平面代表弱纹理区域(非平面往往存在着一些亮度的差异)
参考文章:
https://ethanli.blog.csdn.net/article/details/83754473