注:这是一个专题,我会一步步介绍SGBM的实现,按照我的使用和优化过程逐步改善算法,附带实现方法
系列文章
- 【立体匹配】:双目立体匹配SGBM:(1)运行
【立体匹配】:双目立体匹配SGBM:(1)运行
- 双目立体匹配SGBM概述
- SGBM参数配置方法
- 性能优化策略
- 常见问题与解决方案
- 具体使用
- sgbm的python类接口
双目立体匹配SGBM概述
SGBM(Semi-Global Block Matching)是一种结合局部和全局优化的立体匹配算法,通过代价聚合和动态规划实现视差计算。其核心包括代价计算、代价聚合、视差计算和视差优化四个步骤。
SGBM参数配置方法
OpenCV中SGBM的关键参数如下:
import cv2
sgbm = cv2.StereoSGBM_create(
minDisparity=0, # 最小视差
numDisparities=64, # 视差范围(需为16的倍数)
blockSize=3, # 匹配块大小(奇数)
P1=8*3*3, # 平滑性惩罚参数1
P2=32*3*3, # 平滑性惩罚参数2
disp12MaxDiff=1, # 左右视差检查阈值
preFilterCap=63, # 预处理滤波截断值
uniquenessRatio=15, # 唯一性匹配比率
speckleWindowSize=100, # 视差连通区域滤波窗口
speckleRange=32 # 视差连通性阈值
)
性能优化策略
参数调优
- 增大
numDisparities
可提升深度范围但会增加计算量,建议根据实际场景调整。 P1
和P2
控制视差平滑性,通常设为P1=8*通道数*blockSize^2
,P2=4*P1
。
预处理增强
- 使用直方图均衡化或CLAHE增强图像对比度:
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
left_img = clahe.apply(left_img)
后处理优化
- 采用WLS(加权最小二乘)滤波消除噪声:
wls_filter = cv2.ximgproc.createDisparityWLSFilter(sgbm)
filtered_disp = wls_filter.filter(disp, left_img)
常见问题与解决方案
边缘模糊
- 原因:
blockSize
过大导致过度平滑。 - 解决:减小
blockSize
或使用导向滤波优化视差图。
视差不连续
- 原因:
uniquenessRatio
过低或噪声干扰。 - 解决:增大
uniquenessRatio
至10-20,或启用speckleWindowSize
滤波。
具体使用
sgbm的python类接口
import cv2
import numpy as np
import time
cv2.setNumThreads(4) # 设置最大线程
cv2.setUseOptimized(True)
class SGBM:
def __init__(self, use_blur=True):
self.prev_disp = None
self.alpha = 0.3 # 平滑系数 (0-1),越小越平滑
self.use_blur = use_blur
self.sgbm = self.create_sgbm()
def create_sgbm(self):
window_size = 5
min_disp = 0
num_disp = 64 - min_disp # 必须是16的整数倍
stereo = cv2.StereoSGBM_create(
minDisparity=min_disp,
numDisparities=num_disp,
blockSize=window_size,
P1=8 * 3 * window_size**2, # 视差平滑参数
P2=32 * 3 * window_size**2,
disp12MaxDiff=1,
uniquenessRatio=10,
speckleWindowSize=100,
speckleRange=32,
mode=cv2.STEREO_SGBM_MODE_SGBM_3WAY# STEREO_SGBM_MODE_SGBM_3WAY ,STEREO_SGBM_MODE_HH, STEREO_SGBM_MODE_SGBM, STEREO_SGBM_MODE_HH4,STEREO_SGBM_MODE_HH4的速度最快,STEREO_SGBM_MODE_HH的精度最好
)
return stereo
def create_bm_matcher(self):
stereo = cv2.StereoBM_create(
numDisparities=64, # 视差范围(必须是16的倍数)
blockSize=15 # 匹配块大小(奇数,建议5~25)
)
return stereo
def estimate_depth(self, left_image, right_image):
"""
进行深度估计推理,返回视差图(深度图)。
"""
# 转换为灰度图
gray_left = cv2.cvtColor(left_image, cv2.COLOR_BGR2GRAY)
gray_right = cv2.cvtColor(right_image, cv2.COLOR_BGR2GRAY)
# clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
# gray_left = clahe.apply(gray_left)
# gray_right = clahe.apply(gray_right)
# gray_left = cv2.equalizeHist(gray_left)
# gray_right = cv2.equalizeHist(gray_right)
disp = self.sgbm.compute(gray_left, gray_right).astype(np.float32) / 16.0 # SGBM返回的视差需要除以16
# # 应用选择的滤波器
# if current_filter in ['guided', 'wls', 'fgs']:
# disp = apply_disparity_filter(
# disp, left_image, current_filter,
# lambda_=8000, sigma=1.5
# )
# else:
# disp = apply_disparity_filter(disp, filter_type=current_filter)
if self.prev_disp is not None:
disp = self.alpha * disp + (1 - self.alpha) * self.prev_disp
self.prev_disp = disp.copy()
# disp = cv2.medianBlur(disp, 5) # 中值滤波
disp = cv2.morphologyEx(disp, cv2.MORPH_CLOSE, np.ones((5,5),np.uint8)) # 闭运算填充空洞
return disp
在这个类里,默认调用sgbm方法,也可以切换bm算法,速度更快,但是视差图的空洞更多
import cv2
import numpy as np
from stereomodel.OpencvSGBM.utils.SGBM import SGBM
from config import Stereo
SGBM_ins = SGBM(use_blur=True)
Stereo_ins = Stereo()
if __name__ == '__main__':
left_img = cv2.imread('../../data/mid/im0.png')
right_img = cv2.imread('../../data/mid/im1.png')
disp = SGBM_ins.estimate_depth(left_img, right_img)
Stereo_ins.show_depth_point(disp, left_img)
sgbm方法
bm方法