Python+OpenCV双目视觉测距实战:从标定到三维重建全流程解析
2026/8/28 9:50:23 网站建设 项目流程

简介:双目立体视觉是计算机视觉中实现三维感知与深度测量的核心技术,其原理模仿人类双眼,通过计算同一场景在两个不同视角下的图像视差来恢复物体的三维信息。该技术基于对极几何与三角测量原理,核心价值在于能以被动、非接触的方式获取丰富的环境深度信息,为机器赋予“空间感知”能力。在工程实践中,完整的双目视觉系统通常遵循相机标定、立体校正、立体匹配与深度计算四大核心流程。其中,相机标定是确保测量精度的基石,需要精确获取相机的内参、外参及畸变系数;而立体匹配则是技术难点,需通过SGBM等算法在左右图像间寻找对应点以计算视差。这些技术广泛应用于机器人自主导航、避障、三维重建、AR/VR及工业检测等场景。本文将以一个基于Python和OpenCV的双目测距项目为例,深入剖析从摄像头选型、双目标定、参数调优到最终三维点云生成的全链路实践细节与避坑指南。

1. 项目缘起:从“看得见”到“测得出”的跨越

最近在整理硬盘,翻到了一个几年前做的老项目,一个基于Python和OpenCV的双目视觉测距系统。当时做这个的初衷挺简单的,就是想给一个机器人小车装上“眼睛”,让它能判断前方障碍物的距离,实现自主避障。市面上虽然有超声波、激光雷达这些现成的方案,但总觉得视觉方案更“酷”,信息也更丰富——毕竟人眼就是靠两个摄像头(眼睛)来判断距离的。于是,就一头扎进了双目视觉的世界。

这个项目打包成了一个python+opencv双目视觉测距源码+项目说明.zip,里面包含了完整的代码、标定用的图片、一个简单的说明文档,以及我踩过的无数个坑的记录。今天把它重新梳理出来,不只是分享代码,更重要的是把从摄像头选型、标定、匹配到最终测距这一整套流程背后的原理、实操细节和那些“教科书上不会写”的经验,掰开揉碎了讲清楚。无论你是想复现一个类似的测距应用,还是单纯对计算机视觉如何感知深度感兴趣,希望这篇近万字的“回忆录”能给你带来实实在在的帮助。

双目测距,听起来高大上,其实核心思想非常直观:模仿人的双眼。当我们看一个物体时,左眼和右眼看到的图像是有细微差别的,大脑就是根据这个差别(视差)来计算出物体离我们有多远。计算机要做的事情也一样:用两个摄像头模拟双眼,拍下两幅图像,找到同一个物体在两幅图中的位置差异,然后通过几何关系反推出距离。整个过程,可以粗略地分为“相机标定”、“立体校正”、“立体匹配”和“深度计算”四个核心环节。下面,我们就一个环节一个环节地拆解。

2. 双目视觉系统的“体检报告”:相机标定详解

在你指望两个摄像头能精准测距之前,必须给它们做一次全面的“体检”,这就是相机标定。标定的目的是确定相机的“内在特性”和“外在关系”。内在参数好比是相机的“身份证”,包括焦距、主点坐标、畸变系数等;而外在参数则是描述两个相机之间的相对位置关系(旋转和平移)。如果这些参数不准,后续的测距结果就会失之毫厘,谬以千里。

2.1 标定板:我们需要的“标准尺”

标定需要一个已知尺寸的、高对比度的图案作为参照物,最常用的就是棋盘格标定板。我项目中用的是9x6的棋盘格(内角点数量,即内部黑白格子相交的点)。选择这个尺寸是因为它比较常见,OpenCV的示例也多用这个。你需要将标定板打印出来,贴在一块平整的硬纸板或亚克力板上,确保它在拍摄时是刚性的,不会弯曲。

注意:打印的棋盘格尺寸一定要精确测量。我最初用普通A4纸打印,结果发现不同打印机的缩放比例有细微差异,导致标定的焦距误差很大。后来改用PDF文件,并确保打印设置里“缩放”选项为“无”,同时用游标卡尺测量了单个方格的实际边长(比如我设定为30mm),并在代码中准确输入这个值。这个物理尺寸是连接像素世界和真实世界的唯一桥梁。

2.2 采集标定图像:多角度、全覆盖

