尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

双目立体视觉系统实战:标定、立体匹配与深度图生成

双目立体视觉系统实战:标定、立体匹配与深度图生成 简介这是一份面向机器视觉初学者与相关工程技术人员的双目立体视觉系统教学课件内容系统讲解立体视觉如何基于视差原理通过双目图像获取三维环境信息并覆盖系统标定、立体匹配以及3D重建与后续处理等关键环节。课件对单个摄像机标定与系统标定的作用、亚像素级匹配对深度估计精度的提升以及光轴平行模型和光轴会聚模型下的三维坐标计算均做了清楚说明适合用于课程教学、自学入门或项目方案梳理。压缩包仅含1个pptx文件约4.58MB共55页结构紧凑便于快速通读和课堂演示。目前已有133人学习下载适合需要建立双目视觉整体认知并了解核心原理的读者。1. 双目立体视觉系统在学什么先分清它和ToF、结构光的差别拿到一份名为《双目立体视觉系统》的教案最常见的打开方式是从相机模型和公式讲起。但真正常被问住的问题是在 ToF 和结构光已经足够成熟的今天为什么还要用两个普通相机去算深度反直觉的答案是双目立体视觉系统不主动发射任何光它只靠左右两幅图像之间的视差用三角测量把深度还原出来。这意味着它几乎没有额外功耗成本接近两个工业相机帧率不受投射模组限制室内外都能工作所以在机器人抓取、工业测量、ADAS 前视感知里它依然是性价比最高的深度方案之一。但它的代价也很明确弱纹理、重复纹理、大面积反光会让匹配直接失效远距离深度误差随距离平方增长。这套系统的学习路径不是为了把对极几何的公式背下来而是为了把“世界坐标—相机坐标—像素坐标—视差—深度”这条链路在工程上真正打通。适合读它的人是打算做深度感知但不想引入主动光源的工程师以及刚拿到双目模组、需要快速评估精度的选型同学。2. 双目立体视觉系统的成像基础相机标定、对极几何与极线校正2.1 双目相机为什么能测深度视差与三角测量的直觉关系人眼能感知远近是因为两只眼睛从略微不同的角度观察同一个物体物体在左右视网膜上的位置存在偏移这个偏移就是视差。双目相机完全复刻这个机制只是把视网膜换成了图像传感器。物体越近左右图像中的水平偏移越大物体越远偏移趋近于零。几何关系用一条公式就能表达深度 Z f · b / d其中 f 是焦距b 是左右相机光心之间的距离基线d 是视差单位是像素。这个公式是后面一切计算的起点它说明三件事第一深度和视差成反比近距离的视差变化大深度分辨率高第二基线越长同样视差对应的深度越远但近处遮挡会加重第三f 本质上决定了视差对深度的放大倍数长焦镜头天然比短焦镜头在纵深方向更敏感代价是视场角变小。2.2 标定到底标了什么内参、外参、畸变与对极约束把公式 Z f·b/d 真正用起来之前必须回答一个问题f 是多少、b 是多少、左右相机光轴是不是严格平行现实中没有任何一对相机是完美对齐的所以需要用标定来求以下参数。内参包括焦距 f_x、f_y、主点 c_x、c_y描述的是相机坐标系到像素坐标系的投影关系畸变系数包括径向畸变 k1、k2、k3 和切向畸变 p1、p2描述的是镜头光学误差导致的像素偏移。外参是右相机相对左相机的旋转矩阵 R 和平移向量 T它们描述两个相机之间的空间位姿关系。把这些参数代入对极几何可以得到基础矩阵 F 和本质矩阵 E它们的核心约束是左图中的一个像素点在右图中对应的搜索轨迹是一条极线而不是整幅图。极线校正Rectification是标定之后最关键的一步。它通过对左右图像做重投影把极线强行拉成水平的这样立体匹配时只需要在同一行上进行一维搜索计算量从二维降为一维。判断一套双目立体视觉系统标定得好不好最直观的指标就是校正后的左右图像在同一行上是否对齐。2.3 用 OpenCV 跑通双目标定的最小代码标定棋盘格是工业界最常用的做法。下面的流程用了 OpenCV 的 Python 接口覆盖“角点检测—单目标定—双目标定—极线校正”四步每一步都有独立的输出可以检查。import cv2 import numpy as np # 棋盘格内角点数9x6 表示每张图要检测到 54 个内角点 pattern_size (9, 6) 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) obj_points [] # 世界坐标棋盘格上所有角点都在 z0 平面 img_points_l [] # 左图像素坐标 img_points_r [] # 右图像素坐标 # image_pairs 是预先读入的左右图像对列表建议采集 15~25 对 for left_img, right_img in image_pairs: gray_l cv2.cvtColor(left_img, cv2.COLOR_BGR2GRAY) gray_r cv2.cvtColor(right_img, cv2.COLOR_BGR2GRAY) ret_l, corners_l cv2.findChessboardCorners(gray_l, pattern_size) ret_r, corners_r cv2.findChessboardCorners(gray_r, pattern_size) if ret_l and ret_r: criteria (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) corners_l cv2.cornerSubPix(gray_l, corners_l, (11, 11), (-1, -1), criteria) corners_r cv2.cornerSubPix(gray_r, corners_r, (11, 11), (-1, -1), criteria) obj_points.append(objp) img_points_l.append(corners_l) img_points_r.append(corners_r) # 先分别做单目标定为双目标定提供内参初值 ret_l, K_l, dist_l, _, _ cv2.calibrateCamera(obj_points, img_points_l, gray_l.shape[::-1], None, None) ret_r, K_r, dist_r, _, _ cv2.calibrateCamera(obj_points, img_points_r, gray_r.shape[::-1], None, None) # 双目标定求解右相机相对左相机的 R 和 T ret, K_l, dist_l, K_r, dist_r, R, T, E, F cv2.stereoCalibrate( obj_points, img_points_l, img_points_r, K_l, dist_l, K_r, dist_r, gray_l.shape[::-1], criteriacriteria, flags0 ) # 极线校正输出用于 remap 的映射表 R1, R2, P1, P2, Q, roi1, roi2 cv2.stereoRectify( K_l, dist_l, K_r, dist_r, gray_l.shape[::-1], R, T, alpha0 ) map1_l, map2_l cv2.initUndistortRectifyMap(K_l, dist_l, R1, P1, gray_l.shape[::-1], cv2.CV_32FC1) map1_r, map2_r cv2.initUndistortRectifyMap(K_r, dist_r, R2, P2, gray_r.shape[::-1], cv2.CV_32FC1) rect_l cv2.remap(left_img, map1_l, map2_l, cv2.INTER_LINEAR) rect_r cv2.remap(right_img, map1_r, map2_r, cv2.INTER_LINEAR)这段代码里有几个参数要解释清楚。pattern_size是内角点数而不是棋盘格格数9x6 的棋盘格实际上有 10x7 个格子写反会导致检测失败。cornerSubPix把角点细化到亚像素精度这一步直接影响重投影误差不能省略。stereoCalibrate的flags0表示让左右内参在优化过程中自由变化如果你对单个相机标定结果有信心可以用cv2.CALIB_FIX_INTRINSIC只优化外参。alpha0表示校正后裁剪掉图像中无内容的黑色区域得到的图视野略小但更干净如果要保留完整像素改为alpha1。2.4 标定结果怎么判断好坏重投影误差与极线对齐标定不是跑完代码就结束必须看输出。stereoCalibrate返回的ret是重投影均方根误差单位是像素一般要求小于 0.3超过 0.5 基本说明角点检测有噪声或者标定板图像数量不足。更直观的验证方式是把rect_l和rect_r并排显示在同一行上画一条水平线观察棋盘格角点是否落在同一水平位置。只要极线校正做对了任意一个角点在左右图中的 y 坐标差应该在 1 个像素以内。提示采集标定图像时棋盘格要覆盖画面的四角和中心并且倾斜角度至少要有 15 度以上纯正面朝向的图对求解内参没有帮助。3. 双目立体视觉系统的立体匹配SGBM 参数调优与视差后处理3.1 立体匹配的四步流水线代价计算、聚合、优化与精化有了极线校正后的左右图像接下来的任务是找到左图每个像素在右图中的对应点进而得到视差。这件事在计算视觉里被拆成四个阶段。第一步是匹配代价计算评估两个像素在多大概率上是同一个空间点第二步是代价聚合把邻域内像素的代价信息汇总抑制单点噪声第三步是视差优化通过动态规划或全局能量最小化选出每个像素的最终视差第四步是视差精化做亚像素插值和左右一致性检查剔除遮挡区域的错误匹配。理解这条流水线比记算法名字重要。SGMSemi-Global Matching是这套流程最成功的工程实现它的核心思想是在多个方向上做路径聚合用平滑惩罚项约束相邻像素的视差变化既保留全局优化的效果又把计算量控制在实时范围内。OpenCV 的 SGBM 是 SGM 的简化实现虽然效果不是最顶级的但胜在开箱即用、参数稳定是评估一套双目立体视觉系统可行性的首选工具。3.2 代价函数怎么选SAD、NCC 与 Census 的取舍代价计算是匹配质量的地基。SAD绝对差之和最简单对光照变化极其敏感适合室内光照稳定的场景NCC归一化互相关对线性光照变化不敏感但计算量大Census 变换对光照变化和噪声有很强的鲁棒性因为它比较的是邻域内像素的相对大小关系而不是绝对值已经成为工业界的主流选择。下表给出三种代价函数在典型场景下的表现对比代价函数光照鲁棒性弱纹理表现计算开销适用场景SAD差中低光照可控的室内、静态场景NCC中中高光照渐变、纹理较丰富的场景Census好中中室外、光照突变、高动态范围场景实践中要注意SGBM 内部使用的是一种基于块采样的代价计算方式不完全等同于上表中的任何一类但选型思路可以迁移如果你在户外强光下做匹配优先考虑基于 Census 的算法族如果在实验室环境里做尺寸测量SAD 甚至都够用。3.3 用 SGBM 算第一张视差图完整代码与参数表下面的代码把校正后的左右灰度图输入 SGBM输出视差图。这是整个双目立体视觉系统里参数最敏感的一步每个参数都直接影响视差图质量。# 创建 SGBM 匹配器OpenCV 4.x 使用 createStereoSGBM sgbm cv2.StereoSGBM_create( minDisparity0, numDisparities64, # 视差搜索范围必须是 16 的倍数 blockSize11, # 匹配块大小必须是奇数常用 3~11 P18 * 3 * blockSize ** 2, # 视差变化 1 像素的惩罚项较小 P232 * 3 * blockSize ** 2, # 视差变化大于 1 像素的惩罚项较大 disp12MaxDiff1, # 左右一致性检查允许的最大差值 preFilterCap63, # 预处理阶段截断值低于此值保留 uniquenessRatio10, # 最小代价是次小代价的比例阈值 speckleWindowSize100, # 去除小连通区域的窗口大小 speckleRange2, # 连通区域内允许的视差波动 modecv2.STEREO_SGM_MODE_SGBM ) # compute 输入是校正后的灰度图输出是 int16 类型的原始视差 disparity sgbm.compute(rect_l_gray, rect_r_gray).astype(np.float32) / 16.0SGBM 返回的原始值不是真正的像素视差而是放大 16 倍的定点数所以必须除以 16这一步漏掉会让后续所有深度计算都偏离 16 倍。numDisparities决定能检测到的最近距离数值越大匹配范围越广但计算量线性增长而且视差范围以外的区域会产生大量噪声它的值必须能被 16 整除这是 API 的硬约束。blockSize越大弱纹理区域的空洞越少但深度边界会被磨平尤其注意它必须是奇数。P1和P2是平滑惩罚项P2一般是P1的 4 到 10 倍如果视差图出现横向拉扯的条纹通常是P2太小如果过度平滑导致细节丢失则需要调小。3.4 视差图常见的脏点空洞、条纹与遮挡区第一版视差图出来之后几乎一定是不干净的。空洞主要来自遮挡区域左图可见但右图不可见的像素没有正确匹配disp12MaxDiff开启的左右一致性检查会把错误值置为负数表现就是黑色空洞。条纹噪声通常出现在物体边缘和浅纹理区域是代价聚合不充分或平滑惩罚不匹配的表现。细小噪点可以用speckleWindowSize清除凡是小于该阈值的连通视差区域都会被置为无效。对视觉质量要求更高的场景可以引入 WLS 滤波做视差后处理。OpenCV 的cv2.ximgproc.createDisparityWLSFilter需要配合cv2.ximgproc.createRightMatcher使用对左图和右图分别计算视差然后以左图为引导图做保边滤波。WLS 能把视差图的边缘修得很干净但代价是额外的一次 SGBM 计算和约 30% 以上的耗时是否使用取决于你的应用是追求实时性还是追求重建质量。4. 从视差到深度图三角测量、Q 矩阵与精度边界4.1 从视差到三维点云Q 矩阵与企业级落地代码极线校正之后左右图像的投影矩阵 P1、P2 已经被重新计算它们把像素坐标映射到以左相机为原点的三维坐标系。OpenCV 把这一步封装在了 Q 矩阵里Q 是一个 4×4 的投影矩阵它的作用是把齐次坐标下的像素视差映射为齐次三维坐标。使用reprojectImageTo3D可以在一条调用里完成整个双目立体视觉系统的三维重建输出。# Q 矩阵由 stereoRectify 直接输出shape 为 (4, 4) # disparity 是除以 16 后的浮点视差图无效区域为负数 points_3d cv2.reprojectImageTo3D(disparity, Q, handleMissingValuesTrue) # points_3d 是 HxWx3 的数组三个通道分别是 x、y、z # 深度就是 z 通道 depth points_3d[:, :, 2] # 过滤无效深度inf、nan 和负值对应的点不可用 valid np.isfinite(depth) (depth 0)handleMissingValuesTrue会把无效视差对应的三维点变成一个很大的固定值而不是 NaN这一步在很多渲染管线里能避免颜色缓冲区的异常过滤时统一用np.isfinite和depth 0双条件判断即可。Q 矩阵最后一行通常为 [0, 0, -1/T_x, 0]其中 T_x 是基线的负数它决定了视差到深度的缩放比例。如果点云的尺度和你实际测量值相差一个常数优先检查基线单位是不是毫米和米混用了。4.2 深度精度由谁决定误差公式与参数边界双目立体视觉系统的深度误差可以用一个公式描述ΔZ Z² · Δd / (b · f)。也就是说深度误差和距离的平方成正比和基线、焦距成反比。这解释了为什么双目相机在 2 米以内表现优秀到了 10 米开外误差会急剧膨胀。下表给出一个 50mm 焦距、120mm 基线配置在 3μm 像元尺寸下的理论深度精度参考目标距离 Z视差精度 0.1 像素视差精度 0.5 像素0.5 m约 0.5 mm约 2.5 mm1.0 m约 2.0 mm约 10.0 mm3.0 m约 18.0 mm约 90.0 mm注意这组数据只做量级参考实际还受图像噪声、标定残差和匹配窗口影响。但它揭示了三个工程结论第一想提升远距离精度优先加长基线而不是换更高分辨率相机基线翻倍等效于视差精度翻倍第二亚像素匹配不是可选项从整像素提升到 0.1 像素的视差精度等于把深度精度提升了 10 倍第三近距离场景下制约精度的是标定残差和匹配噪声而不是相机分辨率盲目换高分辨率相机收效甚微。4.3 深度图里的典型伪影边缘膨胀、远处噪点与光照不均边缘膨胀是最容易观察到的伪影。前景物体的深度会向背景扩散形成一圈错误的前景深度根源是匹配窗口跨越了深度不连续边界。缓解方法是减小blockSize但弱纹理区域会出现空洞所以边缘保护和空洞填充在参数上是一对矛盾。远处噪点来自于视差量化误差距离越远同样的视差步长对应的深度间隔越大深度图上就会看到一层层阶梯状的层次感这种伪影在像素级匹配下无法完全消除。光照不均的问题往往被忽略左右相机即使型号相同同一物体在两幅图中的亮度也可能有明显差异导致匹配代价整体偏高。SGBM 内置的preFilterCap截断能在一定程度上压缩这种差异但根本解法是在硬件层面对两个传感器做曝光同步或者在预处理阶段做直方图匹配。5. 用最小实验跑通双目立体视觉系统棋盘格、代码与排错路径5.1 一套最小实验需要什么硬件与采集流程不需要专业的双目模组两台同型号的工业相机或 USB 相机就能起步但有几个硬性要求传感器型号一致、分辨率一致、镜头焦距一致最好能硬件触发同步采集。如果只能用软件触发尽量拍摄静态场景避免运动物体造成左右图时间不一致。棋盘格要用漫反射材质打印贴在硬板上避免弯折。采集时让棋盘格出现在画面的不同位置和角度至少 15 对推荐 20 对以上并把重投影误差控制在 0.3 像素以内。5.2 验证立体视觉结果的三个维度极线、直方图和点云跑通流程之后用三个手段做验证。第一是极线验证在rect_l和rect_r上画同一水平线用鼠标点击角点对比 y 坐标差。第二是视差直方图把视差值的分布画出来真实场景中的视差应该集中在几个有限的峰值附近如果直方图像噪声一样弥散说明标定或匹配参数有问题。第三是点云投影把reprojectImageTo3D输出的三维点投影回左图比较颜色和位置是否和原图一致这个闭环能同时检验标定、匹配和三维重建三个环节的错误。5.3 一个容易被忽略的性能坑最后一个经验是分辨率与算力的平衡。很多人一上来就用全分辨率跑 SGBM1080p 图像配合 64 级视差搜索在普通 CPU 上耗时可能到几百毫秒。实际工程做法是先降采样到 720p 或 640p 完成算法验证确认深度精度和视差图质量达标后再逐步提高分辨率。如果把分辨率从 1080p 降到 720pSGBM 的耗时通常能降到原来的 40% 左右而近距离场景下的深度精度损失往往在一个可接受的范围内——这是调试双目立体视觉系统时最实用也最常被忽略的一个平衡点。本文还有配套的精品资源点击获取
返回列表