双目立体匹配算法:ELAS

一、简介

        ELAS是由Andreas Geiger于2010年提出的一种比较新颖的立体匹配算法,全称为《Efficient Large Scale Stereo Matching》。这种算法的优点是对于高分辨率的图像,能够兼顾较高的匹配速度和匹配精度,在许多公开的数据集中ELAS的平均匹配精度十分接近SGM,但是速度却比SGM更快。ELAS算法并没有遵循典型的代价计算、代价聚合、视差优化、视差细化的过程,而是将立体匹配过程建模成一个贝叶斯推理的问题,即通过最大后验概率估计方法推理视差。该算法利用图像中一些稀疏的能够鲁棒匹配的支持点作为先验,并将这些支持点进行Delaunay三角化,从而可以降低剩余像素点的匹配歧义,不需要进行全局优化。该算法可以自动决定每一个像素点的视差搜索范围,并且很容易实现并行操作。仅使用单核CPU的话,ELAS算法对于一百万像素分辨率的图像进行匹配需要仅仅大概1s左右的时间。

ELAS算法的基本流程是:

(1) 对左右图像进行sobel滤波,生成sobel特征图;

(2) 基于sobel特征进行匹配,筛选出其中具有较高置信度的匹配点,作为"support points",即支持点;

(3) 对支持点构建Delaunay三角网格;

(4) 构建立体匹配的概率生成模型;

(5) 基于最大后验概率密度估计(MAP)进行稠密立体匹配,获取初始视差图;

(6) 视差后处理:无效孔洞插值、(自适应)中值滤波、连通域噪声剔除、左右一致性检验。

二、计算支持点

       在计算支持点之前,需要设置一个全局可靠的视差搜索范围:[disp_{min}, disp_{max}], 一般最小视差设置为0,最大视差需要根据所使用的场景来调整。首先对左图像采用3\times 3sobel算子进行滤波,获取每一个像素点的水平和垂直响应值G_{x}G_{y}。然后对每一个像素,从其周围5\times 5邻域中的水平方向响应值选取12个,垂直方向响应值选取4个,构成一个16维的特征向量:            

特征匹配时,对左图中的每一个像素,使用右图中对应的每一个像素5\times 5邻域中的四个角的特征向量\mathbf{f}^{r}(u-d+2, v+2)\mathbf{f}^{r}(u-d+2, v-2)\mathbf{f}^{r}(u-d-2, v+2)\mathbf{f}^{r}(u-d-2, v-2)d\in [disp_{min}, disp_{max}],进行匹配,匹配对的相似性度量值的计算方式如下:

                                                  E(f)=\sum \left | \mathbf{f}^{l}(u,v)-\mathbf{f}^{r}(x-d,y) \right |,x=u\pm 2,y=v\pm 2                                          

从中选取相似度最高的像素作为匹配对(同样的方法可以获取右图所有像素的匹配),然后采用ratio test对匹配进行筛选,即最低匹配值和次低匹配值的比值小于设定阈值时才认为是正确的匹配。为了进一步保证匹配的置信度,采用左右一致性检验和支持点周围一定邻域内其它支持点数量的判断(数量太少,认为该支持点不可靠),继续筛选剩余匹配。经过以上几个步骤就可以得到所有的支持点了。

       作者还指出sobel滤波对于特征匹配是十分高效的,虽然有很多可以进行鲁棒匹配的描述子,比如SIFT、SURF等,但是他们对于整个立体匹配的精度并没有带来提升,而且还会拖累运行速度。

三、用于立体匹配的概率生成模型

(1)基本定义

S=\left ( \mathbf{s}_{1},\mathbf{s}_{2},......\mathbf{s}_{M} \right )表示支持点的集合,其中每一个支持点表示成\mathbf{s}_{m}=(u_{m},v_{m},d_{m})^{}T,其中d_{m}(u_{m}, v_{m})点对应的视差。令O=\left ( \mathbf{o}_{1},\mathbf{o}_{2},......\mathbf{o}_{N} \right )表示观测点的集合,每一个观测点表示成\mathbf{o}_{n}=(u_{n},v_{n},\mathbf{f}_{n})^{}T,其中\mathbf{f}_{_{n}}为特征向量。令\mathbf{o}^{(l)}_{n}\mathbf{o}^{(r)}_{n}表示左右图像中对应像素点。不失一般性,将左图作为参考图。

