双目立体视觉系统实战:标定、校正、SGBM匹配与深度图生成
发布时间:2026/9/18 14:22:29 作者:尧图编辑部 阅读量:1,286

简介这份PPT课件面向机器视觉、计算机视觉方向的学生与工程技术人员系统讲解双目立体视觉的完整知识框架帮助读者建立从二维图像认知三维环境信息的能力。内容围绕双目视觉系统模型与关键技术展开涵盖视差与深度的反比关系、亚像素精细化、基于三角测量原理的三维坐标计算以及光轴平行模型与光轴会聚模型的公式推导并延伸至单个摄像机标定与系统标定、立体匹配、3D重建及后续处理等核心环节。资源包内含1个pptx文件共55页压缩包约4.51MB以图文与公式推导为主结构清晰便于按章节检索学习。目前已有757人学习下载适合用于课程复习、项目入门或逆向工程、测试测量、视觉导航等应用场景的知识梳理也可作为理解目标识别图像分割等工程问题的参考材料。1. 从一份双目立体视觉系统PPT课件说起很多做机器视觉的工程师都有过类似经历接手一个双目测距项目硬件选好了、相机装上了但打开一份双目立体视觉系统PPT课件发现里面全是公式推导和示意图看完还是不知道代码从哪一行开始写。这份课件的价值不在于它讲了多少理论而在于它把双目视觉的完整链路——标定、校正、匹配、深度恢复——压缩成了一条可以照着走的路径。问题在于课件通常只给结论不给参数只给框图不给命令。真正落地时你需要知道棋盘格用几乘几、stereoRectify的 alpha 设多少、SGBM 的numDisparities为什么必须是 16 的倍数。这篇内容就是沿着这类课件的主线把每个环节补上可执行的代码和可调的参数让新手能跑通让做过单目的熟手能看清双目的边界在哪。2. 双目立体视觉系统标定与校正的完整实现2.1 从课件里的标定框图到可运行的 calibrateCamera课件上标定那一页通常画两个相机对着一个棋盘格标注了旋转矩阵 R 和平移向量 T。落到代码里第一步是分别对左右相机做单目标定拿到各自的内参和畸变系数再做双目标定求 R 和 T。常见做法是用 OpenCV 的findChessboardCorners加cornerSubPix提亚像素角点棋盘格建议用 9×6 或 11×8方格实际尺寸用尺子量准误差直接传到深度上。import cv2 import numpy as np # 棋盘格内角点数列, 行不是方格数 pattern_size (9, 6) square_size 25.0 # 单格实际边长单位 mm objp np.zeros((pattern_size[0] * pattern_size[1], 3), np.float32) objp[:, :2] np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2) objp * square_size criteria (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) def collect_corners(img_paths): objpoints, imgpoints [], [] for p in img_paths: img cv2.imread(p, cv2.IMREAD_GRAYSCALE) ok, corners cv2.findChessboardCorners(img, pattern_size, None) if not ok: continue corners cv2.cornerSubPix(img, corners, (11, 11), (-1, -1), criteria) objpoints.append(objp) imgpoints.append(corners) return objpoints, imgpoints # 左右各拍 15~20 张覆盖画面四角和不同倾角 objL, imgL collect_corners(left_imgs) objR, imgR collect_corners(right_imgs) # 单目标定 retL, mtxL, distL, _, _ cv2.calibrateCamera(objL, imgL, gray_shape, None, None) retR, mtxR, distR, _, _ cv2.calibrateCamera(objR, imgR, gray_shape, None, None) # 双目标定固定内参只求外参 flags cv2.CALIB_FIX_INTRINSIC retS, mtxL, distL, mtxR, distR, R, T, E, F cv2.stereoCalibrate( objL, imgL, imgR, mtxL, distL, mtxR, distR, gray_shape, flagsflags)逻辑上stereoCalibrate在已知内参的前提下只优化左右相机之间的刚体变换CALIB_FIX_INTRINSIC能避免内参被外参数据带偏。T的模长就是基线单位跟square_size一致后面算深度时直接用。重投影误差retS一般要压到 0.3 像素以内超过 0.5 基本说明角点提歪了或者标定板拍得太少。2.2 stereoRectify 与 initUndistortRectifyMap 的参数怎么设标定完不能直接匹配因为左右图像没有行对齐极线是斜的。stereoRectify把两个相机虚拟成共面且光轴平行让同名点落在同一行上匹配搜索从二维降到一维。R1, R2, P1, P2, Q, roi1, roi2 cv2.stereoRectify( mtxL, distL, mtxR, distR, gray_shape, R, T, alpha0, # 0 裁剪掉无效像素1 保留全部 flagscv2.CALIB_ZERO_DISPARITY) mapLx, mapLy cv2.initUndistortRectifyMap( mtxL, distL, R1, P1, gray_shape, cv2.CV_32FC1) mapRx, mapRy cv2.initUndistortRectifyMap( mtxR, distR, R2, P2, gray_shape, cv2.CV_32FC1) imgL_rect cv2.remap(imgL, mapLx, mapLy, cv2.INTER_LINEAR) imgR_rect cv2.remap(imgR, mapRx, mapRy, cv2.INTER_LINEAR)alpha是最容易踩的坑设 0 时校正后图像会裁掉黑边视野变小但全是有效像素设 1 保留原始视野但边缘出现大量黑色区域匹配时会产生无效视差。做测距一般选 0做全景拼接选 1。CALIB_ZERO_DISPARITY保证主点在左右图中水平对齐省掉后续的偏移补偿。Q是重投影矩阵reprojectImageTo3D靠它把视差图转成三维点云别丢。2.3 校正效果的验证方法校正完必须验证不能直接往下走。最直接的办法是在校正后的左右图上画等间距水平线看同一行是否对应同一物理点。检查项合格标准不合格时的处理极线对齐左右图同一行特征点 y 差 1 像素重拍标定图增加倾斜角度重投影误差 0.3 像素剔除误差大的标定图重算基线一致性T 模长与实测基线误差 2%检查 square_size 是否量准校正后黑边alpha0 时黑边占比 15%调整标定板覆盖范围验证代码很简单把校正图叠在一起做差分或者用cv2.line每隔 40 行画一条线人工看。如果发现某一行左右错位明显多半是标定时该区域角点太少补拍几张那个角度的图重新标。3. SGBM 立体匹配与深度图生成的参数调优3.1 课件里一笔带过的匹配算法实际怎么选课件讲匹配通常只列 BM、SGBM、GC 三种然后说 SGBM 效果好。实际选型要看场景BM 快但视差图空洞多适合实时性要求高、纹理丰富的场景SGBM 半全局优化边缘和弱纹理区表现好是大多数项目的默认选择GC 精度最高但慢基本只在离线处理用。OpenCV 里StereoSGBM_create是主力参数多但每个都有明确作用。window_size 5 min_disp 0 num_disp 16 * 8 # 必须是 16 的倍数 stereo cv2.StereoSGBM_create( minDisparitymin_disp, numDisparitiesnum_disp, blockSizewindow_size, P18 * 3 * window_size ** 2, P232 * 3 * window_size ** 2, disp12MaxDiff1, uniquenessRatio10, speckleWindowSize100, speckleRange32, preFilterCap63, modecv2.STEREO_SGBM_MODE_SGBM_3WAY) disparity stereo.compute(imgL_rect, imgR_rect).astype(np.float32) / 16.0numDisparities决定搜索范围必须覆盖场景最大视差。基线 60mm、焦距 700 像素、最近测 0.5m 时视差约 84 像素取 96 或 128 才够。blockSize取 5 或 7太大边缘糊太小噪声多。P1和P2是平滑惩罚项控制视差连续性经验值是P18*通道数*blockSize²P2是P1的 4 倍左右。uniquenessRatio设 5 到 15过滤掉匹配歧义点值越大越严格但空洞越多。3.2 视差图转深度图的公式与 Q 矩阵拿到视差后深度公式是Z f * B / df 是校正后焦距P1 里的P1[0][0]B 是基线d 是视差。但更省事的做法是直接用reprojectImageTo3D它内部用 Q 矩阵一次算出 XYZ。points_3d cv2.reprojectImageTo3D(disparity, Q) # 过滤无效视差 mask disparity disparity.min() mask mask (disparity num_disp) cloud points_3d[mask] colors imgL_rect[mask]Q矩阵里已经包含了1/Tx和主点偏移所以不用自己算 f 和 B。注意disparity要除以 16 还原成真实像素值SGBM 输出的是定点数放大了 16 倍。如果深度整体偏大或偏小先检查square_size单位是否和期望一致再检查Q是不是用了stereoRectify返回的那个。3.3 空洞填充与视差后处理原始视差图在弱纹理和遮挡区会有大片空洞直接转点云很难看。常见做法是左右一致性检测加空洞填充。# 左右一致性检测 stereoR cv2.StereoSGBM_create(...) # 参数相同 dispR stereoR.compute(imgR_rect, imgL_rect).astype(np.float32) / 16.0 dispR np.fliplr(dispR) # 右视差图翻转对齐 valid np.abs(disparity - dispR) 1.0 disparity[~valid] disparity.min() # 中值滤波去椒盐 disparity cv2.medianBlur(disparity.astype(np.float32), 5)一致性检测能去掉大部分误匹配代价是空洞变多。填充可以用cv2.inpaint或者简单的邻域插值但要注意填充出来的深度不可信做测量时应该标记为无效而不是硬填。speckleWindowSize和speckleRange在 SGBM 里已经做了一轮斑点过滤如果还有零星噪点中值滤波窗口取 5 就够取 7 以上会抹掉细小结构。4. 双目测距精度的影响因素与排错清单4.1 基线、焦距、分辨率对精度的定量影响深度误差公式是ΔZ Z² * Δd / (f * B)这个式子说明三件事距离越远误差平方增长基线越大误差越小焦距越长误差越小。但基线不能无限大太大导致左右视野重叠区变小近处匹配不上。常见配置是基线 60 到 120mm焦距 500 到 1000 像素分辨率 1280×720 起步。参数典型值对精度的影响调整方向基线 B60~120mm越大精度越高重叠区越小测 1m 内用 60mm焦距 f500~1000px越大精度越高视野越窄配合分辨率选视差精度 Δd0.1~0.5px亚像素匹配决定下限用 SGBM 加亚像素分辨率1280×720 起越高角点越准不低于 720p按 1m 距离、f700、B60mm、Δd0.25 像素算ΔZ 约 6mm。如果实际误差远大于这个值问题不在公式在标定或匹配。4.2 标定重投影误差偏大的排查顺序重投影误差超过 0.5 像素时按这个顺序查先看标定板是否平整打印在纸上贴到玻璃上比直接拿纸强再看角点提取是否用了cornerSubPix不用亚像素误差直接翻倍然后看标定图数量少于 10 张基本不够15 到 20 张覆盖各角度才稳最后看square_size是否量准用游标卡尺量三个格取平均别用标称值。提示标定图不要只在一个距离拍远近各拍几张否则外参在深度方向外推会发散。4.3 视差图出现大面积黑色或条纹的处理黑色区域代表视差无效条纹代表匹配在某个方向系统性偏移。黑色多先看numDisparities是否够大不够时远处物体视差超出搜索范围直接无效。条纹状错误多半是极线没对齐回到校正步骤重新验证。还有一种情况是左右图曝光差异大SGBM 对亮度敏感匹配前用cv2.equalizeHist做直方图均衡或者把曝光手动锁死。# 匹配前预处理减小左右亮度差异 imgL_gray cv2.equalizeHist(imgL_rect) imgR_gray cv2.equalizeHist(imgR_rect) # 或者用 CLAHE 局部均衡弱纹理场景更稳 clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8, 8)) imgL_gray clahe.apply(imgL_rect)5. 把课件方案落到实际项目的三个技巧第一个技巧是标定一次存一次参数。把mtxL、distL、mtxR、distR、R、T、Q用np.savez存成 npz 文件运行时直接加载别每次启动重标。相机一旦松动或换了镜头参数必须重标所以机械结构上要给相机加防松设计。np.savez(stereo_params.npz, mtxLmtxL, distLdistL, mtxRmtxR, distRdistR, RR, TT, QQ) data np.load(stereo_params.npz)第二个技巧是给深度图加 ROI 掩膜。实际项目往往只关心画面中间一块区域把 ROI 外的视差直接置无效能省掉大量后处理时间也避免边缘畸变残留导致的假点。ROI 用校正后的有效区域roi1、roi2求交集得到。第三个技巧是用已知尺寸物体做在线校验。放一个边长已知的方块在测量范围内跑一遍点云量它的边长和真实值比。误差在 2% 以内说明整条链路正常超过就按第 4 章的排查顺序往回找。这个校验比看重投影误差更直接因为它验证的是端到端的深度精度而不是中间某一步的拟合质量。每次改完参数都跑一遍比反复调 SGBM 参数猜效果靠谱得多。本文还有配套的精品资源点击获取