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) 视差后处理:无效孔洞插值、(自适应)中值滤波、连通域噪声剔除、左右一致性检验。
在计算支持点之前,需要设置一个全局可靠的视差搜索范围:, 一般最小视差设置为0,最大视差需要根据所使用的场景来调整。首先对左图像采用的sobel算子进行滤波,获取每一个像素点的水平和垂直响应值和。然后对每一个像素,从其周围邻域中的水平方向响应值选取12个,垂直方向响应值选取4个,构成一个16维的特征向量:
特征匹配时,对左图中的每一个像素,使用右图中对应的每一个像素邻域中的四个角的特征向量、、、, ,进行匹配,匹配对的相似性度量值的计算方式如下:
从中选取相似度最高的像素作为匹配对(同样的方法可以获取右图所有像素的匹配),然后采用ratio test对匹配进行筛选,即最低匹配值和次低匹配值的比值小于设定阈值时才认为是正确的匹配。为了进一步保证匹配的置信度,采用左右一致性检验和支持点周围一定邻域内其它支持点数量的判断(数量太少,认为该支持点不可靠),继续筛选剩余匹配。经过以上几个步骤就可以得到所有的支持点了。
作者还指出sobel滤波对于特征匹配是十分高效的,虽然有很多可以进行鲁棒匹配的描述子,比如SIFT、SURF等,但是他们对于整个立体匹配的精度并没有带来提升,而且还会拖累运行速度。
所谓概率生成模型就是将视差和左右图的像素看作随机变量,根据贝叶斯理论对视差的概率分布进行建模,根据概率生成模型,给定参考图像和支持点,可以从另外一副图像中进行采样。令表示支持点的集合,每一个支持点,令表示观测点的集合,每一个观测点,其中为特征向量。令
和表示左右图像中对应像素点。不失一般性,将左图作为参考图。在给定视差的条件下,观测对和支持点集S是条件独立的,他们的联合分布可以被分解为:
其中称为先验概率,称为似然概率。
(1)先验概率建模
作者认为先验概率与均匀分布和高斯分布的组合成比例:
其中为观测点基于周围支持点的平均值,为观测点周围邻域内所有支持点的视差值所构成的取值范围。为了进一步缩小取值范围,所有与均值之差大于的视差都被排除了。表示为一个分段线性函数,可以通过基于支持点构建的delaunay三角形来进行视差插值得到。对每一个三角形,其所在的视差平面定义如下:
其中i是所属三角形的索引,对每一个三角形,平面参数可以由三角形的三个顶点通过求解线性方程求得。
(2)似然概率建模
作者认为图像似然概率可以表示成一个拉普拉斯分布:
其中 和 为特征向量,是一个常数。if条件保证似然概率约束在左右图像的同一行上。特征向量由观测点周围邻域内像素的sobel响应值拼接而成,故其维数为(实际使用的特征向量依然按照上一节所引入的16维特征向量)。作者在文中特别指出基于sobel滤波的特征性能明显好于基于LoG滤波的特征。
(3)基于概率生成模型的采样
概率生成模型的优点是给定支持点集合和左图参考点,可以从右图采样对应的像素点,方法如下:
1)给定S和,可以根据先验概率分布随机采样视差;
2)给定和,可以根据似然概率分布得到右图对应点。
如下图所示是采样过程示意图:
为了更好的理解这个过程,下图展示了使用灰度值作为特征,对左图每一个像素重复上述过程采样一百次,取平均之后得到一个新图,从图中可以看到,正如所预料的那样,新图相当于一个模糊版本的右图。
第三节讲述了概率生成模型:
(1)提出一个先验概率分布和图像似然概率分布,并基于此进行立体匹配,先验分布决定视差采样的范围,似然概率提供了视差采样可靠性的判断;
(2)根据概率生成模型,给定左图和支持点,可以从右图中进行采样。
然而更为重要的是如何根据概率生成模型来计算视差图。作者提出可以使用最大后验概率估计(MAP)的方法估计视差:
其中,,......,表示右图中位于对极线上的观测点。后验概率表示给定左图观测点、支持点集和右图观测点,左图观测点取视差的概率。后验概率可以被分解为:
将先验概率和似然概率带入上式,并取负对数可以得到一个能量函数:
能量函数的第一部分表示似然项,第二部分为先验项或平滑项。通过最小化能量函数,可以得到观测点的视差。出于编程的考虑,实际当中应用的能量函数为:
这样可以预计算先验项。最小化能量函数时,将视差分成了两个部分:(1)基于先验分布,仅考虑观测点经插值后的平均视差的局部范围,即, sradius一般取2或3;(2)基于像素周围邻域内的所有支撑点的视差构成的视差搜索范围(如下图所示),这部分视差在最小化能量函数时不考虑先验项。(Note:最小化搜索过程中考虑了纹理,对于太弱的纹理直接跳过计算视差)
同样的方法可以计算右图的视差图。
后处理部分采用无效区域孔洞插值、左右一致性检验、连通域噪声剔除、中值滤波、自适应中值滤波。
1.左右一致性检测
设左图像素点的视差为,则右图的对应点的视差为,若,则认为视差是可靠的,否则将其置为-1或其他无效值。其中一般取1或2,可适当调整。
2.孔洞插值
在立体匹配的过程中,有多种情况导致视差图中某点的像素被置为无效值,如纹理不足、左右一致性检验、图像边缘处的像素。对于这些点处的视差往往需要通过插值给予其一个合理的视差值。首先考虑水平方向,若某个无效区域的最左侧和最右侧的视差值为和,考虑深度突变,则其中所有点的视差的插值公式如下:
其中表示深度不连续的阈值。垂直方向也以同样的方法进行一次插值。
3.中值滤波与自适应中值滤波
中值滤波可以消除视差图中的孤立噪点,而自适应中值滤波类似于双边滤波,在平滑去噪的同时,还能较好的保留视差图的边缘。原理:略
4.连通域噪声剔除
视差图中总是包含着错误的视差,由于弱纹理、反射、噪声等诸多因素,这些错误的视差往往成聚集成小的块状区域,使用该方法可以有效去除视差图中块状噪声区域。给定连通域的灰度阈值和尺寸阈值,通过检测连通域的尺寸是否满足尺寸阈值条件,来判断该连通域是否为噪声。若为噪声,将该连通域中的所有视差置为无效值。
作者已经开源了C++源代码,并提供了Matla接口,下载地址为http://www.cvlibs.n。源码如下:
下载源码后,导入visual studio工程,编写main函数:
#include
#include
#include "elas.h"
#include "image.h"
#include
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 iml = imread( path1, -1);
Mat imr = imread( path2, -1);
if (iml.rows != imr.rows || iml.cols != imr.cols){
cerr << "左右视图必须拥有相同的尺寸" << endl;
return 1;
}
Mat left, right;
if (iml.channels() == 3)
cvtColor(iml, left, COLOR_RGB2GRAY);
else
iml.copyTo(left);
if (imr.channels() == 3)
cvtColor(imr, right, COLOR_RGB2GRAY);
else
imr.copyTo(right);
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(0), disp_right.ptr(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算法生成的对应的视差图:
各位觉得不错的一定要点个赞哦!!!!!!