一、简介

  立体匹配旨在为校正后的左右视图提供稠密的匹配对,这种问题称为"stereo correspondence problem"。有大量的算法用于求解立体匹配问题,根据Scharstein和 Szeliski的研究,所有这些算法的计算流程基本上都可以分为四个步骤:A.匹配代价计算;B.代价聚合; C.视差计算/优化; D.视差细化。其中匹配代价用来度量像素或者像素块之间的相似性,计算匹配代价的方法有AD、SAD、BT、NCC、Census-Hamming、HMI、Daisy以及基于深度学习方法的匹配代价等等。代价聚合的目的是将每个像素的匹配代价和它周围的像素关联起来,常用的方法是将一个固定尺寸的窗口中的相同视差下的匹配代价简单求和、加权求和,或者在利用图像中的梯度或颜色进行分割后的区域中进行聚合,还有一些方法使用引导滤波等进行代价聚合。
  在上述框架下,立体匹配算法基本被分类为两种:(1)局部算法:A => B => C => D(2)全局算法:A => C => D。局部算法一般在代价聚合之后,通过选择最低匹配代价来得到对应的视差,即winner takes all。全局算法没有进行代价聚合,而是定义了一个包含数据项和平滑项的能量函数并通过最小化能量函数来求得视差,数据项一般为所有像素的代价和,平滑项一般为对邻域像素视差差异的惩罚,部分方法还添加更多的项,比如对遮挡区域的惩罚、左右一致性或对称性约束,或者根据分割区域对平滑项进行加权等。根据所使用的优化方法,全局算法主要有Dynamic programming、Graph Cut、Belief Propogation等。

​  全局算法由于非常高的运算量或内存消耗,在大多数场合都无法应用,而局部算法虽然速度很快,但是鲁棒性差,匹配质量比较低。2005年,Heiko Hirschmuller提出了一种半全局的立体匹配算法,叫做SGM,该算法建议采用单像素互信息(HMI)作为匹配代价,沿着多个方向进行一维能量最小化来近似替代二维全局能量最小化,因此被称为半全局算法。SGM的运算速度远远快于大多数全局算法,同时精度也比较高,除了深度学习的方法外,SGM在各种公开数据集的双目匹配任务的排行榜上一直都位列前排,证明了SGM算法的强大。在OpenCV中高度优化的SGBM算法对于1920*1080分辨率的图像,其运行时间可以低至300ms以内(当然这还取决于机器性能)。

二、SGM

1.pixelwise匹配代价计算:HMI

​  互信息(Mutual Information)用于表征两幅图像共有信息量的大小,可作为两幅图像相似性的度量,然而MI是针对整张图像而言的,Junhwan Kim通过对MI进行泰勒展开得到单像素的互信息HMI的计算方法,该方法作为像素相似性度量对光照变化较为鲁棒,是一种优秀的代价函数。关于如何求解单像素互信息,限于篇幅这里就不叙述了,详情可以查看作者论文。

2.动态规划

2.1一些基本概念

  动态规划(dynamic programming)是一种求解多阶段决策过程(decision process)最优化的数学方法,即利用各阶段之间的关系,逐个求解。下面介绍一些动态规划算法的基本概念。
(1)阶段:阶段是整个过程的自然划分,通常按时间顺序或空间特征划分阶段,表示阶段序号的变量称为阶段变量,一般用字母kkk表示。
(2)状态:在整个过程中,每个阶段开始所处的自然状况或客观条件称为状态,是不可控因素。状态变量常用sks_{k}sk​表示。
(3)决策:一个阶段的状态确定后,可以作出不同的选择,从而演变到下一阶段的某个状态,这种选择叫做决策,常用uku_{k}uk​表示。
(4)策略:由决策组成的序列称为策略。
(5)状态转移方程:给定第k阶段的状态和决策,则第k+1k+1k+1阶段的状态由状态转移方程决定。
(6)指标函数:指标函数是衡量过程优劣的数量指标,它是定义在全过程和所有后部子过程上的数量函数,对于给定的状态,指标函数值 随策略改变,采用不同的策略可以得出不同的指标函数值。
(7)最优策略和最优轨线:使指标函数达到最优值(最大值或最小值)的策略称为最优策略,按最优策略和状态转移方程得出的状态序列为最优轨线。