采集图像是体力活,也是技术活。你需要用双目相机从不同角度、不同距离、不同姿态拍摄几十对(建议15-20对以上)棋盘格图像。关键要点:

  1. 同时性:确保左右相机是同时曝光的,或者时间差极小。如果相机不支持硬件触发,就用软件尽量同步采集。我用的USB摄像头,就用cv2.VideoCapture(0)cv2.VideoCapture(1)同时读帧,虽然仍有毫秒级延迟,但对于静态标定板影响不大。
  2. 覆盖整个视野:让标定板出现在图像的各个位置(四个角落、中心),并呈现不同的倾斜和旋转角度。这有助于标定算法更全面地估算畸变。
  3. 清晰度:图像必须清晰,角点不能模糊。光照要均匀,避免反光或阴影遮盖角点。
  4. 留出边界:棋盘格不能太贴近图像边缘,因为边缘的畸变通常最大,角点检测容易出错。

在我的项目文件夹里,你会找到一个calib_imgs目录,里面存放了大约25对这样的图像。每对图像都以left_001.jpg,right_001.jpg这样的格式命名。

2.3 角点检测与参数计算:OpenCV的核心函数

标定的代码实现,OpenCV已经为我们封装好了强大的函数。核心步骤如下:

import numpy as np import cv2 import glob # 准备对象点:真实世界中的3D点 (0,0,0), (1,0,0), (2,0,0) ....,(8,5,0) objp = np.zeros((6*9, 3), np.float32) objp[:,:2] = np.mgrid[0:9, 0:6].T.reshape(-1, 2) objp *= square_size # 乘上棋盘格实际物理尺寸,例如0.03代表30mm # 用于存储所有图像的对象点和图像点 objpoints = [] # 3d点 in real world space imgpoints_left = [] # 2d点 in image plane. imgpoints_right = [] # 读取左右相机图像对 images_left = sorted(glob.glob('path/to/left*.jpg')) images_right = sorted(glob.glob('path/to/right*.jpg')) for fname_left, fname_right in zip(images_left, images_right): img_l = cv2.imread(fname_left) img_r = cv2.imread(fname_right) gray_l = cv2.cvtColor(img_l, cv2.COLOR_BGR2GRAY) gray_r = cv2.cvtColor(img_r, cv2.COLOR_BGR2GRAY) # 查找棋盘格角点 ret_l, corners_l = cv2.findChessboardCorners(gray_l, (9,6), None) ret_r, corners_r = cv2.findChessboardCorners(gray_r, (9,6), None) if ret_l and ret_r: # 亚像素级角点精确化 criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) corners_l_refined = cv2.cornerSubPix(gray_l, corners_l, (11,11), (-1,-1), criteria) corners_r_refined = cv2.cornerSubPix(gray_r, corners_r, (11,11), (-1,-1), criteria) objpoints.append(objp) imgpoints_left.append(corners_l_refined) imgpoints_right.append(corners_r_refined) # 可视化(可选) cv2.drawChessboardCorners(img_l, (9,6), corners_l_refined, ret_l) cv2.imshow('Left Corners', img_l) cv2.waitKey(500) # 单目标定:获取每个相机的内参和畸变系数 ret_l, mtx_l, dist_l, rvecs_l, tvecs_l = cv2.calibrateCamera(objpoints, imgpoints_left, gray_l.shape[::-1], None, None) ret_r, mtx_r, dist_r, rvecs_l, tvecs_l = cv2.calibrateCamera(objpoints, imgpoints_right, gray_r.shape[::-1], None, None) # 双目标定:获取两个相机之间的旋转和平移矩阵 flags = cv2.CALIB_FIX_INTRINSIC # 使用上面单目标定的结果 retval, cameraMatrix1, distCoeffs1, cameraMatrix2, distCoeffs2, R, T, E, F = cv2.stereoCalibrate( objpoints, imgpoints_left, imgpoints_right, mtx_l, dist_l, mtx_r, dist_r, gray_l.shape[::-1], flags=flags )

这段代码做了几件关键事:

  1. findChessboardCorners:在图像中寻找棋盘格角点。
  2. cornerSubPix:将角点位置精确到亚像素级别,这是提高标定精度的关键一步。
  3. calibrateCamera:进行单目标定,得到每个相机的内参矩阵mtx(包含焦距fx, fy和主点cx, cy)和畸变系数dist
  4. stereoCalibrate:进行双目标定,得到右相机相对于左相机的旋转矩阵R和平移向量T。这个T向量的第一个分量(通常是Tx)的绝对值,就是两个相机光心之间的水平距离,也就是基线长度(Baseline),它是后续测距公式中的核心参数。