我们把左图中的某个点\mathbf{o}^{(l)}_{n}的视差d_{n}视为待求解的随机变量,假定在给定视差d_{n}的条件下,观测量\left \{\mathbf{o}^{(l)}_{n}, \mathbf{o}^{(r)}_{n} \right \}和支持点集S是条件独立的,则他们的联合分布可以被分解为:

其中p(d_{n}|S, \mathbf{o}^{(l)}_{n})称为先验概率,p(\mathbf{o}^{(r)}_{n}|\mathbf{o}^{(l)}_{n},d_{n})称为图像似然概率。

该联合分布又叫做概率生成模型。根据概率生成模型,给定参考图像和支持点集,可以从另外一副图像中进行像素采样。

(2)先验概率建模

作者认为先验概率与均匀分布和高斯分布的组合成比例:

其中\mu (S, \mathbf{o}^{(l)}_{n})为观测点基于周围支持点的平均值,Ns为观测点(u_{n}^{(l)},v_{n}^{(l)})周围20\times 20邻域内所有支持点的视差值所构成的取值范围。为了进一步缩小取值范围,所有与均值之差大于3\sigma的视差都被排除了。\mu (S, \mathbf{o}^{(l)}_{n})表示为一个分段线性函数,可以通过基于支持点构建的delaunay三角形来进行视差插值得到。对每一个三角形,其所在的视差平面定义如下:

其中i是(u_{n}^{(l)},v_{n}^{(l)})所属三角形的索引,对每一个三角形,平面参数(a_{i},b_{i},c_{i})可以由三角形的三个顶点通过求解线性方程求得。

(3)似然概率建模

作者认为图像似然概率可以表示成一个拉普拉斯分布:

其中\mathbf{f}_{n}^{(l)} 和 \mathbf{f}_{n}^{(r)}为特征向量,\beta是一个常数。if条件保证似然概率约束在左右图像的同一行上。特征向量由观测点周围5\times 5邻域内像素的sobel响应值拼接而成,故其维数为2\times 5\times 5=50(实际使用的特征向量依然按照上一节所引入的16维特征向量)。作者在文中特别指出基于sobel滤波的特征性能明显好于基于LoG滤波的特征。

(4)基于概率生成模型的采样

概率生成模型的优点是给定支持点集合和左图参考点,可以从右图采样对应的像素点,方法如下:

    1)给定S和\mathbf{o}^{(l)}_{n},可以根据先验概率分布p(d_{n}|S, \mathbf{o}^{(l)}_{n})随机采样视差d_{n}

    2)给定d_{n}\mathbf{o}^{(l)}_{n},可以根据似然概率分布p(\mathbf{o}^{(r)}_{n}|\mathbf{o}^{(l)}_{n},d_{n})得到右图对应点\mathbf{o}^{(r)}_{n}

如下图所示是采样过程示意图:

       为了更好的理解这个过程,下图展示了使用灰度值作为特征,对左图每一个像素重复上述过程采样一百次,取平均之后得到一个新图,从图中可以看到,正如所预料的那样,新图相当于一个模糊版本的右图。

四、视差计算 

       我们的目的是使用贝叶斯推理的方式,在给定支持点集和右视图—即观测量—的情况下求解左视差图的后验概率密度,然后通过最大化后验概率密度(MAP)的方法来估计视差:

其中\mathbf{o}^{(r)}_{1}\mathbf{o}^{(r)}_{2},......,\mathbf{o}^{(r)}_{N}只考虑右图中位于\mathbf{o}^{(l)}_{n}的对极线上的观测点。根据贝叶斯法则,后验概率可以被分解为先验概率和似然概率的乘积,两者只差一个归一化系数:

其中第一部分是先验概率,第二部分是似然概率,将上一节得到的先验概率和似然概率带入上式可以得到后验概率。由于最大化后验概率等价于最小化后验概率的负对数,因而得到下列可用于最小化的能量函数:

能量函数的第一部分表示似然项,第二部分为先验项或平滑项。通过最小化该能量函数,可以得到观测点\mathbf{o}^{(l)}_{n}的视差。出于编程的考虑,实际当中应用的能量函数为:

其目的是可以预计算先验项。最小化能量函数时,将视差分成了两个部分:(1)基于先验分布,仅考虑观测点经插值后的平均视差的局部范围,即|d-\mu |<sradius*\sigma, sradius一般取2或3;(2)基于像素周围20\times 20邻域内的所有支撑点的视差构成的视差搜索范围(如下图所示),这部分视差在最小化能量函数时不考虑先验项。(Note:最小化搜索过程中考虑了纹理,对于太弱的纹理直接跳过计算视差)

