免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

双目视觉三维重建:Matlab标定、匹配与点云生成全流程解析

双目视觉三维重建:Matlab标定、匹配与点云生成全流程解析 简介基于双目立体视觉原理在MATLAB中实现三维重建的完整源码包面向计算机视觉学习者、研究生及有项目开发需求的工程师解决从二维图像恢复物体三维几何信息的问题。资源覆盖双目视觉系统标定、图像预处理、特征检测与匹配、视差计算、深度恢复及点云生成等关键环节对应了SIFT/SURF特征点提取、RANSAC剔除误匹配、三角测量等核心方法。压缩包共22个文件以10张JPG测试图、5个M脚本文件、4张PNG图像为主附带readme说明与license授权文本整体大小约1018KB其中M脚本涵盖主程序main.m和相关功能函数图片素材可供直接运行演示。目前已有1495人学习下载。通过本资源可以直观理解双目三维重建的完整实现流程掌握在MATLAB中调用图像处理和点云显示函数的方法还能基于现有代码进行算法调优或扩展到自己的实验数据尤其适合毕业设计、课程项目以及三维视觉入门实践。1. 双目视觉三维重建在 Matlab 里到底在做什么如果你手头只有一个普通的 USB 摄像头却想拿到物体的真实三维坐标双目视觉是成本最低的入门路径。两颗摄像头从不同角度观察同一场景利用视差disparity反推深度再结合相机内外参数把像素坐标映射到世界坐标系这就是标题里“基于双目视觉的三维重建”的全部内容。Matlab 在这条链路里的优势在于标定、校正、匹配、三角化都有现成函数不用像 OpenCV 那样自己拼凑模块但代价是参数细节藏在工具箱背后默认值往往不能直接用于你的实际场景。这篇文章面向的读者是已经跑通单目图像处理、想转向三维视觉的工程师或者是正在做 Matlab 课程设计、需要把双目重建从“能出点云”推进到“点云能对齐、能测距”的学生。我不会只贴一份能跑的脚本而是把标定、极线校正、立体匹配、三角化这条链路上每个环节的参数为什么这么设、设错了会看到什么现象讲清楚。阅读前你只需要装好 Matlab R2020b 以上版本并确认 Computer Vision Toolbox 已安装——检查方法是运行ver命令看输出列表中是否有 Computer Vision Toolbox 字样。2. 标定是双目重建的地基从单目标定到 stereoParameters2.1 双目系统里标定到底标了什么很多人以为标定就是拿到内参矩阵 K 和畸变系数但双目重建还需要两个额外的量两个相机坐标系之间的旋转矩阵 R 和平移向量 T。R 和 T 决定了左右相机光轴的相对朝向如果这个关系不准确后面的极线校正和三角化都会在系统层面引入误差。Matlab 里用estimateCameraParameters可以同时处理单目和双目标定输入是左右相机各自拍摄的棋盘格图像对输出是一个stereoParameters对象里面封装了内参、畸变、R、T 以及校正所需的投影矩阵。实际操作中我建议你不要跳过单目标定、直接跑双目标定。先用cameraCalibratorApp 或者命令行方式分别标定左右相机检查两边的重投影误差是否都小于 0.5 像素。如果某一个相机单独标定就超过了 1 个像素说明标定板图像质量有问题、拍糊了或者棋盘格边角被截断这时候继续做双目标定只会把误差叠加进 R 和 T。双目标定本身会重新优化所有参数而不是简单拼接两个单目结果但初始值的好坏直接影响优化是否能收敛到全局最优。2.2 拍标定板的三个关键约束标定板不是随便拍 20 张就能用的。我的一般做法是棋盘格贴在硬质平板上确保板面没有弯曲左右相机同时拍摄保证每一对图像里棋盘格完整可见标定板在画面中要覆盖不同的位置、角度和距离尤其是四个角落都要出现。这里有一个经常被忽略的点标定板的位姿变化要足够“大”如果所有图像里棋盘格都位于画面中央且角度几乎不变那么优化问题会退化为病态你得到的内参看起来合理实际上外参和畸变都很不稳定。% 加载左右图像对 leftImages imageDatastore(fullfile(data, left)); rightImages imageDatastore(fullfile(data, right)); % 检测棋盘格角点 [imagePoints, boardSize] detectCheckerboardPoints(leftImages.Files, rightImages.Files); % 生成世界坐标系的棋盘格角点坐标 worldPoints generateCheckerboardPoints(boardSize, 15); % 15mm 方格边长 % 计算双目相机参数 stereoParams estimateCameraParameters(imagePoints, worldPoints, ... NumRadialDistortionCoefficients, 2, ... EstimateTangentialDistortion, false); % 查看标定精度 fprintf(重投影误差: %.3f 像素\n, stereoParams.MeanReprojectionError);代码里的NumRadialDistortionCoefficients设为 2 表示使用两个径向畸变系数k1、k2这是大多数工业镜头的合理选择如果使用广角镜头或者画面边缘畸变非常明显可以改成 3。EstimateTangentialDistortion默认是 true但对精密装配的镜头来说切向畸变通常很小把它关掉可以减少参数量、提高数值稳定性。worldPoints的坐标单位是毫米这里 15 是棋盘格单个方格的边长必须与你实际使用的棋盘格一致否则重建出来的点云尺度会整体偏大或偏小。2.3 标定结果怎么判断好坏一个常见的误区是只看重投影误差觉得小于 0.3 像素就万事大吉。重投影误差只反映“标定板角点在图像上的拟合程度”但不能直接反映 R 和 T 的精度。建议你额外做两个检查一是用showExtrinsics可视化相机和标定板的相对位置看看标定板是否分布在相机前方合理的扇形区域内二是固定双目相机不动重新拍一组验证图像不是标定用的那组用rectifyStereoImages做校正观察校正后左右图像的同一特征点是否位于同一水平线上。后者是端到端的验证比单独看标定误差更靠谱。如果在验证时发现校正后的图像有明显上下错位排查顺序是先看左右相机安装是否基本平行再看stereoParams.RotationOfCamera2的旋转角度是否在合理范围。两个相机光轴夹角过大时Matlab 的校正虽然还能工作但视差搜索范围会变宽、匹配歧义增加重建精度急剧下降。一般建议光轴夹角控制在 5 度以内基线距离根据场景深度来选近距离重建用 5-10cm 基线远距离场景则需要拉开到 20cm 以上。3. 从极线校正到视差图立体匹配的参数博弈3.1 极线校正让匹配从二维降为一维极线校正的数学原理是通过旋转两个相机坐标系使左右图像平面共面且行对齐这样左图像上的任意一个像素在右图像上只可能出现在同一行。Matlab 里rectifyStereoImages一步完成这项工作输入是原始图像对和stereoParams输出是校正后的图像对和用于三角化的投影矩阵stereoParams.CameraParameters1与CameraParameters2。建议你把校正后的图像保存下来看一眼确认左右图的相同物体确实在水平方向上对齐。这个步骤不要跳过因为立体匹配stereo matching完全依赖于极线约束的准确性。如果校正后还有可感知的竖直偏移问题多半出在标定阶段此时回去检查标定板图像集而不是试图在匹配阶段补偿。% 校正左右图像 [leftRect, rightRect] rectifyStereoImages(Ileft, Iright, stereoParams, ... OutputView, valid); % 转为灰度图视差计算只需要灰度信息 leftGray im2gray(leftRect); rightGray im2gray(rightRect);参数说明OutputView有两个取值valid会裁剪掉校正后图像中的空白区域即只有两个视图重叠的部分计算量小但视野会缩小full保留完整图像视野大但边缘会出现大量无匹配区域。我做三维重建时优先用valid因为视差图边缘的无效值会直接污染后续的三维点云比较视野大小可以用OutputView设为full时的边界框信息。3.2 disparityBM 还是 disparitySGM速度和精度的选择Matlab 提供两种立体匹配算法disparityBMBlock Matching块匹配和disparitySGMSemi-Global Matching半全局匹配。BM 的原理是对每个像素在右图同一行上滑动一个固定大小的窗口计算窗口内灰度差的绝对值之和SAD取最小值对应的偏移量作为视差。BM 速度快但窗口大小决定了深度图的平滑度窗口太小纹理稀疏区域会出现大量噪点窗口太大物体边缘会被过度平滑形成“圆角”效应。SGM 在 BM 的基础上增加了多方向路径代价聚合简单说就是不仅看当前像素的匹配代价还把左右上下多个方向的平滑约束加进来所以对弱纹理区域的处理明显更好代价是计算时间大约是 BM 的 10 到 50 倍。我的选择标准是如果是实时性要求高的场景用 BM 配合中值滤波如果追求重建质量、对时间不敏感直接用 SGM默认参数已经比 BM 的最佳参数好不少。% 使用 SGM 计算视差图 disparityMap disparitySGM(leftGray, rightGray, ... DisparityRange, [0 64], ... UniquenessThreshold, 15, ... BlockSize, 9); % 或者使用 BM速度更快适合调试 % disparityMap disparityBM(leftGray, rightGray, ... % DisparityRange, [0 64], ... % BlockSize, 15, ... % UniquenessThreshold, 10);DisparityRange是最关键的参数它决定了算法搜索视差的上下界。视差范围过大会增加误匹配概率过小则近处的物体无法正确重建。一个经验公式是最小视差对应最远深度最大视差对应最近深度。假如你的场景最近距离是 300mm相机焦距是 600 像素基线是 80mm那么最大视差 600 × 80 / 300 160所以DisparityRange的上界至少要设到 160。注意视差值和深度成反比所以视差图的像素值越大代表物体越近。3.3 匹配参数调优哪些参数值得先动UniquenessThreshold控制左右匹配的唯一性数值越大要求匹配代价的最小值比其他候选值显著多少才能被认为有效。SGM 默认是 5实际使用中如果视差图出现大片“空洞”黑色无效值可以适当调低到 2-3如果视差图看起来噪声很大、出现大量错误匹配的小碎片则调高到 15-20。BlockSize要满足奇数的要求SGM 的块大小一般取 5 到 15 之间。块太小对噪声敏感块太大则边缘模糊。另一个容易被忽视的参数是ContrastThreshold它用于区分图像中的纹理区域和均匀区域。在 SGM 里均匀区域的匹配代价聚合会被抑制以避免在墙壁、地面这类低纹理区域产生随机视差。如果你的场景中恰好有大面积的白色墙面且墙面重建出来后表面凹凸不平可以尝试调高这个阈值让算法避开这些区域宁可输出无效值也不要错误的深度。4. 从视差图到三维点云三角化与坐标映射4.1 深度和视差的关系不是简单的除法视差图只给出了左右图像的对应关系严格来说还不是深度图。在理想的平行双目系统中深度 Z 满足 Z f × B / d其中 f 是焦距像素单位B 是基线长度d 是视差。但实际标定后的双目系统并不完全是理想平行结构Matlab 的reconstructScene函数通过stereoParams里的投影矩阵直接完成从视差到三维坐标的映射这个映射已经包含了旋转和平移带来的修正所以不建议跳过它手动用 Z fB/d 去算。在动手之前先把视差图的后处理做掉。直接用原始视差图重建点云你会发现点云里布满飞点——这些是在匹配错误的位置产生的孤立三维点坐标通常远偏离真实表面。常见的做法是对视差图做中值滤波然后根据对象属性过滤掉无效值。% 对视差图做中值滤波消除孤立噪声点 disparityMap medfilt2(disparityMap, [5 5]); % 将视差图映射到三维点云 pointCloud3D reconstructScene(disparityMap, stereoParams); % pointCloud3D 是 HxWx3 的矩阵三通道分别是 X, Y, Z 坐标单位与标定板一致 % 转换成 pointCloud 对象便于后续处理和可视化 ptCloud pointCloud(pointCloud3D, Color, leftRect); % 剔除无效点和坐标异常的点 validIdx ~isnan(pointCloud3D(:, :, 1)) ... ~isinf(pointCloud3D(:, :, 1)) ... pointCloud3D(:, :, 3) 0 ... pointCloud3D(:, :, 3) 5000; % 最大深度 5000mm ptCloud select(ptCloud, find(validIdx)); % 可视化点云 pcshow(ptCloud);这里的核心是reconstructScene的输入要求。它接收的视差图必须是int16类型其中无效像素值为-32768而不是 NaN 或者 0。如果你直接传入 double 类型的视差图Matlab 会把它当作像素单位的小数视差处理结果整个点云坐标都会偏移。我在实践中发现用disparitySGM输出的视差图已经是 int16但如果之前做了medfilt2输出会变成 double此时需要手动转换并重设无效值。4.2 点云后处理剔除飞点和统计滤波reconstructScene输出的点云包含三类噪声匹配错误导致的孤立点、深度超出传感器范围的远点、以及物体边缘的“连接点”——这类点位于前景和背景之间坐标被插值到了错误的中间深度。处理这些点的顺序很重要先用深度范围过滤掉明显不合理的远点再用统计滤波剔除孤立的离群点。统计滤波的思路是对每个点计算它到 K 个最近邻居的平均距离如果这个平均距离大于整体均值加上若干倍标准差就把这个点判为离群点。Matlab 的pcdenoise内置了这一逻辑NumNeighbors默认设为 4 到 8 之间Threshold默认是 1.0表示 1 倍标准差。对于双目重建的点云我会把Threshold调高到 2.0 左右因为双目点云的噪声分布比激光雷达点云更重太严格的阈值会误删真实表面上的点。% 统计滤波去除离群点 ptCloudDenoised pcdenoise(ptCloud, ... NumNeighbors, 6, ... Threshold, 2.0); % 可选均匀下采样减少数据量加速后续处理 ptCloudDownsampled pcdownsample(ptCloudDenoised, gridAverage, 1.0);gridAverage下采样以 1.0mm 为体素栅格大小每个栅格里只保留一个点取平均值这样可以把几十万点的点云压缩到几万点而几何细节基本不损失。体素大小要根据重建物体的尺寸来定如果重建一个拳头大小的物体1mm 体素合适如果要重建整面墙体素可以放到 10mm。注意下采样应该在滤波之后做否则下采样会掩盖离群点的特征让滤波算法失效。4.3 三维坐标的单位与场景尺度问题标定阶段generateCheckerboardPoints(boardSize, 15)里的 15 决定了世界坐标系的单位是毫米因此reconstructScene输出的 X、Y、Z 坐标单位也是毫米。如果你的棋盘格方格边长是 30mm这里就要写 30整个点云的尺度会自动放大一倍。这个问题看起来简单但实际项目里很多同事会在不同标定板上混用导致点云和真实物体对不上。验证尺度是否正确的办法是找两个特征点之间的直线距离和实际测量值对比。一个简单的做法是在场景里放一个已知长度的物体比如 200mm 长的直尺重建后取直尺两端对应的三维坐标计算欧氏距离。如果偏差超过 5%优先检查方格边长是否填对其次是视差范围是否截断了近处或远处的目标。5. 重建质量验证与故障排查从视差图反推标定和参数问题最后一章处理实际项目中必然会遇到的验证和排错问题。好的习惯是不要等完整重建完后才发现效果差而是每一步都留一个快速验证手段。这里我给出三个最常用的验证方法和一个实用的调参技巧。验证方法一重投影误差的前后端对比。标定完成后记录MeanReprojectionError这是前端误差。重建完成后用pcshow观察点云表面是否存在周期性波纹或带状条纹。如果出现这种条纹通常意味着视差图在某个深度范围内存在固定的量化误差或者是极线校正没有完全对齐。此时回到第 3 章的校正结果在左右校正图中手动选取一个远处的特征点比较它的行列坐标。列坐标差就是视差行坐标差应该严格为 0如果行坐标差超过 1 个像素重做标定不要通过加大BlockSize去掩盖问题。验证方法二平面拟合残差。在场景中放一块平整的硬质平面比如瓷砖或亚克力板重建后框选平面所在的点云区域用pcfitplane拟合平面查看拟合残差即点到拟合平面的距离均方根误差。对于 500mm 左右的拍摄距离残差小于 1mm 说明重建质量很好1-3mm 说明参数基本合理可以接受大于 5mm 则要排查失焦或匹配参数问题。这个方法对检测系统性的标定误差特别有效因为标定误差会导致平面在三维空间中呈弯曲形变而不是简单的噪声。% 假设 ptCloudPlane 是框选出的平面上点云 [maxDistance, planeModel] pcfitplane(ptCloudPlane, 2.0); distances abs(planeModel - ptCloudPlane.Location); rmse_plane sqrt(mean(sum(distances.^2, 2))); fprintf(平面拟合残差: %.3f mm\n, rmse_plane);验证方法三已知长度物体测量。这是最直接的精度验证也是汇报结果时最有说服力的数据。在点云中通过select交互选点或直接输入两个已知点的坐标测量距离。进行 5 次重复测量记录均值和标准差。标准差代表了系统的重复性精度均值与真实值的偏差代表了系统的准确度。如果准确度差而重复性好几乎可以确定是标定板的方格边长或基线距离填错了。一个我经常用的调试技巧在标定和重建的全流程中始终保持对中间结果的可视化检查习惯。不要一次性跑完整个脚本每一步的输出都调用imshow或pcshow看一眼。特别是校正后的图像对用montage把左右图像水平拼接显示用眼睛确认远处和近处的目标都大致水平对齐。这个习惯看似简单但比任何代码检查都能更快发现问题——因为双目视觉的很多错误在代码层面完全合法只有视觉上才会暴露。如果你缩小DisparityRange的上界后发现近处物体的深度值突然跳变请记得视差范围是全局参数它限制的是视差值的上限而不是某个区域的深度范围。近处物体需要大视差如果你把上界从 160 改到 80原来 100mm 近的物体就彻底无法匹配。正确做法是保持上界不变通过RegionOfInterest参数限定匹配区域让算法只在感兴趣区域计算视差既能减少误匹配又能保留近处物体的重建能力。本文还有配套的精品资源点击获取
返回列表