
简介该资源面向计算机视觉初学者与需要完成课程设计、毕业设计的开发者提供一套基于OpenCV、VS2019与MATLAB的双目视觉测距完整工程。代码覆盖相机标定、立体校正到BM局部匹配算法输出视差图的全流程可用于理解双目测距原理并快速搭建实验环境。压缩包共42个文件约1.16MB包含30张左右相机标定图片、6个标定结果与参数文本、CameraCalibration.sln解决方案及cpp源码、vcxproj工程文件以及项目说明md文档图片与参数文件便于直接复现标定与校正过程。目前已有661人学习下载。读者可获得可编译运行的VS工程、标定与立体校正参数、视差图生成代码及项目说明适合对照源码梳理从标定到测距的实现思路并在此基础上进行算法替换或精度优化。1. 从一组标定图到真实距离这套双目测距源码能省掉哪些重复劳动如果你手头正好有一对工业相机、一堆棋盘格标定图却卡在「标定参数怎么串起来、视差图怎么转成毫米」这一步这套基于 OpenCV VS2019 MATLAB 的双目视觉测距源码包值得先拆开看。它把从相机标定、立体校正、BM 局部匹配到视差输出这条链路完整跑通压缩包里既有CameraCalibration.sln这样的 VS 工程也有calibrationresults_L.txt、stereoRectifyParams.txt、stereocalibrateresult.txt这类中间结果文件还有left01.jpg到left13.jpg、right01.jpg到right13.jpg的左右标定图对。换句话说它不是一段孤立的测距 demo而是一份带输入、带中间产物、带校正前后对比图的完整工程记录。适合谁做立体视觉入门、课程设计、机器人避障原型、或者需要快速验证一套双目硬件基线的人。你不需要从零推导对极几何但得愿意动手把参数、路径、匹配窗口一个个对齐。2. 标定链路拆解MATLAB 工具箱与 OpenCV 参数文件怎么对接2.1 为什么标定和测距要分两套工具做双目测距的精度上限几乎在标定阶段就定死了。这套源码把标定放在 MATLAB 侧测距放在 OpenCV VS2019 侧是有现实理由的MATLAB 的 Camera Calibrator 工具箱对棋盘格角点提取、畸变系数初值估计、重投影误差可视化做得非常顺手你能直接看到每张图的误差条哪张图拍糊了、哪个角点偏了一眼就能剔除。而 OpenCV 的stereoRectify、StereoBM在 C 里跑实时视差更合适。两者之间靠文本文件交接calibrationresults_L.txt和calibrationresults_R.txt存左右相机的内参矩阵与畸变系数stereoRectifyParams.txt存立体校正后的旋转、投影矩阵stereocalibrateresult.txt汇总立体标定结果。常见做法是先在 MATLAB 里分别标定左右相机导出内参再做立体标定得到 R、T最后把这一整套参数写进 OpenCV 能读的格式。这套源码的caliberationpics_L.txt和caliberationpics_R.txt就是标定图清单里面按行列出参与标定的图片路径。你拿到手第一件事不是编译而是打开这两个清单确认路径指向的left01.jpg到left13.jpg是否真实存在。2.2 用 MATLAB 导出可被 OpenCV 读取的参数MATLAB 标定完参数默认在cameraParams对象里。要交给 OpenCV得手动导出成纯文本。下面这段是我一般会用的导出脚本把内参、畸变、立体参数分别落盘% 假设 stereoParams 是 stereoCameraCalibrator 导出的对象 % 左相机内参 K_left stereoParams.CameraParameters1.IntrinsicMatrix; % 转置OpenCV 行主序 D_left stereoParams.CameraParameters1.RadialDistortion; T_left stereoParams.CameraParameters1.TangentialDistortion; % 右相机内参 K_right stereoParams.CameraParameters2.IntrinsicMatrix; D_right stereoParams.CameraParameters2.RadialDistortion; T_right stereoParams.CameraParameters2.TangentialDistortion; % 立体参数 R stereoParams.RotationOfCamera2; T stereoParams.TranslationOfCamera2; % 写入文本每行一个数方便 C 逐行读 writematrix(K_left, calibrationresults_L.txt, Delimiter, tab); writematrix([D_left, T_left], distortion_L.txt, Delimiter, tab); writematrix(K_right, calibrationresults_R.txt, Delimiter, tab); writematrix([D_right, T_right], distortion_R.txt, Delimiter, tab); writematrix(R, stereoRectifyParams.txt, Delimiter, tab); writematrix(T, stereocalibrateresult.txt, Delimiter, tab);逻辑说明MATLAB 的IntrinsicMatrix是列主序存储OpenCV 的Mat默认按行读所以必须转置。畸变系数 MATLAB 把径向和切向分开存OpenCV 的stereoRectify需要拼成一个 5 元素向量[k1 k2 p1 p2 k3]顺序不能错。参数说明RadialDistortion通常两个值TangentialDistortion两个值如果用了三参数径向畸变要补上第三个。写文件时用 tab 分隔C 侧用ifstream逐行读再atof转换即可。提示导出后别急着编译先用记事本打开calibrationresults_L.txt确认第一行三个数是 fx、0、cx 的量级如果出现 0.001 这种明显不对的值多半是转置漏了。2.3 VS2019 工程里参数加载与立体校正的落点打开CameraCalibration.sln核心逻辑在CameraCalibration.cpp。工程属性里要确认 OpenCV 的 include 和 lib 路径指向你本机安装位置VS2019 的 Debug/Release 配置要分别设别只改一个。参数加载部分一般长这样// 读取标定结果 Mat K_left Mat::eye(3, 3, CV_64F); Mat D_left Mat::zeros(1, 5, CV_64F); ifstream fL(calibrationresults_L.txt); for (int i 0; i 3; i) for (int j 0; j 3; j) fL K_left.atdouble(i, j); // 按行读入内参 // 畸变系数单独文件顺序 k1 k2 p1 p2 k3 ifstream fDL(distortion_L.txt); for (int i 0; i 5; i) fDL D_left.atdouble(0, i); // 立体校正 Mat R, T, R1, R2, P1, P2, Q; fL R; // 实际按文件格式逐元素读 stereoRectify(K_left, D_left, K_right, D_right, imgSize, R, T, R1, R2, P1, P2, Q, CALIB_ZERO_DISPARITY, 0, imgSize);逻辑说明stereoRectify输出Q矩阵是后面reprojectImageTo3D把视差转三维坐标的关键很多人只拿视差图看灰度忘了Q才是测距的桥。参数说明CALIB_ZERO_DISPARITY让主点在左右图对齐alpha0表示校正后图像不保留黑边imgSize必须和实际图像尺寸一致否则Q算出来的尺度全错。校正完可以用initUndistortRectifyMap生成映射表再remap得到左相机校正图像.jpg、右相机校正图像.jpg那样的结果和包里给的对比图对一下极线应该水平对齐。3. BM 局部匹配出视差窗口、视差范围与测距公式的实操3.1 BM 算法为什么先拿来跑通而不是直接上 SGBMBMBlock Matching是局部匹配里最直白的一种对左图每个像素在右图同一行的一定范围内滑动窗口找绝对差之和最小的位置偏移量就是视差。它没有全局能量优化速度快参数少适合先把整条链路跑通。这套源码摘要里明确写了用 BM 输出视差图这是明智的起步选择。SGBM 精度更好但参数多、调参玄学新手容易在P1、P2、uniquenessRatio上卡住。先用 BM 确认标定参数没错、极线对齐了再换 SGBM 提升质量这个顺序能帮你把「标定问题」和「匹配问题」分开定位。BM 的核心参数就几个numDisparities必须是 16 的整数倍决定搜索范围blockSize是匹配窗口边长奇数越大越平滑但边缘越糊minDisparity一般设 0textureThreshold过滤低纹理区域。视差范围和你的基线、最近测距距离直接相关下面给个估算。3.2 视差范围与测距公式的参数推导测距公式是Z f * B / d其中f是校正后焦距像素B是基线米d是视差像素。Q矩阵里其实已经编码了这些。假设校正后f 800像素基线B 0.06米你想测最近 0.5 米那最大视差d_max f*B/Z_min 800*0.06/0.5 96像素所以numDisparities至少设 96取 16 的倍数就是 96 或 112。想测到 3 米最小视差d_min 800*0.06/3 16像素minDisparity可以设 0 让 BM 自己找但设成 16 能减少无效搜索。PtrStereoBM bm StereoBM::create(96, 15); // numDisparities96, blockSize15 bm-setMinDisparity(0); bm-setTextureThreshold(10); bm-setUniquenessRatio(15); bm-setSpeckleWindowSize(100); bm-setSpeckleRange(32); Mat disp, disp8; bm-compute(rectifiedLeft, rectifiedRight, disp); disp.convertTo(disp8, CV_8U, 255.0 / (96 * 16)); // 归一化显示逻辑说明compute输入必须是校正后的灰度图彩色图要先cvtColor。disp是 16 位有符号真实视差要除以 16 才是像素值。参数说明blockSize15是经验起点纹理弱就加大到 21但别超过 31uniquenessRatio15表示最优匹配要比次优好 15% 才接受调大能压误匹配但会丢弱纹理speckleWindowSize和speckleRange做连通域去噪视差图有零星白点就调这两个。3.3 从视差图到真实距离的验证步骤拿到disp后用reprojectImageTo3D(disp, xyz, Q, true)得到三维点云xyz.atVec3f(y,x)[2]就是该像素的 Z 值单位与标定时棋盘格边长一致通常是毫米。验证方法很直接在场景里放一个已知距离的物体比如 1 米处的标定板看对应像素的 Z 值偏差多少。包里给的校正前左右相机图像.jpg和校正后左右相机图像.jpg就是给你做这个对照的——校正后极线水平同一物体在左右图的 y 坐标应该一致不一致说明标定或校正有问题。注意reprojectImageTo3D对无效视差disp0会输出很大的值统计距离前要先掩掉。我一般用disp minDisparity*16做掩膜再算中位数比均值抗噪。4. 避坑与排查标定、匹配、编译里最容易翻车的几处4.1 标定图数量不够或角度太单一现象重投影误差看着不大但测距时远处物体 Z 值飘得厉害。原因标定图都集中在正对相机的一个平面标定解算出的焦距和主点在小范围内自洽但外推到不同深度就崩。解决标定图要覆盖画面四个角、不同倾斜角、不同距离left01到left13这 13 对图如果都是同一角度建议自己补拍。一般 15 到 20 对、角度分散重投影误差能压到 0.2 像素以内。4.2 左右图清单顺序错位现象立体校正后左右图明显不对齐极线歪斜。原因caliberationpics_L.txt和caliberationpics_R.txt里同一行的左右图不是同一时刻拍的或者顺序错了一位。解决逐行核对文件名left01必须配right01拍的时候左右相机同步触发。这个坑血泪经验错一对图整个标定结果全废。4.3 VS2019 的 Debug 与 Release 混用 OpenCV 库现象Debug 编译报 LNK2038 或运行崩溃。原因OpenCV 的opencv_world4xxd.lib是 Debug 版opencv_world4xx.lib是 Release 版工程配置和链接的库不匹配。解决在 VS2019 属性管理器里Debug 配置链接带d的 libRelease 链接不带d的附加依赖项分别设。别图省事两个配置用同一份。4.4 视差图大面积黑或全白现象disp8几乎全黑或全白没有纹理。原因全黑多半是numDisparities太小真实视差超出搜索范围全白可能是minDisparity设太大或图像没转灰度。解决先打印disp的最大最小值确认有效视差落在[minDisparity, minDisparitynumDisparities]内再确认compute输入是CV_8UC1。另外textureThreshold设太高会把低纹理区全滤掉先设 10 试。4.5 测距数值单位对不上现象Z 值算出来是 0.06 而不是 60。原因标定棋盘格边长单位是米还是毫米没统一T平移向量单位跟着棋盘格走。解决标定时记录棋盘格实际边长如果用的米Z 就是米用毫米就是毫米。Q矩阵不改变单位只做坐标变换。统一用毫米最后除以 1000 转米最不容易乱。5. 进阶技巧把 BM 换成 SGBM 并做亚像素级距离验证跑通 BM 之后想提升视差图质量换 SGBM 是自然一步。SGBM 在cv::StereoSGBM里参数比 BM 多但核心就几个minDisparity、numDisparities、blockSize、P1、P2、disp12MaxDiff、uniquenessRatio、speckleWindowSize。P1和P2是平滑惩罚项经验公式P1 8*channels*blockSize^2P2 32*channels*blockSize^2先按这个设再微调。int blockSize 5; int cn rectifiedLeft.channels(); int P1 8 * cn * blockSize * blockSize; int P2 32 * cn * blockSize * blockSize; PtrStereoSGBM sgbm StereoSGBM::create(0, 96, blockSize); sgbm-setP1(P1); sgbm-setP2(P2); sgbm-setDisp12MaxDiff(1); sgbm-setUniquenessRatio(10); sgbm-setSpeckleWindowSize(100); sgbm-setSpeckleRange(32); sgbm-setMode(StereoSGBM::MODE_SGBM_3WAY); Mat disp_sgbm; sgbm-compute(rectifiedLeft, rectifiedRight, disp_sgbm); disp_sgbm.convertTo(disp_sgbm, CV_32F, 1.0 / 16); // SGBM 同样放大 16 倍逻辑说明SGBM 输出也是 16 倍放大转 float 时除以 16。MODE_SGBM_3WAY比默认模式快且质量接近。参数说明blockSize设小3 或 5能保留边缘但噪点多配合speckleWindowSize去噪disp12MaxDiff1做左右一致性检查能去掉大部分误匹配代价是视差图有空洞后续可以用filterSpeckles或简单的空洞填充补。验证距离时我习惯在场景里放三个已知距离的标记物比如 0.8 米、1.5 米、2.5 米分别取对应像素区域的 Z 中位数和真实值比。误差在 2% 以内算这套标定和匹配参数合格。如果某个距离段误差突然变大多半是那个距离的视差落到了numDisparities边缘回去调范围。提示SGBM 比 BM 慢不少如果做实时先把图像缩放到 640x480 再匹配精度损失可接受速度能翻几倍。从那以后我每次拿到新的双目数据都强制先跑一遍校正前后对比图确认极线水平了再动匹配参数不然调半天都是白费。希望帮到你。本文还有配套的精品资源点击获取