同样的方法可以计算右图的视差图。

五、后处理

后处理部分采用无效区域孔洞插值、左右一致性检验、连通域噪声剔除、中值滤波、自适应中值滤波。

1.左右一致性检测

       设左图像素点(u,v)的视差为d^{(l)},则右图的对应点(u-d^{(l)},v)的视差为d^{(r)},若|d^{(l)} - d^{(r)}|<\tau,则认为视差d^{(l)}是可靠的,否则将其置为-1或其他无效值。其中\tau一般取1或2,可适当调整。

2.孔洞插值

       在对立体匹配的过程中,有多种情况会使得视差图中某点的像素被置为无效值,如纹理不足、左右一致性检验等。对于这些点处的视差往往需要通过插值给予其一个合理的视差值。首先考虑水平方向,若某个无效区域的最左侧和最右侧的视差值为d_{1}d_{2},考虑深度突变,则其中所有点的视差的插值公式如下:

其中\eta表示深度不连续的阈值。垂直方向也以同样的方法进行一次插值。

3.中值滤波与加权均值滤波

       中值滤波主要用于消除视差图中的孤立噪点。加权均值滤波对当前像素的视差按邻域内进行加权求均值。

4.连通域噪声剔除

       视差图中总是包含着错误的视差,由于弱纹理、反射、噪声等诸多因素,这些错误的视差往往成聚集成小的块状区域,使用该方法可以有效去除视差图中块状噪声区域。给定连通域的灰度阈值和尺寸阈值,通过检测连通域的尺寸是否满足尺寸阈值条件,来判断该连通域是否为噪声。若为噪声,将该连通域中的所有视差置为无效值。

六、代码实现

作者已经开源了C++源代码,并提供了Matla接口,下载地址为Download。源码如下:

下载源码后,导入visual studio工程,编写main函数(代码使用了第三方库opencv来读取图片):

#include <iostream>
#include <time.h>
#include "elas.h"
#include "image.h"
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
    string path1 = "C:/Users/thinkpad/Desktop/left/000003.png";
	string path2 = "C:/Users/thinkpad/Desktop/right/000003.png";

	//读取左右视图  
	Mat left = imread(path1, -1);
	Mat right = imread(path2, -1);

	if (left.size != right.size) {
		cerr << "左右视图必须拥有相同的尺寸" << endl;
		return -1;
	}

	if (left.channels() == 3)
		cvtColor(left, left, COLOR_RGB2GRAY);
	if (right.channels() == 3)
		cvtColor(right, right, COLOR_RGB2GRAY);

	int width = left.cols;
	int height = left.rows;
	int dim[3] = { width, height, width };

	Mat disp_left = Mat::zeros(Size(width, height), CV_32FC1);
	Mat disp_right = Mat::zeros(Size(width, height), CV_32FC1);

	// 参数设置	
	Elas::parameters param;
	param.disp_min = 0;                                     // 最小视差	
	param.disp_max = 256;                                // 最大视差	
	param.support_threshold = 0.85;              // 比率测试:最低match VS 次低match
	param.support_texture = 10;                     // 支持点的最小纹理
	param.candidate_stepsize = 5;                  // 用于支持点的sobel特征匹配的邻域半径
	param.incon_window_size = 5;                  // 不连续性窗口的尺寸
	param.incon_threshold = 5;                       // 不连续性窗口内的视差范围阈值
	param.incon_min_support = 5;                 // 不连续性窗口内的最低支持点数量
	param.add_corners = true;                        // 是否添加角点
	param.grid_size = 20;                                  // 网格尺寸
	param.beta = 0.02;                                      // 图像相似性度量的参数
	param.gamma = 3;                                      // 先验概率常数
	param.sigma = 1;                                         // 先验概率的标准差
	param.sradius = 3;                                       // 标准差半径
	param.match_texture = 1;                         // 最低纹理
	param.lr_threshold = 1;                             // 左右一致性检验阈值
	param.speckle_sim_threshold = 1;          // 连通域判断阈值
	param.speckle_size = 200;                        // 连通域噪声尺寸判断阈值
	param.ipol_gap_width = 3;                       // 空洞宽
	param.filter_median = false;                     // 是否中值滤波
	param.filter_adaptive_mean = true;        // 是否自适应中值滤波
	param.postprocess_only_left = true;     // 是否只对左视差图后处理,设置为True可以节省时间
	param.subsampling = false;                     // 每个两个像素进行视差计算,设置为True可以节省时间,但是传入的D1和D2的分辨率必须为(w/2) x (h/2)

	clock_t start = clock();
	Elas elas(param);
	elas.process(left.data, right.data, disp_left.ptr<float>(0), disp_right.ptr<float>(0), dim);
	clock_t end = clock();
	cout << "running time:" << (double)(1000 * (end - start) / CLOCKS_PER_SEC) << endl;

	disp_left = disp_left * 3;
	imwrite("C:/Users/thinkpad/Desktop/disparity.jpg", disp_left);


}