标定完成后,务必将这些参数(cameraMatrix1, distCoeffs1, cameraMatrix2, distCoeffs2, R, T)保存下来(比如用np.save),后续所有步骤都要用到。我项目中的calibration_params.npz文件就是这样生成的。

3. 立体校正:将“歪头”的相机视图对齐

拿到标定参数后,我们知道了两个相机并不完美平行,它们可能有点“歪头”(存在旋转R)。直接在这样的原始图像上寻找对应点非常困难。立体校正的目的,就是对两幅图像进行透视变换,使得它们看起来就像是由两个完全平行放置的理想相机拍摄的一样。校正后,同一个空间点在两幅图像中的纵坐标(v坐标)将完全相同,匹配问题从二维搜索简化为一维搜索(只需在同一行上搜索)。

OpenCV提供了cv2.stereoRectifycv2.initUndistortRectifyMap来完成这个工作。

# 立体校正 R1, R2, P1, P2, Q, validPixROI1, validPixROI2 = cv2.stereoRectify( cameraMatrix1, distCoeffs1, cameraMatrix2, distCoeffs2, image_size, # 图像尺寸,例如 (640, 480) R, T, alpha=0 # 这个参数很重要! ) # 计算校正映射表 map1x, map1y = cv2.initUndistortRectifyMap(cameraMatrix1, distCoeffs1, R1, P1, image_size, cv2.CV_32FC1) map2x, map2y = cv2.initUndistortRectifyMap(cameraMatrix2, distCoeffs2, R2, P2, image_size, cv2.CV_32FC1) # 对图像进行校正(在实际视频流中,对每一帧进行此操作) img_left_rectified = cv2.remap(img_left, map1x, map1y, cv2.INTER_LINEAR) img_right_rectified = cv2.remap(img_right, map2x, map2y, cv2.INTER_LINEAR)

这里有几个关键点:

  • alpha参数:这是立体校正中最容易踩坑的地方。alpha=0表示校正后图像会被裁剪掉所有无有效像素的区域(黑边),图像尺寸不变,但内容可能丢失一部分。alpha=1表示保留所有原始像素,但会引入大量黑边。alpha=-1表示自动缩放和平移图像以尽量保留有效区域。我通常从0-1开始尝试,并通过观察校正后的图像对和validPixROI(有效像素区域)来选择。对于后续的匹配,通常希望黑边越少越好,所以项目中我选择了alpha=0
  • 映射表(Map)initUndistortRectifyMap计算出的映射表是一次性的。在校正阶段计算一次后保存下来,在后续处理每一帧图像时,直接使用cv2.remap进行重映射,这比每帧都重新计算要高效得多。
  • 效果验证:校正后,你可以画一些水平线贯穿左右图,看看对应的特征点是否基本在同一水平线上。项目中的check_rectification.py脚本就做了这个可视化。

4. 立体匹配:在“对齐”的图像中寻找对应点

这是双目视觉中最核心、最复杂的一步。目标:对于左图校正图像中的每一个像素,在右图校正图像的同一行上,找到与之对应的像素点。这两个像素点的横坐标之差,就是视差(Disparity)

4.1 匹配算法概览:从局部到全局

OpenCV主要提供了两种类型的立体匹配算法:

  1. 局部块匹配(Block Matching, BM)cv2.StereoBM_create。为左图每个像素,在右图对应行上的一定范围内,用一个固定大小的窗口(如5x5,7x7)计算窗口内像素的相似度(如SAD,绝对差和),选择相似度最高的位置作为匹配点。速度快,但对纹理稀疏、重复区域效果差,容易产生“斑点状”噪声。
  2. 半全局块匹配(Semi-Global Block Matching, SGBM)cv2.StereoSGBM_create。在BM的基础上,引入了一维路径上的动态规划约束,通过多个方向上的代价聚合来优化视差图,效果比BM好很多,是当前的主流选择。我的项目采用的就是SGBM。

4.2 SGBM参数调优:一场平衡艺术

SGBM有一大堆参数,调参过程堪比炼丹。下面结合代码解释关键参数:

# 创建SGBM对象 window_size = 5 min_disp = 0 num_disp = 16 * 5 # 最大视差 - 最小视差,必须是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, # 控制视差平滑度的参数,P2 > P1 disp12MaxDiff = 1, uniquenessRatio = 15, # 唯一性比率,用于后处理 speckleWindowSize = 100, # 过滤小连通区域的窗口大小 speckleRange = 32, # 连通区域内的视差变化阈值 preFilterCap = 63, mode = cv2.STEREO_SGBM_MODE_SGBM_3WAY ) # 计算视差图(输入必须是8位单通道灰度图) gray_left = cv2.cvtColor(img_left_rectified, cv2.COLOR_BGR2GRAY) gray_right = cv2.cvtColor(img_right_rectified, cv2.COLOR_BGR2GRAY) disparity = stereo.compute(gray_left, gray_right).astype(np.float32) / 16.0 # SGBM返回的视差图需要除以16得到真实视差

参数详解与调优心得:

  • numDisparities(视差搜索范围):这是最重要的参数。numDisparities = max_disparity - min_disparity。它决定了算法搜索的宽度。设得太小,远处的物体可能找不到匹配(视差小);设得太大,计算量增加且可能引入噪声。一般根据基线长度和最近测距距离估算。一个经验公式:max_disparity ≈ (focal_length * baseline) / min_distance。在项目中,我通过试验,将其设为80(即16*5),对于我的相机配置和1米到5米的测距范围比较合适。
  • blockSize(窗口大小):奇数,如3,5,7。窗口越大,对纹理平滑区域越友好,但会损失边缘细节。窗口越小,细节保持越好,但对噪声更敏感。通常从5开始尝试。
  • P1,P2(平滑约束参数):这两个参数控制视差图的平滑度。P2通常远大于P1(3-4倍)。它们与窗口大小有关,常见的启发式设置是P1 = 8*chn*window_size^2,P2 = 32*chn*window_size^2chn是图像通道数,灰度图为1)。增大它们会使视差图更平滑,但可能模糊物体边界。
  • uniquenessRatio(唯一性比率):后处理参数。如果最佳匹配的代价与次佳匹配的代价相差不大(小于uniquenessRatio),则认为该点匹配不可靠,将其视差置为无效。通常设置在5-15之间,可以有效过滤掉一些模糊区域的错误匹配。
  • speckleWindowSizespeckleRange(斑点滤波器):用于过滤视差图中小的、孤立的噪声块(斑点)。speckleWindowSize是判断连通区域大小的阈值,小于此值的区域会被过滤掉。speckleRange是判断连通区域内视差是否连续变化的阈值。这两个参数对于生成干净的视差图非常有效。

实操心得:调参没有银弹。最好的方法是固定一个场景(有远有近,有纹理丰富和平滑的区域),写一个简单的GUI滑块程序,实时调整参数并观察视差图的变化。你会直观地看到每个参数是如何影响结果的。我的项目里有一个tune_sgbm_params.py脚本,就是干这个用的。

4.3 视差图后处理:从粗糙到精细

直接计算出的视差图往往充满噪声和无效值(特别是遮挡区域、无纹理区域)。常见的后处理包括:

  • 空洞填充:对于无效的视差点,可以用其周围有效视差的均值或中值来填充。OpenCV的cv2.filterSpeckles就是一种方法。
  • 视差滤波:使用中值滤波(cv2.medianBlur)或双边滤波(cv2.bilateralFilter)来平滑视差图,同时保留边缘。双边滤波效果通常更好,但速度慢。
  • 左右一致性检查(L-R Check):这是一个非常有效的后处理步骤。原理是:用左图作为参考图得到的视差图,和用右图作为参考图得到的视差图应该是一致的。如果不一致,说明该点匹配不可靠。这可以剔除很多遮挡区域的错误匹配。OpenCV的cv2.StereoMatcher(BM/SGBM的基类)有getDisparityVis等方法,但实现完整的L-R Check需要自己计算右视差图并进行比较。我在项目中实现了一个简单的版本,效果提升显著。
# 简单的中值滤波和空洞填充示例 disparity_filtered = cv2.medianBlur(disparity, 5) # 创建一个掩膜,标识无效视差(通常SGBM计算无效视差为负值或极小值) mask = disparity_filtered > min_disp # 使用形态学操作(如闭运算)帮助填充小空洞 kernel = np.ones((5,5), np.uint8) mask_closed = cv2.morphologyEx(mask.astype(np.uint8), cv2.MORPH_CLOSE, kernel) # 对无效区域进行填充(例如,使用最近邻有效视差) # 这里可以使用cv2.inpaint,但更简单的是用周围像素的平均值 # 实际项目中,我使用了更复杂的基于扫描线的填充算法。

经过后处理的视差图,质量会有质的提升,为下一步的深度计算打下坚实基础。

