用Python和OpenCV从零搭建一个完整的双目视觉系统(四)

本系列文章旨在系统性地阐述如何利用 Python 与 OpenCV 库,从零开始构建一个完整的双目立体视觉系统。

本项目github地址:https://github.com/present-cjn/stereo-vision-python.git

在上一篇文章中,我们完成了相机标定这一最关键的基础步骤,并得到了一份高精度的相机参数文件。现在,我们将利用这份参数文件,来执行双目视觉的核心任务------立体匹配 (Stereo Matching)

我们的目标是,模仿人类大脑处理左右眼图像差异的方式,为左图中的每一个像素,在右图中找到它的对应点。通过计算它们之间的像素位置差异,即视差 (Disparity) ,我们就能得到一张灰色的、包含深度信息的图像------视差图 (Disparity Map)。这张图上的每一个像素点的强度值,都直接对应着真实世界的深度信息。

本文将详细讲解立体匹配的全过程,从准备工作"立体校正",到核心算法"SGBM",再到决定最终效果的"参数调优"。

1. 匹配前的准备:立体校正 (Rectification)

在原始的双目图像中,由于相机安装可能存在微小的角度偏差,左图中的一个点,其在右图中的对应点可能位于一条倾斜的直线上(我们称之为"对极线")。在整张图上沿着成千上万条斜线去搜索匹配点,计算量巨大且效率低下。

立体校正 (Stereo Rectification) 的目的,就是通过数学变换,将这种复杂的二维搜索问题,简化为高效的一维搜索。

  • 目标 : 利用标定得到的旋转矩阵 R 和平移向量 T,对左右图像进行虚拟的旋转,使得两个相机的成像平面完全共面,并且像素行严格对齐。
  • 效果 : 校正后,对于左图上的任意一个像素点 (x, y),其在右图上的对应点必定位于同一水平线 y****上。现在,算法只需要在这一行上进行一维搜索即可,效率和可靠性都大大提升。

原左右图像为

极线校正后左右图像为

代码实现 (utils/image_utils.py)

我们的 rectify_stereo_pair 函数封装了整个校正过程。

复制代码
# utils/image_utils.py
def rectify_stereo_pair(left_img, right_img, stereo_params, calib_image_size):
    # 从标定参数中提取 K, D, R, T 矩阵
    K1, D1, K2, D2, R, T = (stereo_params[k] for k in ('K1', 'D1', 'K2', 'D2', 'R', 'T'))

    # 1. 计算校正变换和投影矩阵
    # 这一步是核心,它计算出将相机坐标系旋转到理想平行状态所需的旋转矩阵 R1, R2
    # 以及将3D点投影到校正后图像平面上的投影矩阵 P1, P2
    # 最重要的是,它还输出了用于三维重建的 Q 矩阵
    R1, R2, P1, P2, Q, _, _ = cv2.stereoRectify(
        K1, D1, K2, D2, calib_image_size, R, T, alpha=0
    )

    # 2. 计算从原始图像到校正图像的像素映射表
    # `initUndistortRectifyMap` 会为每个像素计算出它在校正后图像中的新位置
    left_map1, left_map2 = cv2.initUndistortRectifyMap(K1, D1, R1, P1, left_img.shape[1::-1], cv2.CV_16SC2)
    right_map1, right_map2 = cv2.initUndistortRectifyMap(K2, D2, R2, P2, right_img.shape[1::-1], cv2.CV_16SC2)

    # 3. 应用映射表,执行校正
    left_rectified = cv2.remap(left_img, left_map1, left_map2, cv2.INTER_LINEAR)
    right_rectified = cv2.remap(right_img, right_map1, right_map2, cv2.INTER_LINEAR)
    
    return left_rectified, right_rectified, Q

这个函数的输出,就是我们进行立体匹配所需要的、高质量的校正图像和Q矩阵。

2. 核心算法:SGBM (Semi-Global Block Matching)

现在我们有了对齐的左右图像,接下来就要计算视差了。本项目采用的是 SGBM (半全局块匹配) 算法,它是对传统 BM (块匹配) 算法的巨大改进。

  • 基本思想: 它不仅像 BM 算法那样,通过比较小像素块的相似度(SAD)来寻找匹配,更引入了一个"全局"的视角。它会惩罚那些导致视差剧烈跳变的匹配,鼓励生成一条平滑、连续的视差路径。
  • 优势: 相比 BM,SGBM 能更好地处理弱纹理区域(如白墙)和重复纹理区域,生成的视差图更完整、噪声更少。