七、实验效果

这里随机选择了KITTI双目数据集中的几张图片,下面是左视图及使用ELAS算法生成的对应的视差图:

八、总结

  1. ELAS算法的最大优点就是匹配速度快的同时又有较高的匹配精度,这是因为它通过鲁棒匹配的支持点作为先验,降低了剩余像素点视差的搜索范围。所有视差平面实际上代表了场景的几何结构,因此位于某视差平面内的点的视差必定与附近的支持点具有相近的视差。
  2. 获取支撑点的过程实际上就是传统的不包含代价聚合的局部立体匹配算法,这一步骤耗时较短,所以ELAS算法的运行时间下限不会比最快的局部匹配算法更少。
  3. 先验概率和似然概率的建模似乎还有可以改造的空间。
  4. 虽然在公开数据集中ELAS匹配都能够取得很好的效果。但是对于实际场景的图像特别是室外图像,当传感器噪声较高、存在大面积无纹理区域时,ELAS算法依然不能处理的很好,原因在于在这种情况下sobel算子从图像中不能获得足够多的精确支持点,后续的匹配性能就会大打折扣。
  5. 到目前为止,关于ELAS算法的变种不是太多,其中较好的改进有LS-ELAS。

各位觉得不错的一定要点个赞哦!!!!!!