2.2基于动态规划的立体匹配

  动态规划立体匹配是建立在极线约束的基础上的,其大致思想就是在每条对极线上寻找一条最优的匹配路径,使得全局能量函数在每条对极线上达到最低。在经过立体校正以后,对极线则转变为左右视图每一对应的水平行。

在沿着对极线进行动态规划时通常要遵循以下几个约束:
 (1) 视差范围约束:即视差的搜索范围是有限的,不能在一个无限大的区间内去搜索视差;
 (2) 唯一性约束:给定左图中的一个像素点,它在右图中最多只能有一个对应点;
 (3) 可见性约束:当某点处于遮挡区域时,它在另一幅图中将无法找到对应点,遮挡区域一般出现在由背景进入前景或者由前景进入背景的边界地带;
 (4) 顺序性约束:即假设在左图中的两个点的横坐标为xl1,xl2x^{1}_{l}, x^{2}_{l}xl1​,xl2​,在右图中的对应点横坐标为xr1,xr2x^{1}_{r}, x^{2}_{r}xr1​,xr2​,若xl1<xl2x^{1}_{l}<x^{2}_{l}xl1​<xl2​,则xr1<xr2x^{1}_{r}<x^{2}_{r}xr1​<xr2​;
 (5) 连续性约束或平滑性约束:由于现实世界物体是连续的,因此在图像中的视差也应当是均匀平滑变化的,但是在物体的边缘处视差是有可能发生大的跳变。

  能量函数的一般定义如下:
E(d)=E(data)+E(smooth)E(d) = E(data) + E(smooth)E(d)=E(data)+E(smooth)
其中 E(data)为图像数据项,用于判断匹配像素点之间的相似性, E(smooth)为像素点与邻域像素的平滑约束项, 用于保证视差的连续性。使用不同的能量函数优化得到的最终结果也会不同,因此构造能量函数也是十分重要的。在论文中,作者提出了一个具体的能量函数:

其中第一项为所有像素在视差图D下的匹配代价之和,第二项为当像素p和邻域像素q的视差差异为1时添加的常数惩罚值P1,第三项则添加了更大的惩罚值P2用于更大的视差差异。小的惩罚值能够适应倾斜表面和曲面,大的惩罚值能够适应深度不连续区域,由于深度不连续性一般出现在灰度发生跳变的地方,因此P2一般根据灰度梯度自适应的决定:

为了方便求解,不考虑顺序性约束和可见性约束,则求解过程如下:

按照上述过程,只要依次求解每个阶段的最优值minfk(d)min f_{k}(d)minfk​(d),该行像素的视差也就求出来了。从这个过程中可以发现,该计算流程实际上就是接下来所要讲解的SGM在水平方向上的代价聚合。

3.代价聚合

  全局立体匹配的过程可以认为是通过最小化全局能量函数来求得视差图,然而不幸的是,最小化这样一个2D全局能量函数是一个NP hard问题。在上一小节中介绍了在一维对极线上使用动态规划进行立体匹配的方法,该方法可以在多项式时间内完成能量最小化过程,然而算法最终得到的视差图中却会有明显的水平条纹,这是因为一维动态规划仅考虑了对极线上的约束,而对极线之间的约束却没有考虑。一些改进算法如行列双通道动态规划、基于垂直一致性加强约束的动态规划、基于树结构的动态规划可以有效改善这种问题,但这些算法都大大增加了运算复杂度。以上这些想法推动Hirschmuller提出了semi-global matching方法,如下图所示,其思想是通过在多个方向1维路径上平等地进行代价聚合,然后使用WTA求解视差,来作为一个近似求解2维能量最小化的过程。

代价聚合按照如下公式递归计算:

其中rrr代表一条路径,p−rp-rp−r代表沿着该路径上位于p前面的像素,代价C(p,d)C(p,d)C(p,d)可以为BT代价或者MI代价等。可以看出这种方法其实更加接近scanline optimization而非传统的动态规划。按照上述公式进行代价聚合,聚合值沿着路径会不断增加,最终将得到一个极大的数值,所以Hirschmuller建议采用如下改进的代价聚合公式:

这个改进并不影响最后的计算结果,因为被减去的那项在像素p的不同视差下都对应一个常数,然而聚合代价的上限将会变为:L<Cmax+P2L<C_{max}+P_{2}L<Cmax​+P2​。代价聚合的路径可以为8或16个,这样可以全面覆盖整张图像,最终的聚合代价是所有路径上聚合代价的和:

于是代价聚合的上限则变为:L<16∗(Cmax+P2)L<16*(C_{max}+P_{2})L<16∗(Cmax​+P2​),可以看到代价聚合的复杂度为O(NWHD)O(NWHD)O(NWHD),其中N为代价聚合的路径数,W和H为图像的宽、高,D为视差搜索范围。由于代价聚合公式结构规整,操作简单,仅仅涉及了加减法和求最小,所以可以很容易的使用SIMD指令实现并行操作。

4.视差计算

经过代价聚合后,可以采用winner takes all方法来计算视差:

对于亚像素视差估计,可以通过抛物线拟合来求解。

5.多基线匹配

略,后续补充。

6.视差细化—后处理

初步计算出的视差不可避免地包含一些错误,因此需要采用一些方法去除或修正其中的错误视差。

6.1peak filter

  噪声、过饱和、大面积无纹理区域容易导致错误匹配,在生成的视差图中经常以块状聚集。对图像进行连通域分割,比如4连通或者8连通,然后通过一个预定义的尺寸阈值来判断这块连通域是否为噪声,若是则将其剔除。这种方法十分高效,可以在O(WH)步骤内完成。

6.2灰度一致性视差选择

略,后续补充

6.3无效视差值填充

  无效视差被分成两类:occlusions(遮挡)和mismatches(误匹配),两种情形下的视差插值需要进行不同的处理,occlusions在邻域像素的视差中选择最小值,因为occlusions一般处于背景当中。mismatches一般取邻域像素的中值:

下面是Teddy图像的各个视差后处理的效果展示:

7.视差融合

三、OpenCV中的SGBM

​  在OpenCV中库中已经实现了SGM算法,叫做semi-global block matching,即SGBM,分为预处理、代价计算、代价聚合、后处理这四个步骤来实现。opencv在其中增加了一些独特的处理方法,下面分别说明一下各个步骤。
(1) 预处理
  SGBM 算法首先利用水平 Sobel 算子,对图像进行预处理,其处理方法如下式所示:

利用该方法,也就是水平 Sobel 算子对图片的所有的像素点进行处理过后,会变成一个崭新的图片(P的数值代表像素的像素值): PNEWP_{NEW}PNEW​ 表示新图像上的像素值。映射函数公式如下式:

(2) 代价计算
  代价由两部分组成:一是对图像经过预处理后的梯度图计算BT代价,二是对没经过预处理的原图像计算BT代价,然后将两者简单相加,最后将得到的代价立方体在矩形窗口内进行求和:

(3) 代价聚合
即SGM中代价聚合的那一步。

(4) 后处理

A.唯一性检测
​  也就是常见的比率测试匹配筛选方法,即次低代价不少于最低代价的(1+ uniquenessRatio/100)倍的时候时,最低代价对应的视差值才认为是可靠的视差值,否则这个像素点的视差值就被设置为一个无效的负值。其中 uniquenessRatio 是一个常数参数,一般设置为5到15之间,显然,值越大则筛选条件越苛刻,无效视差也就越多。

B.亚像素插值
插值公式如下:


C.左右一致性检测
  设左图中像素ppp的视差为ddd,右图中对应像素的视差为d∗d^{*}d∗,那么一般认为∣d−d∗∣<threshold|d-d^{*}|<threshold∣d−d∗∣<threshold时,ddd才最有可能为正确的视差,否则将其置为一个无效值,即负值,threshold一般取为1或者2。