代码实现 (processing/stereo_matcher.py)

我们将 SGBM 的所有逻辑都封装在 StereoMatcher 类中。

复制代码
# processing/stereo_matcher.py
import cv2
import config

class StereoMatcher:
    def __init__(self):
        # 在构造函数中,从 config.py 文件一次性加载所有 SGBM 参数
        # 这使得调参变得非常方便
        self.matcher = cv2.StereoSGBM_create(
            minDisparity=config.SGBM_MIN_DISPARITY,
            numDisparities=config.SGBM_NUM_DISPARITIES,
            blockSize=config.SGBM_BLOCK_SIZE,
            P1=config.SGBM_P1,
            P2=config.SGBM_P2,
            disp12MaxDiff=config.SGBM_DISP12_MAX_DIFF,
            preFilterCap=config.SGBM_PRE_FILTER_CAP,
            uniquenessRatio=config.SGBM_UNIQUENESS_RATIO,
            speckleWindowSize=config.SGBM_SPECKLE_WINDOW_SIZE,
            speckleRange=config.SGBM_SPECKLE_RANGE,
            mode=config.SGBM_MODE
        )

    def compute_disparity(self, left_rectified_img, right_rectified_img):
        # SGBM 算法要求输入单通道的灰度图
        gray_left = cv2.cvtColor(left_rectified_img, cv2.COLOR_BGR2GRAY)
        gray_right = cv2.cvtColor(right_rectified_img, cv2.COLOR_BGR2GRAY)
        
        # 计算视差图
        disparity_map = self.matcher.compute(gray_left, gray_right)
        
        return disparity_map

4. 视差图的秘密:*16 的含义

当你拿到 compute 函数返回的 disparity_map 时,需要注意一个关键细节:它的数据类型是 CV_16S(16位有符号整数),并且里面的值是真实视差的16倍

这是 OpenCV 为了在内部用高效的整数运算来表示亚像素精度 而做的设计。一个值为 800 的点,其真实的、以像素为单位的视差是 800 / 16.0 = 50.0

因此,当我们需要使用或显示真实的视差值时,必须记得将它除以16.0

用项目的中的测试图片可以得到以下视差图

总结

通过立体校正 的预处理和强大的 SGBM 算法 ,我们成功地从两张2D图像中提取出了包含深度信息的视差图。更重要的是,我们学会了如何通过交互式调参来优化匹配结果,这是所有立体视觉应用开发者的核心技能之一。

在下一篇文章中,我们将进入一个核心步骤:利用这张高质量的视差图和 Q 矩阵,计算出深度图并重建出真正的三维点云。

相关推荐
姓蔡小朋友16 小时前
Java 定时器
java·开发语言
长行16 小时前
Python|Windows 安装 DeepSpeed 安装方法及报错 Unable to pre-compile async_io 处理
windows·python·deepspeed
百锦再16 小时前
python之路并不一马平川:带你踩坑Pandas
开发语言·python·pandas·pip·requests·tools·mircro
Python之栈16 小时前
5款拖拽式Python GUI生成器助你快速打造炫酷界面
python
灏瀚星空16 小时前
基于 Python 与 GitHub,打造个人专属本地化思维导图工具全流程方案(上)
开发语言·人工智能·经验分享·笔记·python·个人开发·visual studio
用什么都重名16 小时前
「实战指南」使用 Python 调用大模型(LLM)
python·大模型·llm·api调用
是Dream呀16 小时前
Python从0到100(一百):基于Transformer的时序数据建模与实现详解
开发语言·python·transformer
资源存储库16 小时前
【笔记】如何修改一个conda环境的python版本?
笔记·python·conda
xcLeigh16 小时前
AI的提示词专栏:Prompt 与 Python Pandas 的结合使用指南
人工智能·python·ai·prompt·提示词
草莓熊Lotso16 小时前
Python 入门超详细指南:环境搭建 + 核心优势 + 应用场景(零基础友好)
运维·开发语言·人工智能·python·深度学习·学习·pycharm