5. 深度计算与三维重建:从像素到真实世界

我们千辛万苦得到了视差图d(单位:像素),现在终于可以计算深度Z(单位:与标定板一致的物理单位,如毫米)了。公式出奇地简单:

Z = (f * B) / d

其中:

  • f:相机的焦距(像素单位),从内参矩阵mtx[0,0]mtx[1,1]取得(通常两者接近,取fx即可)。
  • B:基线长度,即双目标定得到的平移向量T的第一个分量的绝对值|Tx|(单位:物理单位,如毫米)。
  • d:视差(像素)。

这个公式的推导基于相似三角形原理,前提是立体校正后两个相机光轴平行。OpenCV的stereoRectify函数返回的Q矩阵(重投影矩阵)已经封装了这个变换。我们可以直接使用cv2.reprojectImageTo3D函数,将视差图转换为三维点云。

# 使用Q矩阵进行重投影 points_3d = cv2.reprojectImageTo3D(disparity, Q) # points_3d是一个与图像同尺寸的3通道矩阵, (X, Y, Z) # 提取深度图(Z通道) depth_map = points_3d[:, :, 2] # 过滤无效深度(例如,视差为0或负值对应的深度无穷大或无效) depth_map[disparity <= min_disp] = 0 # 或一个很大的值,如10000 # 现在,depth_map中每个像素的值就代表了该点距离相机的实际距离。 # 例如,你想知道图像中心点的距离: h, w = depth_map.shape center_depth = depth_map[h//2, w//2] if center_depth > 0 and center_depth < 10000: # 有效范围判断 print(f"中心点距离约为:{center_depth:.1f} mm")

重要细节:

  1. 单位一致性:确保f(像素)、B(物理单位)和Z(物理单位)的单位匹配。如果你的标定板方格尺寸用的是毫米,B就是毫米,计算出的Z也是毫米。
  2. Q矩阵stereoRectify输出的Q矩阵是一个4x4的透视变换矩阵,它已经包含了f,B,cx,cy等所有信息。使用它是最方便准确的方式。
  3. 无效值处理:视差为0或负值(在SGBM中,无效点可能被设为minDisparity - 1)会导致深度计算为无穷大或负值,必须过滤掉。
  4. 精度与范围:从公式可以看出,深度Z与视差d成反比。这意味着:
    • 距离越近,视差越大,深度计算相对越准
    • 距离越远,视差越小(趋近于0),深度计算对噪声极其敏感,误差会呈平方级放大。因此,双目测距的有效范围有一个上限,通常由基线长度B和相机分辨率决定。B越大,能测的距离越远,但近距离盲区也越大(因为物体可能只出现在一个相机视野中)。

在我的项目中,基线B大约是120mm,相机焦距f约700像素,对于1米到4米范围内的物体,测距误差可以控制在5%以内。但对于5米以外的物体,误差就变得不可接受了。

6. 项目实战:代码结构与避坑指南

我的项目源码结构如下:

stereo_vision_distance/ ├── calibrate.py # 双目标定脚本 ├── calibrate_params.npz # 保存的标定参数文件 ├── rectify_and_match.py # 立体校正与匹配主程序 ├── utils/ │ ├── stereo_rectifier.py # 立体校正类封装 │ ├ disparity_postprocess.py # 视差图后处理函数 │ └── visualization.py # 可视化工具函数 ├── data/ │ ├── calib_imgs/ # 标定图像 │ └── test_imgs/ # 测试图像 └── requirements.txt # Python依赖

6.1 核心流程串联

主程序rectify_and_match.py的流程清晰体现了上述所有步骤:

# 1. 加载标定参数 calib_data = np.load('calibrate_params.npz') mtx1, dist1, mtx2, dist2, R, T = ... # 从calib_data中读取 # 2. 初始化立体校正器(预计算映射表) rectifier = StereoRectifier(mtx1, dist1, mtx2, dist2, R, T, image_size) rectifier.compute_rectify_maps(alpha=0) # 3. 初始化SGBM匹配器(参数已调优) stereo_matcher = create_sgbm_matcher(num_disp=80, block_size=5) # 4. 打开双目摄像头或读取测试图像对 cap_left = cv2.VideoCapture(0) cap_right = cv2.VideoCapture(1) while True: ret_l, frame_l = cap_left.read() ret_r, frame_r = cap_right.read() if not (ret_l and ret_r): break # 5. 立体校正 frame_l_rect, frame_r_rect = rectifier.rectify(frame_l, frame_r) # 6. 立体匹配(计算视差) disparity = stereo_matcher.compute(frame_l_rect, frame_r_rect) # 7. 视差图后处理 disparity_processed = postprocess_disparity(disparity) # 8. 计算深度图 depth_map = cv2.reprojectImageTo3D(disparity_processed, rectifier.Q) depth_map = depth_map[:, :, 2] # 取Z通道 # 9. 可视化 # 显示校正后的图像对 # 显示视差图(归一化到0-255以便显示) # 在左图上标注特定点的深度值 # ... if cv2.waitKey(1) & 0xFF == ord('q'): break