D.连通域噪声滤波

  连通域噪声滤波也就是作者在论文中所讲述的peak filter,这里就不重复叙述了。peak filter可以有效剔除噪声块,但是也很容易剔除正确的视差,使得整张视差图空洞比较多。

四、代码实现

下面提供C++代码,python代码可以参考博客:双目测距理论及其python实现!

#include <opencv2/opencv.hpp>
using namespace cv;int main(int argc, const char** argv)
{Mat left = imread("path/to/left.png", -1);Mat right = imread("path/to/right.png", -1);if(left.empty() || right.empty()) {printf("error:inputs are empty!please check the image path!")return -1;}assert(left.size == right.size);int cn = left.channels();Ptr<StereoSGBM> matcher = StereoSGBM::create(0, 128, 3, 8 * cn * 3 * 3, 32 * cn * 3 * 3, 2, 63, 15, 100, 1,  cv::StereoSGBM::MODE_SGBM_3WAY);Mat disp;matcher->compute(left, right, disp);return 0;
}

可以看到代码比较少。下面简单解释一下StereoSGBM::create()函数中的最后一个参数mode,它的可选值有四个,如下所示:

enum{MODE_SGBM = 0, //左->右、上->下、左上->右下、右上->左下(共4条路径)MODE_HH   = 1,  //左<->右、上<->下、左上<->右下、左下<->右上(共8条路径)MODE_SGBM_3WAY = 2,  //左->右、右->左、上->下(共3条路径)MODE_HH4  = 3  //左<->右、上<->下(共4条路径)};

这四个值代表SGM中代价聚合部分所使用的聚合路径的情况,可以看到MODE_SGBM_3WAY只在三个路径上进行了代价聚合,因此速度最快,但实际效果可能较差,而MODE_HH在8条路径上进行了代价聚合,因此速度最慢,但最终效果最好,具体使用哪一种,在于自己的取舍。在原作者的文章中,Hirschmuller提出可以使用16个方向的路径进行代价聚合,但是SGBM最多提供了8个路径的代价聚合,主要原因在于16路径并没有比8路径具有太大的优势,反而还成倍地增加了计算量,因此使用16路径是极不划算的。

五、效果图



六、总结

  1. SGM算法由于具有很高的匹配精度,并且可以从数据层面和多线程层面高度并行化处理,因此可以达到比较高的速度,使用GPU或FPGA甚至能够达到实时,因此SGM是至今最受欢迎的立体匹配算法,特别在商业应用中应该是使用最多的算法了。以SGM为核心的匹配算法也非常多,除了opencv中的SGBM外,还有ADCensus、wSGM、rSGM、iSGM、MGM、tMGM-16、PMSGM、SGM-Forest、SGM-Net、GA-Net等等,有兴趣的可以查阅相关论文阅读。
  2. OpenCV在SGBM算法中加入了多种后处理,但全部处理都聚焦在如何剔除错误视差,而如何填补除错误视差后视差图上形成的空洞却没有相关的算法实现,而这一步对于后续的三维重建还是比较重要的,目前比较热门的研究领域是基于深度学习对初始视差图进行refine,其中包括深度填充、高分辨率细节恢复等。
  3. SGM算法的效果还是相当不错的,然而在实际应用当中,SGM对于图像噪声、光照不均、大面积无纹理等各种情况依然不能很好的处理,真正的视差效果往往没有middleburry中的数据那样好,比如下图:

    还有的情况,SGBM算法几乎大面积失败了:

    如何处理实际场景中的图像特别是室外图像,需要有针对性的考虑,比如代价计算部分可以考虑对光照变化容忍性较强的NCC或census变换、代价融合(ADCensus)以及基于深度学习的方法(MC-CNN)等

双目立体匹配算法:SGM相关推荐

  1. 双目立体匹配算法SGM步骤拆解

    立体匹配是立体视觉研究中的关键部分,其目标是在两个或多个视点中匹配相应像素点,计算视差.双目摄像头类似人眼的工作原理,对同一目标可以形成视差,用来感知三维世界,由于成本远低于激光雷达,因此在自动驾驶领 ...