<think>好的,我现在需要帮助用户了解或实现传统的双目立体匹配算法。首先,我得回顾一下用户提供的引用内容,确保信息准确。用户引用了四个资料,其中提到了双目立体视觉的基本流程、传统算法与深度学习的结合、ELAS算法以及动态规划方法。用户的问题是关于传统算法的原理与实现,所以重点应该放在传统方法上,而不是深度学习。 首先,我需要明确双目立体匹配的基本原理。根据引用[1]和[2],双目立体视觉通过左右视图的匹配像素点对,利用三角测量计算深度。核心在于立体匹配算法。传统算法通常分为四个步骤:匹配代价计算、代价聚合、视差计算和视差优化。这些步骤在引用[3]和[4]中也有提及,比如ELAS算法虽然不同,但提到了代价计算和视差优化等概念。 接下来,用户需要知道传统算法的原理和实现步骤。我需要分步骤解释,并确保每个步骤都有对应的说明。例如,匹配代价计算常用方法有SAD、SSD、NCC等,这些在传统算法中是基础。然后,代价聚合可能使用局部方法(如窗口求和)或全局方法(如动态规划、图割)。视差计算通常通过WTA(赢者通吃)策略,而视差优化则包括左右一致性检查、亚像素细化等。 然后,用户可能需要一个具体的例子来理解如何实现。根据引用[4],动态规划方法是在对极线上寻找最优路径,所以可以考虑以动态规划为例,给出一个简单的代码框架。不过,用户提供的引用中ELAS算法更高效,但属于较新的方法,可能不属于传统范畴。因此,选择动态规划或SGM(半全局匹配)作为例子可能更合适,但SGM可能复杂度较高,动态规划更基础。 同时,用户的问题可能涉及如何选择算法,以及实现的难点在哪里。比如,传统算法在纹理缺失区域、重复纹理等情况下表现不佳,这些需要处理。另外,代码示例可能需要使用Python,但实际中传统算法可能用C++更高效,不过用户可能需要Python示例。所以给出一个简化的SAD加动态规划的伪代码或示例可能有助于理解。 最后,根据用户的要求,生成相关问题。这些问题应该围绕传统算法的步骤、优缺点、改进方法、与其他算法的比较等展开。例如,如何解决遮挡问题,或者传统算法与深度学习的对比。 现在需要确保回答结构清晰,分步骤说明原理,并给出实现步骤和示例代码,同时引用提供的资料。比如,在代价聚合部分提到动态规划,可以引用[4]。在提到ELAS算法时,虽然它属于传统改进,但用户可能想了解传统方法,所以需要明确区分。 需要检查是否有遗漏的关键点,比如相机标定和极线校正的重要性,这在引用[2]中提到,是预处理步骤的一部分。因此在实现步骤前,必须进行图像校正,使对应点位于同一行,如引用[4]所述。 可能还需要提到常用的算法如BM(块匹配)、SGBM(半全局块匹配),这些在OpenCV中有实现,方便用户参考。但用户可能希望自己实现,所以需要解释基本原理。 总结,回答应包括: 1. 双目立体匹配的基本原理,包括四个步骤。 2. 各步骤的详细说明,结合引用内容。 3. 实现步骤,包括预处理、算法核心。 4. 代码示例,可能是一个简化的动态规划或SAD方法。 5. 算法优缺点和应用场景。 6. 相关问题。 需要注意使用正确的中文,数学公式用$$...$$,行内用$...$,确保LaTeX正确。例如,视差d与深度Z的关系公式需要正确呈现。 现在开始组织内容,确保每个部分引用正确的资料,并在末尾添加相关问题。</think>### 传统双目立体匹配算法原理与实现 #### 一、双目立体匹配基本原理 双目立体匹配的目标是从左右视图的对应像素点中计算视差(disparity)$d$,进而通过三角测量公式计算深度$Z$: $$ Z = \frac{f \cdot B}{d} $$ 其中$f$为焦距,$B$为基线距离(两摄像机间距)[^1]。 #### 二、传统算法核心步骤 1. **匹配代价计算** 计算左右图像中像素的相似性。常用方法: - **SAD(绝对差值和)**:$C_{\text{SAD}}(x,y,d) = \sum_{(i,j)\in W} |I_L(x+i,y+j) - I_R(x+i-d,y+j)|$ - **SSD(平方差和)**:$C_{\text{SSD}}(x,y,d) = \sum_{(i,j)\in W} (I_L(x+i,y+j) - I_R(x+i-d,y+j))^2$ - **NCC(归一化互相关)**:$C_{\text{NCC}}(x,y,d) = \frac{\sum (I_L - \mu_L)(I_R - \mu_R)}{\sqrt{\sum (I_L - \mu_L)^2 \sum (I_R - \mu_R)^2}}$[^2] 2. **代价聚合** 通过局部或全局方法优化代价矩阵: - **局部方法**:对窗口内代价求和(如均值滤波)。 - **全局方法**:动态规划(在极线约束下寻找最优路径)[^4]、图割(最小化能量函数)。 3. **视差计算** 使用**赢者通吃(WTA)**策略选择最小代价对应的视差: $$ d_{\text{best}} = \arg\min_d C(x,y,d) $$ 4. **视差优化** - **左右一致性检查**:剔除遮挡区域。 - **亚像素细化**:二次插值提升精度。 - **中值滤波**:平滑噪声。 #### 三、实现步骤与代码示例 1. **预处理** - 相机标定与极线校正,使匹配点位于同一水平行。 2. **动态规划实现(简化版)** ```python import numpy as np def compute_disparity(left_img, right_img, max_d=64): h, w = left_img.shape disparity = np.zeros((h, w)) for y in range(h): # 初始化代价矩阵和路径表 cost = np.zeros((w, max_d)) path = np.zeros((w, max_d), dtype=int) for x in range(w): for d in range(max_d): if x - d >= 0: cost[x, d] = abs(int(left_img[y, x]) - int(right_img[y, x - d])) else: cost[x, d] = float('inf') # 动态规划递推 for x in range(1, w): for d in range(max_d): min_prev = min(cost[x-1, max(0, d-1):min(max_d, d+2)]) cost[x, d] += min_prev path[x, d] = np.argmin(cost[x-1, max(0, d-1):min(max_d, d+2)]) # 回溯最优路径 best_d = np.argmin(cost[-1, :]) for x in reversed(range(w)): disparity[y, x] = best_d if x > 0: best_d = path[x, best_d] return disparity ``` #### 四、优缺点与应用 - **优点**:计算可控,硬件资源需求低。 - **缺点**:对纹理缺失、重复纹理敏感,依赖极线校正精度。 - **应用**:工业检测(零件尺寸测量)、机器人导航[^3]。
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值