目录
C++和OPENCV,实现双目视觉及三维重建。
左右相机各拍17张棋盘标定板图片;
棋盘标定板长12个格子,宽9个格子,格子边长6mm;
左右相机图像
放在两个文件夹了,命名都是从1.PNG到17.PNG,
在vs2019中运行正常
以下是代码中立体标定部分的详细解释:
代码功能说明
这段代码通过 stereoCalibrate
函数计算双目相机系统的 外参(旋转矩阵 R
和平移向量 T
),并输出标定误差。
其核心目的是确定左右相机之间的空间几何关系,为后续的立体校正和深度计算提供基础。
函数参数与输出详解
1. 输入参数
objectPoints
: 三维标定板角点的物理坐标(单位由squareSize
决定,例如毫米)。leftImagePoints
/rightImagePoints
: 左右图像中检测到的对应角点的像素坐标。leftCameraMatrix
/leftDistCoeffs
: 左相机的内参矩阵和畸变系数(已通过单目标定得到)。rightCameraMatrix
/rightDistCoeffs
: 右相机的内参矩阵和畸变系数(同上)。imageSize
: 图像分辨率(如 1280x1024)。CALIB_FIX_INTRINSIC
: 标志位,表示 固定左右相机的内参和畸变系数,仅优化外参。
2. 输出参数
R
: 右相机相对于左相机的 旋转矩阵(3x3 矩阵),表示右相机的姿态如何旋转以对齐到左相机坐标系。T
: 右相机相对于左相机的 平移向量(3x1 向量),单位为标定板尺寸单位(例如毫米)。E
: 本质矩阵(Essential Matrix),用于描述两相机之间的极几何关系,满足 x2TEx1=0 x_2^T E x_1 = 0 x2TEx1=0。F
: 基础矩阵(Fundamental Matrix),类似本质矩阵但考虑了内参,满足 p2TFp1=0 p_2^T F p_1 = 0 p2TFp1=0(p p p 为像素坐标)。stereoError
: 标定误差(单位:像素),表示角点重投影误差的均方根(RMS)。
关键结果解读
1. 旋转矩阵 R
- 物理意义: 右相机的坐标系需要经过旋转矩阵
R
变换后,才能与左相机坐标系对齐。 - 示例:
cpp
R = [ 0.999, 0.001, -0.005,
-0.001, 0.999, 0.003,
0.005, -0.003, 0.999 ]
2. 平移向量 T
- 物理意义: 右相机相对于左相机的空间位置偏移,通常以左相机为原点。
- 示例:
cpp
表示右相机位于左相机左侧 34.67mm,上方 0.10mm,后方 4.66mm。T = [-34.6668, 0.1029, 4.6637] // 单位:毫米
3. 标定误差 stereoError
- 合理范围: 误差应小于 0.5像素。若误差过大(如 >1像素),可能原因包括:
- 角点检测不准确(检查生成的
*_corners_*.jpg
)。 - 标定板图像数量不足或分布不合理。
- 相机内参标定不准确(单目标定阶段问题)。
- 角点检测不准确(检查生成的
与其他步骤的关联
1. 单目 vs. 立体标定
- 单目标定:确定单个相机的内参(焦距、主点)和畸变系数。
- 立体标定:在单目标定基础上,计算双目的外参(
R
,T
)。
2. 对立体校正的影响
R
和T
用于stereoRectify
函数,生成左右相机的校正映射矩阵(R1
,R2
,P1
,P2
)。- 若
R
或T
不准确,会导致校正后的图像无法对齐,视差图无效(如全黑或噪声严重)。
3. 对深度计算的影响
- 平移向量
T
的模长(基线长度)直接影响深度精度。较大的基线(如 100mm)适合远距离测距,较小的基线(如 30mm)适合近距离。
常见问题排查
1. 标定误差过高
- 检查角点检测:确认所有标定板图像的角点均被正确标记(查看
*_corners_*.jpg
)。 - 增加标定板多样性:采集标定板在不同距离、角度下的图像,覆盖整个视野。
- 验证单目标定结果:确保左右相机的内参和畸变系数合理(如主点应在图像中心附近)。
2. 外参结果不合理
- 检查标定板方向:标定板的坐标系定义需一致(例如棋盘格的第一个角点位置)。
- 验证物理安装:平移向量
T
的 X 分量应为负值(右相机在左相机左侧)。
3. 后续应用失败
- 校正验证:使用
stereoRectify
和initUndistortRectifyMap
后,绘制水平线验证对齐效果。 - 深度生成调试:检查视差图生成参数(如
numDisparities
是否覆盖实际视差范围)。
总结
此代码段通过立体标定确定了双相机的空间关系,输出的 R
和 T
是深度计算的核心参数。标定误差和参数合理性直接影响后续步骤的准确性,需通过多角度验证确保结果可靠。
C++和OPENCV实现相机标定代码:
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
#include <string>
using namespace cv;
using namespace std;
// 可调参数区域
const Size boardSize(11, 8); // 棋盘格内角点数量(格子数-1)
const float squareSize = 6.0f; // 棋盘格实际尺寸(毫米)
const string leftFolder = "left_images/"; // 左图文件夹路径
const string rightFolder = "right_images/"; // 右图文件夹路径
// 新标志:仅估计k1, k2, p1, p2,固定高阶项
const int calibrationFlags = CALIB_FIX_ASPECT_RATIO | CALIB_FIX_K3 | CALIB_FIX_K4 | CALIB_FIX_K5 | CALIB_FIX_K6;
const Size imageSize(1280, 1024); // 图像尺寸(需根据实际修改)
// 立体匹配参数(根据场景调整)
const int minDisparity = 0;
const int numDisparities = 16 * 5; // 必须为16的整数倍
const int blockSize = 5; // 奇数,3-11之间
const int disp12MaxDiff = 20