  2. 双目立体匹配算法漫谈

    双目立体匹配算法漫谈 双目立体匹配算法漫谈 前提 一些基本假设 框架 matching cost computation cost (support) aggregation;代价聚合 双目立体匹配算 ...

  3. 双目立体匹配算法:Patch Match Stereo实用详解教程

    来源:CSDN 作者:dulingwen 01 简介 我们知道,现有立体匹配算法一般被分类为局部算法.全局算法和半全局算法,其中局部算法和半全局算法是应用最为广泛的.在局部算法中,一个最简单的做法就是 ...

  4. 一文读懂经典双目稠密匹配算法SGM

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 本文由知乎作者David LEE授权转载,不得擅自二次转载. 原文链接:https://zhuanla ...

  5. 双目立体匹配算法SGBM

    semi-global matching(SGM)是一种用于计算双目视觉中视差(disparity)的半全局匹配算法,在OpenCV中的实现为semi-global block matching(SG ...

  6. 双目立体视觉之立体匹配算法

    一.立体匹配简介: 双目立体视觉是指使用两个摄像机从不同的角度获取同一个场景的左右视图,然后使用双目立体匹配算法来寻找左右视图中的匹配像素点对,最后利用三角测量原理来还原三维空间物理点过程.其中双目立 ...

  7. 基于python的AD-census立体匹配算法实现

    文章目录 前言 一.AD-census是什么? 1.代价计算 2.代价聚合 3.视差优化 4.视差后处理 二.基于python的AD-census立体匹配算法实现 前言   AD-Census算法来自 ...

  8. 基于深度学习算法和传统立体匹配算法的双目立体视觉

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 01 立体视觉是什么? 在开始之前,我相信很多站友都会有这个疑问, ...

  9. [双目视差] 立体匹配算法推理 - SGBM算法(一)

    文章目录 立体匹配算法推理 - SGBM算法(一) 一.SGBM与SGM的区别 二.代价计算 立体匹配算法推理 - SGBM算法(一) SGBM立体匹配算法,总体来讲包含以下6个步骤: Preproc ...

最新文章

  1. 华为鸿蒙智慧屏怎么样,65寸华为智慧屏怎么样?4000元选哪个好?
  2. tortoiseHG不用每次输入密码
  3. 简化PHP开发的10个工具
  4. 数据中心水冷系统一次泵与二次泵的选择
  5. Python中安装模块的方法
  6. R语言——导入Excel表格数据方法
  7. SpringCloud之RestTemplate,几种常见的请求方式
  8. 黑马C++设计模式2
  9. Coinlist将在4月1日到3日举行Rally(RLY)代币销售
  10. JavaScript之基础-9 JavaScript String(内置对象、String概述、字符串常用操作、模式匹配)...
  11. C++对结构体按照某一项元素进行排序
  12. 实习成长之路——设计模式三:组合与继承有啥关系?为什么说多用组合少用继承?如何选择使用哪种方式?
  13. 产品沉思录 #Vol.20200315:交易平台研究
  14. 米思齐(Mixly for Mac)官方版下载过程以及遇到的问题/解决方法
  15. excel与python生成正态分布的数据,实践
  16. vue + ElementUI + BMap 百度地图实现地图选址定位并获取地址信息
  17. 新能源汽车智能化,集度先行一步
  18. 白塞尔公式_如何设计像乌塞尔这样的800人的婚礼
  19. httprunner3.x使用过程中遇到的问题
  20. 什么是 5G CPE

热门文章

  1. CS50_AI_1_Konwledge 推理 (Python实现-中文注释)
  2. Word排版---最大限度的节约打印纸张
  3. Codeup之那些曾经的非主流
  4. 视频操纵中的新AI技术转向
  5. oracle批量插入优化,oracle批量插入优化方案
  6. Stata16安装包下载及安装教程
  7. VM704振弦采集模块工程监测振弦采集仪器设备开发
  8. 为什么php工作难找?
  9. Qualcomm usb modem驱动小结
  10. java游戏倒计时代码怎么用,Java实现倒计时代码