简介:资源是基于OpenCV、Matlab和C++实现的双目视觉测量方案,面向毕业设计、课程设计和项目开发人员,解决相机标定、立体校正、三维坐标重建及工件变形量计算等问题。包内共33个文件,包含C++源码、工程配置、Matlab标定结果、图像样本、可执行程序及说明文档等,压缩包仅2.01MB,结构紧凑便于快速部署。项目通过图像预处理提取测量点,借助Matlab工具箱完成相机标定,再以OpenCV实现立体校正和立体匹配,从而重建三维坐标并计算变形量,内容覆盖从标定到测量的完整技术链路。目前已有305人学习下载,适合需要参考源码快速上手双目视觉测量的开发者,可直接用于课题研究或功能延伸扩展。
1. 双目视觉测量:从相机标定到工件变形量的一条完整链路
做毕设或课设时最怕的不是题目难,而是每个环节都要自己从零拼。这个基于 OpenCV + Matlab + C++ 的双目视觉测量项目,正好把「图像预处理 → Matlab 相机标定 → 立体校正 → 三维坐标重建 → 工件变形量计算」整条链路一次给全了。它解决的是很多视觉测量项目的共同痛点:单目相机只能给出像素坐标,量不出真实物理尺寸,而双目通过视差能重建出毫米级的三维坐标,再对比工件变形前后的坐标差,就能算出变形量。源码包里有 Visual Studio 的 C++ 工程、标定结果、左右相机原始图像和 README,适合拿来当毕设主框架,也适合课设跑通全流程,或者作为项目开发里测量模块的起点。我建议拿到后先不要急着跑,而是把标定结果文件和 C++ 工程里读取格式先对齐,这一步会省掉后面大量的排查时间。
2. 图像预处理与特征点提取:C++ 工程的第一道关卡
2.1 先盘点工程文件,别急着点运行
解压后你会看到一个典型的 Visual Studio 工程结构:.sln解决方案文件是入口,x64目录是编译输出,两张jpg是左右相机拍摄的原始工件图,立体标定结果.txt是 Matlab 标定完导出的参数文件,README.md是说明文档。很多人上来就双击.sln,结果报一堆opencv_worldxxx.dll找不到,这不是项目问题,而是环境变量没配好。
我一般的做法是:先打开 README 看项目要求的 OpenCV 版本和配置方式,再检查系统环境变量里有没有 OpenCV 的bin目录,最后打开 VS 工程属性页确认附加包含目录和附加库目录指向的路径是否真实存在。这套检查十分钟能做完,但能避开后面半小时的玄学报错。
2.2 预处理流程:灰度化、滤波、对比度增强
图像预处理在双目视觉里承担的任务很明确:让左右两幅图在亮度、对比度上尽量一致,为后续特征点提取和匹配铺路。项目里左图和右图是同一工件不同视角的拍摄结果,光照差异必然存在,所以预处理不能只做一步,需要组合拳。
以下代码是典型的预处理流程,对应项目里「图像预处理及特征点提取」这个 C++ 工程的核心部分:
#include <opencv2/opencv.hpp> using namespace cv; using namespace std; Mat preprocessImage(const Mat& src) { Mat gray, filtered, enhanced; // 1. 彩色图转灰度:去掉颜色干扰,只保留亮度信息 cvtColor(src, gray, COLOR_BGR2GRAY); // 2. 中值滤波:去掉椒盐噪声,同时保留边缘,3x3核已经够用 medianBlur(gray, filtered, 3); // 3. CLAHE 对比度增强:限制对比度自适应直方图均衡化 // 比普通 equalizeHist 好,不会把噪声一起放大 Ptr<CLAHE> clahe = createCLAHE(2.0, Size(8, 8)); clahe->apply(filtered, enhanced); return enhanced; } int main() { Mat left = imread("left.jpg", IMREAD_COLOR); Mat right = imread("right.jpg", IMREAD_COLOR); Mat pLeft = preprocessImage(left); Mat pRight = preprocessImage(right); imwrite("preprocess_left.png", pLeft); imwrite("preprocess_right.png", pRight); return 0; }这段代码的逻辑是「降噪 → 增强对比度」,顺序不能反。先滤波是为了让 CLAHE 不会把噪声也增强出来。clipLimit参数设为 2.0 是保守值,适用于大多数工件表面;如果工件表面光照不均明显,可以调到 3.0 或 4.0,但调太高会出现光晕。Size(8, 8)是局部对比度计算的区块大小,对一般分辨率图像不需要改。
预处理参数对后续影响很大。中值滤波核如果用到 5,边缘会变钝,后续特征点位置的亚像素精度会下降;CLAHE 的 clipLimit 如果给到 5 以上,工件表面纹理会被过度增强,特征点会出现大量误匹配。这是一个需要按实际图像调试的环节,不是抄了代码就完事。
2.3 特征点提取:Shi-Tomasi 角点还是尺度不变特征
预处理之后就是特征点提取。项目标题里写的是「图像预处理及特征点提取」,也就是说特征点提取是实现测量点定位的核心手段。所谓测量点,就是工件上你要计算变形量的那几个关键位置,比如边缘角点、圆孔中心、表面刻痕交点。
在双目视觉里常见做法是分两种场景:如果工件本身纹理丰富,用 SIFT 或 SURF 这类尺度不变特征提取,能适应左右视角的尺度和旋转差异;如果工件表面是弱纹理或人工标记点,用 Shi-Tomasi 角点更直接。项目工程放在「特征点提取」这个命名下,我倾向于它采用的是角点或标记点检测的路线。
// Shi-Tomasi 角点检测:适合工件上有明显角点、刻痕、标记的场景 vector<Point2f> detectCorners(const Mat& img, int maxCorners = 50) { vector<Point2f> corners; // qualityLevel 0.01 表示接受角点响应值大于最大值的1% // minDistance 20 控制角点之间的最小像素距离,避免扎堆 goodFeaturesToTrack(img, corners, maxCorners, 0.01, 20); // 亚像素精化:让角点坐标从像素级提升到亚像素级 // 后续三维重建的精度直接依赖这一步,坐标误差越小变形量越准 Mat mask = Mat::zeros(img.size(), CV_8UC1); vector<Point2f> refined; int halfWin = 5; for (size_t i = 0; i < corners.size(); i++) { int x = max(halfWin, min(img.cols - halfWin - 1, (int)corners[i].x)); int y = max(halfWin, min(img.rows - halfWin - 1, (int)corners[i].y)); mask.setTo(0); mask(Rect(x - halfWin, y - halfWin, halfWin * 2 + 1, halfWin * 2 + 1)) = 255; // TermCriteria 控制迭代次数和精度:最多30次,精度0.01像素 cornerSubPix(img, corners, Size(5, 5), Size(-1, -1), TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 30, 0.01)); } return corners; }goodFeaturesToTrack的maxCorners是上限,实际检测到的角点可能少于这个数;qualityLevel=0.01意味着响应值低于最大值 1% 的候选点会被丢弃,如果工件上角点很多可以调到 0.02 让筛选更严格。cornerSubPix的搜索窗口Size(5, 5)是精化时用的邻域大小,窗口越大越稳但越慢,对一般金属工件 5x5 足够。
特征点提取完之后,左右图对应点需要匹配。角点匹配的常见做法是结合描述子(比如 ORB)或光流追踪;如果左右视角差异很小,也可以用极线约束直接搜索对应行。这一步的输出是匹配点对,为后面的立体匹配和三维坐标重建提供稀疏种子点。
2.4 这一步的常见误用:特征点不是越多越好
很多人以为特征点提取越多越好,实际不然。在变形量测量这种场景里,你只需要工件上若干稳定的关键点,多出来的特征点是噪声:可能来自背景、可能来自光照反光,匹配后会给三维重建引入离群点。我一般会控制在每幅图 20 到 50 个点,然后根据极线约束手动剔除不满足对极几何约束的匹配点。
另一个容易踩的坑是把原始图像直接做特征提取而跳过预处理。左右相机拍摄时的曝光不可能完全一致,不做对比度均衡的话,同一特征点在左右图中的响应强度不同,匹配很容易翻车。有人调试的时候怀疑是匹配算法参数的锅,调了半天参数没用,回头发现是预处理没做,这是最常见的血泪经验。
3. Matlab 相机标定:为什么选 Matlab 而不是 OpenCV
3.1 标定是整个测量精度的地基
相机标定解决的核心问题是:三维世界坐标怎么投影成二维像素坐标。这个映射关系由相机内参(焦距、主点、畸变)和相机外参(旋转、平移)决定。双目视觉里还有一个更重要的参数是两台相机之间的相对位姿(旋转矩阵 R 和平移向量 T),它决定了立体匹配时极线在哪一行。
Matlab 的 Camera Calibrator 工具箱是很多双目项目优先选它的原因:交互界面直观,标定板的角点检测是亚像素级的,且每一步都能看到重投影误差。相比之下 OpenCV 的calibrateCamera也能做,但参数默认值和报错信息对新手不太友好,Debug 成本高。项目里既然同时用了 Matlab 和 OpenCV,合理的分工就是:Matlab 负责标定出参数,OpenCV 负责读取参数并完成校正、匹配和重建。
3.2 标定流程:从拍照到导出结果的完整步骤
标定需要拍摄多张棋盘格图像,覆盖不同角度、不同距离和画面的不同区域。单张图像标定不出来,至少需要 10 张以上,项目里的立体标定结果.txt是基于一组左右目棋盘格图像计算得到的,你把同名的两张原始工件图放进相机标定工具时,这套流程会重新走一遍。
Matlab 标定部分的典型流程如下:
% 在 Matlab 命令行里导入标定图像 % 假设左相机拍摄的棋盘格图像放在 ./calib/left 目录下 imageNames = dir(fullfile('./calib/left', '*.jpg')); numImages = length(imageNames); % 声明左右相机各自的图像集 leftImages = cell(1, numImages); for i = 1:numImages leftImages{i} = fullfile('./calib/left', imageNames(i).name); end % 使用 Matlab 自带的标定函数(需 Computer Vision Toolbox) % 棋盘格边长单位是毫米,这里假设每个格子 30mm squareSize = 30; [params, errors] = estimateCameraParameters(leftImages, squareSize);squareSize是用游标卡尺量出来的实际棋盘格边长,单位要和你后续三维重建想要的输出单位一致。如果用毫米标定,后面计算出来的三维坐标单位就是毫米;这里填错是最常见的翻车原因,很多人后续重建坐标和实际尺寸对不上,回头一查是标定的时候边长填了 30,实际格子是 25 毫米。
3.3 标定结果里都有什么:读懂立体标定结果.txt
Matlab 标定完成后,导出的结果包含两类信息。左相机内参矩阵:主点坐标(像素)、焦距(像素)、畸变系数、重投影误差;右相机内参和畸变同理。双目标定后还有右相机相对左相机的旋转矩阵和平移向量,这个相对位姿就是立体校正需要的核心参数。
项目里立体标定结果.txt保存的正是这些内容。用 C++ 读取之前要先确认文件里每个数值的类型和顺序,比如畸变系数是 5 个元素还是 4 个元素,对应distCoeffs的排列顺序是[k1, k2, p1, p2, k3]还是 OpenCV 旧版约定的[k1, k2, p1, p2]。Matlab 默认输出的是 5 参数模型[k1, k2, p1, p2, k3],和 OpenCV 的distCoeffs顺序一致,这是它方便的地方,但数值本身的精度和格式需要你在 C++ 端保持 float 或 double 一致性。
我的习惯是让 Matlab 直接输出一个 OpenCV 能读的 yml 文件,省去手动复制粘贴的环节。下面这段 Matlab 代码把标定结果写成 OpenCV FileStorage 格式:
% 将 Matlab 标定结果写入 OpenCV 可读的 yml 文件 fileID = fopen('stereo_calib.yml', 'w'); fprintf(fileID, '%%YAML:1.0\n'); fprintf(fileID, 'left_camera_matrix:\n'); fprintf(fileID, ' rows: 3\n cols: 3\n data: ['); fprintf(fileID, '%.8f, %.8f, %.8f,\n', params.Intrinsics(1,1), ... 0, params.Intrinsics(1,3)); fprintf(fileID, '%.8f, %.8f, %.8f,\n', 0, params.Intrinsics(2,2), ... params.Intrinsics(2,3)); fprintf(fileID, '%.8f, %.8f, %.8f]\n', 0, 0, 1); fprintf(fileID, 'left_distortion:\n'); fprintf(fileID, ' data: ['); % Distortion 是 5 个元素 [k1, k2, p1, p2, k3] fprintf(fileID, '%.8f, %.8f, %.8f, %.8f, %.8f]\n', ... params.RadialDistortion(1), params.RadialDistortion(2), ... params.TangentialDistortion(1), params.TangentialDistortion(2), ... params.RadialDistortion(3)); fclose(fileID); disp('标定结果已写入 stereo_calib.yml');%YAML:1.0是 OpenCV FileStorage 的固定头,不能省略。%.8f保留 8 位小数,这个精度对毫米级的重建完全够用。写入的顺序和 OpenCVcv::FileStorage读取时的节点名一一对应,后面 C++ 端读取时会按left_camera_matrix和left_distortion这两个 key 去取矩阵,省去自己解析 txt 的坑。
3.4 标定参数自查表
标定完先别急着下一环节,把下面几个指标对一遍:
重投影误差应小于 0.5 像素,超过这个说明有图像抖动或角点提取错误。左右相机的主点坐标应在图像中心附近(比如 640x480 的图主点应该在 320 和 240 附近),偏差超过 50 像素要重新检查标定板是否覆盖了画面边缘。旋转矩阵应近似为单位矩阵,平移向量中 Z 分量应为正(表示右相机在左相机右侧),如果 Z 为负说明图像左右顺序反了。每张图的角点重投影误差如果有个别图明显偏高,直接删掉重新标,别硬留。
4. 立体校正与三维坐标重建:把像素坐标变成毫米坐标
4.1 立体校正:让极线对齐到同一行
双目标定完成后,左右相机的图像还没有对齐。左图上某个点在右图上不一定在同一行,搜索匹配点要遍历二维区域,计算量大且容易错。立体校正的目的就是通过旋转矩阵和平移向量对左右图像做重投影变换,让同一个三维点的像落在左右图的同一行上,这样匹配搜索就从二维退化到一维。
OpenCV 提供的stereoRectify是最常用的立体校正函数,参数来自标定结果的内参、畸变系数和相对位姿。核心代码如下:
#include <opencv2/opencv.hpp> #include <opencv2/calib3d.hpp> using namespace cv; // 从 yml 读入标定参数(上一节生成的 stereo_calib.yml) FileStorage fs("stereo_calib.yml", FileStorage::READ); Mat K1, K2, D1, D2, R, T; fs["left_camera_matrix"] >> K1; fs["left_distortion"] >> D1; fs["right_camera_matrix"] >> K2; fs["right_distortion"] >> D2; fs["rotation_matrix"] >> R; fs["translation_vector"] >> T; fs.release(); // 图像分辨率必须和标定时一致 Size imgSize(640, 480); Mat R1, R2, P1, P2, Q; // stereoRectify 计算左右相机的校正旋转矩阵、投影矩阵和视差到深度的Q矩阵 stereoRectify(K1, D1, K2, D2, imgSize, R, T, R1, R2, P1, P2, Q, CALIB_ZERO_DISPARITY, 0.3); // 根据校正映射表对原图做重映射 Mat map11, map12, map21, map22; initUndistortRectifyMap(K1, D1, R1, P1, imgSize, CV_32FC1, map11, map12); initUndistortRectifyMap(K2, D2, R2, P2, imgSize, CV_32FC1, map21, map22); Mat leftRect, rightRect; remap(leftImage, leftRect, map11, map12, INTER_LINEAR); remap(rightImage, rightRect, map21, map22, INTER_LINEAR);CALIB_ZERO_DISPARITY让主点在校正后保持一致,通常建议打开。0.3是 alpha 参数,控制校正后图像的缩放范围:0.3 表示裁掉约 30% 的无效区域,如果设成 1 则会保留所有像素但图像拉伸变形更明显。Q矩阵是重投影矩阵,第四第五列是左右投影中心坐标和基线长度的加权组合,后面重建三维坐标直接用这个矩阵做矩阵乘法。
校正完的图像可以通过左右视差线叠加观察验证:如果在两幅图上水平画若干条线,同一行上的对应特征应该落在线的同一水平位置,那就说明校正到位了。这一步错了后面所有匹配都是白做,所以我每次都会把校正结果保存成图片,用肉眼先看一眼再继续。
4.2 立体匹配:SGBM 参数不是抄了就能用
立体匹配的目标是计算左右图中每个像素的视差,生成视差图。OpenCV 里 SGBM(Semi-Global Block Matching)是目前工程上用得最多的算法,参数不多但每个都很讲究。
// SGBM 立体匹配:输入是校正后的灰度图 Ptr<StereoSGBM> sgbm = StereoSGBM::create( 16, // minDisparity:最小视差,通常为0 80, // numDisparities:视差范围,必须是16的倍数 11, // blockSize:匹配窗口大小,奇数,3~21之间 8 * 11 * 11, // P1:视差平滑惩罚参数,一般取 8*blockSize^2 32 * 11 * 11, // P2:梯度变化惩罚参数,一般取 32*blockSize^2 1, // disp12MaxDiff:左右一致性检查阈值,1表示严格要求 63, // preFilterCap:预滤波截断值 10, // uniquenessRatio:唯一性比率,越大匹配越严格 100, // speckleWindowSize:滤除小斑点窗口大小 32, // speckleRange:斑点范围 StereoSGBM::MODE_SGBM ); Mat disparity, disparityFloat; sgbm->compute(leftRect, rightRect, disparity); // 视差图是 int16 类型,需要转成 float 并除以16得到真实视差值 disparity.convertTo(disparityFloat, CV_32F, 1.0 / 16.0);numDisparities是视差搜索范围,数值越大能测的深度范围越大,但计算量也随之增加。根据项目的拍摄距离和基线长度,80 到 160 之间通常够用。blockSize对结果质量影响最大:太小则纹理不足时匹配不稳,太大则边缘会被磨平;11 是通用起始值。P1和P2控制视差图的平滑度,P2越大视差图越平滑,但太小会丢失细节;默认值是8*blockSize^2和32*blockSize^2,大部分场景不用改。speckleWindowSize会滤掉小于 100 像素的孤立小区域,能去掉大部分噪声点。
SGBM 输出的视差图是 int16 类型,真实视差值需要右移 4 位,即除以 16。如果直接把这个值当视差去算三维坐标,所有深度都会放大 16 倍,这是很常见的玄学错误,比算法参数本身更容易让结果翻车。
4.3 三维坐标重载:从视差到世界坐标的最后一跳
有了视差图和 Q 矩阵,三维坐标重建就变成一次矩阵运算。OpenCV 里用reprojectImageTo3D一步完成,这也是项目里「三维坐标重载」这个说法的技术含义——把每个像素的二维坐标和视差重载成三维空间坐标。
// 从视差图重建三维坐标 Mat pointCloud; // 输入是 CV_32F 的视差图,Q 是 stereoRectify 输出的重投影矩阵 reprojectImageTo3D(disparityFloat, pointCloud, Q, true, CV_32F); // 我一般会在视差无效的位置置为 NAN,后续计算变形量时跳过这些点 Mat mask = disparityFloat > 0; pointCloud.setTo(NAN, ~mask); // 提取某个特征点的三维坐标 // 假设左图中特征点的像素坐标为 (u, v) Point2f featurePoint(220.5f, 180.3f); Vec3f worldCoord = pointCloud.at<Vec3f>(featurePoint.y, featurePoint.x); double X = worldCoord[0], Y = worldCoord[1], Z = worldCoord[2];pointCloud是三维数组,每个像素位置存了对应的(X, Y, Z)。reprojectImageTo3D的第四个参数handleMissingValues设为 true 时,对于视差无效的点会自动输出一个很大的值而不是真正的坐标,配合后面用掩膜做过滤是最好的处理方式。注意at<Vec3f>的索引是先行后列,很多人在这一步把 x 和 y 写反,导致输出的坐标完全不对。
4.4 从稀疏特征点到稠密三维点云
如果你对工件的变形量只需要测几个关键点,就可以不跑全图的视差计算,直接用稀疏特征点加 Q 矩阵算坐标。具体方法是先对特征点做立体匹配找到左右图的对应点,然后根据对应点的横坐标差求视差,再用Q矩阵和这个视差算出坐标。这种方式速度快、内存占用小,适合做实时或者准实时的测量。
但稠密重建也有不可替代的价值:如果你不确定变形最严重的位置在哪,稀疏点覆盖不了整个表面,可能漏掉局部变形。项目里同时保留了两条路径,稀疏用于快速检测关键点,稠密用于生成视差图和验证整体变形分布。
5. 避坑指南:五个最容易让项目卡住的问题
5.1 打开工程就报 OpenCV DLL 找不到
现象是运行图像预处理及特征点提取.exe时提示找不到opencv_worldxxx.dll或类似的动态库。原因很直接:OpenCV 的bin目录没有加入系统环境变量PATH,或者工程属性里的库目录指向的路径和实际不一致。
解决方法是先确认 OpenCV 安装路径,把...\opencv\build\x64\vc15\bin或对应的版本目录加入系统环境变量,重启 VS 再试。同时检查工程配置是 Debug 还是 Release:Debug 模式要链接带d后缀的库(比如opencv_world450d.lib),Release 模式链接不带d的版本,混着用一样报错。
5.2 Matlab 标定结果复制到 txt,C++ 解析出来全是乱码
现象是立体标定结果.txt用记事本打开正常,但程序读取后数值要么是 0 要么是错位的大数。原因大概率是格式问题:Matlab 输出默认用 4 位小数或科学计数法,而 C++ 解析按%f读,遇到1.2345e+03这类字符将直接中断。
解决的可靠办法是用FileStorage写 yml 文件,由 OpenCV 自己负责序列化和反序列化。如果必须要用 txt,就固定用%12.6f格式输出固定宽度小数,C++ 按固定宽度解析,不要在数据里混任何单位、分号或注释。中文注释尤其不要出现在数值文件里,否则编码不一致时读出来的第一行就是错的。
5.3 视差图一片黑或者全是斑驳噪声
现象是 SGBM 跑完输出的视差图完全不可用。原因通常有两个:一个是输入图像没有做立体校正就进入匹配,极线没有对齐,匹配搜索不到正确的对应点;另一个是numDisparities设置过小,被测工件的真实视差超过了搜索范围。
解决方法是先确认校正环节:把左右校正图叠在一起画几条水平线,看看同一特征是否在同一行。确认校正正确后,把numDisparities从 80 改成 128 或 160,观察视差图变化。再不行就把blockSize调大到 15 或 21,让匹配窗口覆盖更多纹理信息,但注意边缘位置会变模糊。
5.4 重建出来的三维坐标和实际尺寸差了 10% 以上
现象是工件宽度量出来是 220 毫米,实际只有 200 毫米。原因绝大多数不是算法问题,而是标定输入错误:棋盘格边长squareSize填错了单位,或者标定图像的尺寸和实际运行时不一致。
解决方法是重新用卡尺量棋盘格边长,检查 Matlab 标定脚本里的squareSize是否与之对应,同时确认标定图像分辨率有没有被缩放变形。这个 10% 级别的误差就是尺度因子问题,和相机内参无关,属于「标定数据源头错」的范畴,排查优先级最高。
5.5 同一工件重复测两次,变形量结果不一致
现象是工件没动,拍摄条件也没变,但两次计算的变形量有几十毫米的偏差。原因是左右图像的光照或曝光变化导致特征点提取位置漂移,尤其在使用 SIFT 这类对亮度敏感的描述子时,角点位置可能在亚像素基准上移动,经过 Q 矩阵放大后变成了毫米级的坐标偏差。
解决方式是在预处理环节强制保持固定参数,比如固定 CLAHE 的clipLimit,不跟随环境亮度自动调整;采集图像时尽量用固定曝光模式;特征点精化用cornerSubPix把位置稳定到亚像素级。这类问题很像玄学,但其实都是输入稳定性不够造成的。
6. 变形量计算的进阶验证:让测量结果真的可信
6.1 变形量的定义方式
变形量计算不是简单地把两个三维坐标做差,要看工件变形是什么类型。如果是局部凹陷,需要对比同一特征点变形前后的坐标差;如果是整体弯曲,直接对比两端点在某一坐标轴上的偏移更直观。我先会确定外点围成的平面在变形前后的法向量夹角,再算关键点的欧氏距离偏差,两个指标互相印证。
以下代码演示了变形前后两个三维点的差值计算:
// 变形前和变形后同一个特征点的三维坐标 Vec3f coordBefore(120.5f, 80.3f, 500.0f); Vec3f coordAfter(121.2f, 80.1f, 499.2f); // 计算三个方向的偏差 float dx = coordAfter[0] - coordBefore[0]; float dy = coordAfter[1] - coordBefore[1]; float dz = coordAfter[2] - coordBefore[2]; // 欧氏距离就是该点的变形量 float deformation = sqrt(dx * dx + dy * dy + dz * dz);6.2 验证测量精度的实用手段
精度不是靠感觉的。我每次拿到这类双目测量项目做二次开发时,都会强制跑一遍验证流程:找一块已知尺寸的硬板放在工件同一平面,用程序测量板上两个角点的距离,和卡尺量出来的值比较,记录误差;然后把工件同一位置重复测量 10 次,计算标准差,标准差如果超过 0.2 毫米说明流程不稳定。
这个项目里重投影误差在标定结果 txt 里有记录,通常亚像素级,对应的三维测量精度在近距离下能做到毫米以内。如果你的毕设答辩需要展示精度,我建议把误差统计表和工件原图、标定过程截图一起放进去,这套验证材料的说服力比空谈算法效果要好得多。
6.3 到最后一步,最值钱的其实是流程习惯
从拿到这个项目到最终跑通,我花了大概两天。第一天在环境配置和标定结果格式对齐上踩了不少坑,第二天把整个流程理顺后发现,真正影响测量结果的不是算法本身多高深,而是每一步输入是否干净、参数是否自洽。从那以后我每次做双目测量之前,都会强制走一遍验证流程:先量棋盘格边长、再查标定重投影误差、最后用已知尺寸的板子做一次精度验证。这套流程帮我挡掉了很多后续排查的麻烦,希望你也能在复现这个项目的过程中把同样的检查习惯沉淀下来,希望帮到你。
本文还有配套的精品资源,点击获取