作者:Ali Yasin Eser

编译:ronghuaiyang(AI 公园)

导读

双目立体视觉的第二部分,视差图计算算法。

大家好!欢迎来到立体视觉深度第二部分。我将简要解释块匹配算法。我假设您已经阅读了前一篇文章,如果你还没有读过,去读一下。我们将使用来自https://github.com/aliyasineser/stereoDepth的代码。

在第一篇文章中,我们分别拍摄了左右两幅图像,并对相机进行了校准。校准过程之后,我们有了立体图像,两张图像中相同的点在同一条线上,这意味着如果这一对图片种有一支笔,笔上的对应的点应该分别是(X1, Y)和(X2, Y),Y是行号,X1和X2的图像的列号。

双目立体视觉 II:块匹配视差图计算_3d

校正过的图像显示相同的P点。

在本例中,X1和x2之间的差异为我们提供了视差值。我们已经提到过,如果我们用左眼闭着看一个近距离的物体,反之亦然,位置根据睁眼的角度而变化。当物体距离越远,这种差别就越明显。这意味着如果视差值越小,物体越近。每个点都可以做这个操作,但是效率不高,因为:

  • 这是一个很慢的过程。时间是宝贵的。
  • 如果校正的不够好,每个点都不理想。
  • 我们需要减少误差,在这种情况下,逐点处理不会有帮助。

这就是我们使用块匹配的原因。其中一些是使用光流(图像流),或减少图像大小,以使用更少的处理能力。第二点我简单提一下,如果你想了解更详细的信息,请查阅相关论文。

我们从左边的图像开始。使用左图不是强制的,我就是这么用的。太大的块会产生平滑的图像,太小的块会产生噪声。你都应该尝试一下,找到最佳值。在选择最左边的块之后,我们从左到右搜索,并尝试尽可能多地与右边的图像匹配。由于图像被校正过了,在一个轴上搜索就足够了。

双目立体视觉 II:块匹配视差图计算_公众号_02

黑块是我们的待搜索块

双目立体视觉 II:块匹配视差图计算_3d_03

在一个轴上搜索,从左到右搜索

如何进行块匹配?有很多公式。大多数人用的是绝对差的和以及差的平方和。你可以研究一下相关的论文以及人们是如何使用它的。在这些方法中,较小的结果意味着非常相似。这将是我们选择的块的差异值。

双目立体视觉 II:块匹配视差图计算_3d_04

绝对差和的例子

双目立体视觉 II:块匹配视差图计算_3d_05

左校正后的图像,右校正后图像,块匹配后的视差图。

既然我们已经介绍了基础知识,让我们查看一下代码。在块匹配之后,我使用了WLS(加权最小二乘)滤波器来获得更平滑和更接近的视差值,可以更好地代表图像。函数为:

def depth_map(imgL, imgR):
""" Depth map calculation. Works with SGBM and WLS. Need rectified images, returns depth map ( left to right disparity ) """
# SGBM Parameters -----------------
window_size = 3 # wsize default 3; 5; 7 for SGBM reduced size image; 15 for SGBM full size image (1300px and above); 5 Works nicely

left_matcher = cv2.StereoSGBM_create(
minDisparity=-1,
numDisparities=5*16, # max_disp has to be dividable by 16 f. E. HH 192, 256
blockSize=window_size,
P1=8 * 3 * window_size,
# wsize default 3; 5; 7 for SGBM reduced size image; 15 for SGBM full size image (1300px and above); 5 Works nicely
P2=32 * 3 * window_size,
disp12MaxDiff=12,
uniquenessRatio=10,
speckleWindowSize=50,
speckleRange=32,
preFilterCap=63,
mode=cv2.STEREO_SGBM_MODE_SGBM_3WAY
)
right_matcher = cv2.ximgproc.createRightMatcher(left_matcher)
# FILTER Parameters
lmbda = 80000
sigma = 1.3
visual_multiplier = 6

wls_filter = cv2.ximgproc.createDisparityWLSFilter(matcher_left=left_matcher)
wls_filter.setLambda(lmbda)

wls_filter.setSigmaColor(sigma)
displ = left_matcher.compute(imgL, imgR) # .astype(np.float32)/16
dispr = right_matcher.compute(imgR, imgL) # .astype(np.float32)/16
displ = np.int16(displ)
dispr = np.int16(dispr)
filteredImg = wls_filter.filter(displ, imgL, None, dispr) # important to put "imgL" here!!!

filteredImg = cv2.normalize(src=filteredImg, dst=filteredImg, beta=0, alpha=255, norm_type=cv2.NORM_MINMAX);
filteredImg = np.uint8(filteredImg)

return filteredImg

你可以再这里:https://github.com/aliyasineser/stereoDepth检查项目。代码基本上就是在创建匹配器。OpenCV的文档很差(我添加了一些,但这只是冰山一角),但编码方面真的很容易。让我们来看看参数:

  • minDisparity: 最小视差值。通常我们期望这里是0,但当校正算法移动图像时,有时需要设置。
  • numDisparities: 最大视差值,必须大于0,定义视差边界。
  • blockSize: 匹配块的块大小。推荐使用[3-11],推荐使用奇数,因为奇数大小的块有一个中心。
  • P1 和 P2: 负责平滑图像,规则是P2>P1。
  • disp12MaxDiff: 视差计算的最大像素差。
  • preFilterCap:过滤前使用的值。在块匹配之前,计算图像x轴的一个导数,并用于检查边界[-prefiltercap, prefiltercap]。其余的值用Birchfield-Tomasi代价函数处理。
  • uniquenessRatio: 经过成本函数计算,此值用于比较。建议取值范围[5-15]。
  • speckleWindowSize: 过滤删除大的值,得到一个更平滑的图像。建议取值范围[50-200]。
  • speckleRange: 使用领域检查视差得到一个平滑的图像。如果你决定尝试,我建议1或2。小心,这个值会乘以16!OpenCV会这样做,所以你不需要自己去乘。

在代码中我们使用了SGBM。创建左右视差图,使用WLS滤波平滑优化图像。我还没有掌握这个,我用数值做了实验,所以我就不详细讲了。

代码:https://github.com/aliyasineser/stereoDepth可用于单目和立体摄像机标定,以及视差图计算。之后,你可以对结果做任何你想做的事。在我的项目中,我用它来检测前方是否有物体或距离太近,都是关于无人机的。你可以用你的想象力创造很多东西。

参考文献:

英文原文:https://aliyasineser.medium.com/the-depth-ii-block-matching-d599e9372712

本文仅做学术分享,如有侵权,请联系删文。


双目立体视觉 II:块匹配视差图计算_3d_06