本系列文章旨在系统性地阐述如何利用 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 configclass 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 矩阵,计算出深度图并重建出真正的三维点云。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/88241.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/88241.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/88241.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32-中断

中断分为两路:12345用于产生中断;678产生事件外设为NVIC设计流程:使能外设中断设置中断优先级分组初始化结构体编写中断服务函数初始化结构体:typedef struct {uint8_t NVIC_IRQChannel; 指定要使能或禁用的中断通道例如: TIM3_I…

Shader面试题100道之(61-80)

Shader面试题(第61-80题) 以下是第61到第80道Shader相关的面试题及答案: 61. 什么是UV展开?它在Shader中有什么作用? UV展开是将3D模型表面映射到2D纹理空间的过程,用于定义纹理如何贴合模型。在Shader中&a…

C#基础:Winform桌面开发中窗体之间的数据传递

1.主窗体using System; using System.Windows.Forms;public partial class MainForm : Form {public MainForm(){InitializeComponent();}// 打开二级窗体private void btnOpenSecondaryForm_Click(object sender, EventArgs e){// 创建二级窗体并订阅事件SecondaryForm second…

工程改Mvvm

导入CommunityToolKit vs2017只能导入7 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input;namespace WpfApp1.vi…

【HarmonyOS Next之旅】DevEco Studio使用指南(四十二) -> 动态修改编译配置

目录 1 -> 通过hook以及插件上下文实现动态配置 2 -> 在hvigorfile.ts中通过overrides关键字导出动态配置 3 -> 通过hook以及插件上下文动态配置构建配置 3.1 -> 修改每个hvigorNode中的build-profile.json5 3.2 -> 修改module.json5中的配置信息 3.3 -&g…

Android View事件分发机制详解

Android 的 View 事件分发机制是处理用户触摸(Touch)事件的核心流程,它决定了触摸事件如何从系统传递到具体的 View 并被消费。理解这个机制对于处理复杂的触摸交互、解决滑动冲突至关重要。 核心思想:责任链模式 事件分发遵循一个…

【CMake】自定义package并通过find_package找到

在一些场景下我们需要编写一些库,并希望其他程序可以找到这些库并引用。 CMake采用package这个概念来解决这个问题。 关于CMake的find_package文章有很多,但这些文章的内容大多不直观讲了一堆讲不到点子上,让人看了一头雾水。因此我想通过本文…

【MATLAB例程】AOA与TDOA混合定位例程,适用于二维环境、3个锚点的定位|附代码下载链接

本 MATLAB 程序实现了基于 Angle of Arrival (AOA) 与 Time Difference of Arrival (TDOA) 的二维定位方法,通过自适应融合与最小二乘优化,实现对未知目标的高精度估计。本例中固定使用了 3 个基站(锚点),算法框架支持…

磐维数据库panweidb集中式集群配置VIP【添加、删除和修改】

0 说明 panweidb集中式集群为了防止主备切换后应用连接无法切换到新主库,需要配置vip,应用可以只通过该ip与数据库连接,不用感知数据库在哪个节点上。 panweidb中配置 VIP主要依赖 CM 组件的 VIP 仲裁功能,通过回调脚本在主备切换…

python的保险业务管理与数据分析系统

前端开发框架:vue.js 数据库 mysql 版本不限 后端语言框架支持: 1 java(SSM/springboot)-idea/eclipse 2.NodejsVue.js -vscode 3.python(flask/django)–pycharm/vscode 4.php(thinkphp/laravel)-hbuilderx 数据库工具:Navicat/SQLyog等都可以 保险行业…

R语言如何接入实时行情接口

目录 1. 安装必要的R包 2. 导入库 3. 连接WebSocket 4. 处理连接成功后的操作 5. 处理接收到的消息 6. 处理连接关闭和错误 7. 发送心跳数据 8. 自动重连机制 9. 启动连接和重连 总结 在数据分析和金融研究中,实时行情数据的获取至关重要,但市…

Redis数据安全性分析

Redis高可用与数据安全机制深度解析前置知识:Redis基础安装与使用(主从复制、哨兵集群、Cluster集群搭建)一、Redis性能压测工具 工具名称:redis-benchmark核心作用:快速基准测试Redis性能使用场景:评估不同…

差分和前缀和

差分和前缀和的原理、用法和区别。前缀和(Prefix Sum)核心思想:预处理数组的前缀和,快速回答「区间和查询」 适用场景:数组静态(更新少、查询多),需要频繁计算任意区间的和1. 定义与…

C++并发编程-12. 用内存顺序实现内存模型

前情回顾 前文我们介绍了六种内存顺序,以及三种内存模型,本文通过代码示例讲解六种内存顺序使用方法,并实现相应的内存模型。全局一致性模型同步模型(获取和释放)松散模型memory_order_seq_cst memory_order_seq_cst代表全局一致性顺序&#…

AI测试革命:从智能缺陷检测到自愈式测试框架的工业实践

AI测试革命:从智能缺陷检测到自愈式测试框架的工业实践 希望对大家有用! 目录AI测试革命:从智能缺陷检测到自愈式测试框架的工业实践希望对大家有用!一、传统测试之殇:工业质检的切肤之痛二、智能缺陷检测系统架构1. …

二、深度学习——损失函数

二、损失函数损失函数定义:损失函数是用来衡量模型参数的质量的函数,衡量方式是比较网络输出和真实输出的差异别名:损失函数(loss function),代价函数(cost function),目…

面向数据报的套接字通道技术详解

数据报通道基础 通道特性与创建方式 java.nio.channels.DatagramChannel类实例代表数据报通道,默认处于阻塞模式。通过configureBlocking(false)方法可将其配置为非阻塞模式。创建数据报通道需调用其静态open()方法,若用于IP组播则需指定组播组的地址类型…

147.在 Vue3 中使用 OpenLayers 地图上 ECharts 模拟飞机循环飞行

🧩 效果预览 👇 飞机从多个城市起飞并向其他城市飞行,动画流畅,地图可缩放拖拽: 📦 一、项目技术栈 技术用途Vue 3现代前端框架OpenLayers地图底图渲染ECharts ol-echarts飞机飞行动画渲染ol-echarts将 …

OCR与PDF解析的区别

我们日常所接触的文档中,经常能碰到多语言混合的文档。比如论文试卷、财报研报、跨国票据都含有多种语言和文字。要将文档中的内容识别并提取务必需要使用到OCR技术,而传统的OCR工具在处理这类型文档的时候有局限性。早期的 OCR 系统识别精度有限&#x…

Java 单例类详解:从基础到高级,掌握线程安全与高效设计

作为一名Java开发工程师,你一定对**单例模式(Singleton Pattern)**不陌生。它是23种经典设计模式中最简单也是最常用的一种,用于确保一个类在整个应用程序中只有一个实例存在。单例广泛应用于系统配置、数据库连接池、日志管理器、…