6.2 避坑经验与性能优化

  1. 摄像头同步是老大难:如果使用两个独立的USB摄像头,帧率不同步和曝光差异会导致匹配困难。解决方案:

    • 硬件同步:使用支持硬件触发(Trigger)的工业相机,这是最好的方案。
    • 软件逼近:尽量缩短两次grab()retrieve()之间的时间间隔。可以尝试先grab()所有相机,再统一retrieve()。在我的代码中,我使用了多线程,一个线程负责从两个摄像头抓取帧并放入队列,另一个线程从队列中取出成对的帧进行处理,这在一定程度上缓解了问题。
    • 曝光锁定:如果相机支持,通过OpenCV的CAP_PROP_AUTO_EXPOSURE等属性将曝光、白平衡等参数设为固定值,避免左右图亮度差异过大。
  2. 标定质量决定上限:如果标定不准,后面所有步骤都是徒劳。务必:

    • 使用足够多(>15对)、高质量的标定图像。
    • 仔细测量棋盘格物理尺寸。
    • 检查标定重投影误差(stereoCalibrate的返回值)。我的项目里误差控制在0.2像素以下。
  3. SGBM参数调优是持久战:没有一套参数放之四海而皆准。针对你的场景(室内/室外、纹理丰富/稀疏、测距范围)需要重新调整。务必编写实时调参工具辅助。

  4. 计算资源与实时性:SGBM计算量较大,在树莓派等嵌入式设备上很难达到实时(>15fps)。优化策略:

    • 降低分辨率:将图像缩放至320x240或更低,能极大提升速度,但会损失精度和有效测距范围。
    • 限制视差搜索范围(numDisparities):这是最大的性能瓶颈,在满足测距需求的前提下尽可能设小。
    • 使用CUDA加速:如果你有NVIDIA GPU,可以使用OpenCV的cuda模块中的cv2.cuda.StereoSGMcv2.cuda.StereoBM,速度能有数量级的提升。我的项目提供了CPU和CUDA两个版本的匹配器可选。
  5. 深度图的有效区域:立体校正后,图像左右两侧会出现黑边(无有效匹配区域),这些区域的深度值是无效的。在应用深度信息时(如机器人避障),需要忽略这些区域。可以通过validPixROI参数获取有效区域矩形。

7. 扩展思考:从测距到应用

得到可靠的深度图后,它的应用就非常广泛了,远不止显示一个数字。在我的机器人小车项目里,我基于深度图做了以下几件事:

  1. 障碍物检测与地图构建:将深度图转换为二维的“高度图”或“障碍物栅格图”。设定一个高度阈值,高于地面的点被认为是障碍物。可以进一步结合机器人位置,构建一个简单的局部占据栅格地图,用于路径规划。
  2. 目标跟踪与测距:结合目标检测算法(如YOLO),先框出图像中的特定物体(比如人),然后计算该物体框内所有有效深度点的中值或均值,作为该物体的距离。这比单点测距更稳定。
  3. 点云可视化:使用Open3DPCL库可以将reprojectImageTo3D得到的点云进行可视化,非常直观。你可以旋转、缩放这个三维模型,感受双目视觉的魔力。

这个项目虽然基础,但它完整地走通了双目视觉从理论到实践的闭环。它让我深刻体会到,在计算机视觉中,理论公式的优雅和工程实现的琐碎之间存在着巨大的鸿沟。每一个参数、每一个预处理步骤、每一个后处理技巧,都可能对最终结果产生决定性的影响。

最后,源码和说明文档都在那个zip包里。我建议你不要直接运行,而是跟着这篇文章的步骤,自己动手从标定开始做一遍。过程中遇到的每一个报错、每一个不理想的结果,都是最宝贵的学习材料。双目视觉的门槛不低,但一旦跨过去,你会发现它为机器人、AR/VR、三维重建等领域打开了一扇全新的大门。祝你调试顺利。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询