- 操作系统:ubuntu22.04
- OpenCV版本:OpenCV4.9
- IDE:Visual Studio Code
- 编程语言:C++11
算法描述
标定立体相机设置。此函数找到两个相机各自的内参以及两个相机之间的外参。
cv::stereoCalibrate 是 OpenCV 中用于立体相机标定的函数。它通过一组已知的3D点及其在两个相机中的对应2D投影,来估计两个相机之间的相对位置和方向(旋转矩阵R和平移向量T),同时还可以优化每个相机的内参矩阵和畸变系数。
函数原型
cpp
double cv::stereoCalibrate
(
InputArrayOfArrays objectPoints,
InputArrayOfArrays imagePoints1,
InputArrayOfArrays imagePoints2,
InputOutputArray cameraMatrix1,
InputOutputArray distCoeffs1,
InputOutputArray cameraMatrix2,
InputOutputArray distCoeffs2,
Size imageSize,
InputOutputArray R,
InputOutputArray T,
OutputArray E,
OutputArray F,
OutputArrayOfArrays rvecs,
OutputArrayOfArrays tvecs,
OutputArray perViewErrors,
int flags = CALIB_FIX_INTRINSIC,
TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 1e-6)
)
参数
- 参数objectPoints 校准图案点的向量的向量。结构与 calibrateCamera 中相同。对于每个图案视图,两个相机需要看到相同的物体点。因此,objectPoints.size()、imagePoints1.size() 和 imagePoints2.size() 需要相等,并且对于每个 i,objectPoints[i].size()、imagePoints1[i].size() 和 imagePoints2[i].size() 也需要相等。
- 参数imagePoints1 第一个相机观测到的校准图案点投影的向量的向量。结构与 calibrateCamera 中相同。
- 参数imagePoints2 第二个相机观测到的校准图案点投影的向量的向量。结构与 calibrateCamera 中相同。
- 参数cameraMatrix1 第一个相机的输入/输出内参矩阵,与 calibrateCamera 中相同。对于立体情况,可以使用额外的标志,见下文。
- 参数distCoeffs1 输入/输出畸变系数向量,与 calibrateCamera 中相同。
- 参数cameraMatrix2 第二个相机的输入/输出内参矩阵。详见 cameraMatrix1 的描述。
- 参数distCoeffs2 第二个相机的镜头畸变系数的输入/输出向量。详见 distCoeffs1 的描述。
- 参数imageSize 仅用于初始化相机内参矩阵的图像尺寸。
- 参数R 输出旋转矩阵。连同平移向量 T 一起,该矩阵将第一个相机坐标系中的点变换到第二个相机坐标系中的点。更技术性地说,R 和 T 的组合执行了从第一个相机坐标系到第二个相机坐标系的基底变换。由于其对偶性,这个组合等价于第一个相机相对于第二个相机坐标系的位置。
- 参数T 输出平移向量,参见上述描述。
- 参数E 输出本质矩阵。
- 参数F 输出基础矩阵。
- 参数rvecs 输出旋转向量(Rodrigues 表示)的向量,估计每个立体对中第一相机坐标系下的图案视图。具体来说,每个第 i 个旋转向量连同对应的第 i 个平移向量(见下一个输出参数描述),将校准图案从物体坐标空间变换到立体对中第一相机的相机坐标空间。更技术性地说,第 i 个旋转和平移向量的组合执行了从物体坐标空间到立体对中第一相机的相机坐标空间的基底变换。
- 参数tvecs 输出平移向量的向量,估计每个图案视图,参见上一个输出参数(rvecs)的参数描述。
- 参数perViewErrors 每个图案视图的 RMS 重投影误差的输出向量。
- 参数flags 不同的标志,可能是零或以下值的组合:
- CALIB_FIX_INTRINSIC 固定 cameraMatrix? 和 distCoeffs?,以便只估计 R、T、E 和 F 矩阵。
- CALIB_USE_INTRINSIC_GUESS 根据指定的标志优化一些或全部的内参。初始值由用户提供。
- CALIB_USE_EXTRINSIC_GUESS R 和 T 包含有效的初始值,这些值被进一步优化。否则,R 和 T 初始化为图案视图的中位数(每个维度分别处理)。
- CALIB_FIX_PRINCIPAL_POINT 在优化过程中固定主点。
- CALIB_FIX_FOCAL_LENGTH 固定 f y ( j ) f^{(j)}_y fy(j)和 f y ( j ) f^{(j)}_y fy(j) 。
- CALIB_FIX_ASPECT_RATIO 优化 f y ( j ) f^{(j)}_y fy(j) ,固定比率 f x ( j ) / f y ( j ) f^{(j)}_x/f^{(j)}_y fx(j)/fy(j) 。
- CALIB_SAME_FOCAL_LENGTH 强制 f x ( 0 ) = f x ( 1 ) f^{(0)}_x=f^{(1)}_x fx(0)=fx(1)和 f y ( 0 ) = f y ( 1 ) f^{(0)}_y=f^{(1)}_y fy(0)=fy(1)。
- CALIB_ZERO_TANGENT_DIST 将每个相机的切向畸变系数设为零并固定不变。
- CALIB_FIX_K1,..., CALIB_FIX_K6 在优化过程中不改变相应的径向畸变系数。如果设置了 CALIB_USE_INTRINSIC_GUESS,则使用提供的 distCoeffs 矩阵中的系数;否则,将其设置为 0。
- CALIB_RATIONAL_MODEL 启用系数 k4、k5 和 k6。为了提供向后兼容性,此额外标志应显式指定以使标定函数使用有理模型并返回8个系数。如果不设置此标志,函数计算并返回仅5个畸变系数。
- CALIB_THIN_PRISM_MODEL 启用系数 s1、s2、s3 和 s4。为了提供向后兼容性,此额外标志应显式指定以使标定函数使用薄棱镜模型并返回12个系数。如果不设置此标志,函数计算并返回仅5个畸变系数。
- CALIB_FIX_S1_S2_S3_S4 在优化过程中不改变薄棱镜畸变系数。如果设置了 CALIB_USE_INTRINSIC_GUESS,则使用提供的
distCoeffs 矩阵中的系数;否则,将其设置为 0。 - CALIB_TILTED_MODEL 启用系数 τX 和 τY。为了提供向后兼容性,此额外标志应显式指定以使标定函数使用倾斜传感器模型并返回14个系数。如果不设置此标志,函数计算并返回仅5个畸变系数。
- CALIB_FIX_TAUX_TAUY 在优化过程中不改变倾斜传感器模型的系数。如果设置了 CALIB_USE_INTRINSIC_GUESS,则使用提供的 distCoeffs 矩阵中的系数;否则,将其设置为 0。
- criteria 迭代优化算法的终止条件。
该函数估计了构成立体对的两个相机之间的变换。如果分别计算了一个物体相对于第一个相机和第二个相机的姿态 (R1, T1) 和 (R2, T2),并且对于一个立体相机,两个相机之间的相对位置和方向是固定的,那么这两个姿态肯定相互关联。这意味着,如果已知两个相机之间的相对位置和方向 (R, T),就可以在给定 (R1, T1) 的情况下计算 (R2, T2)。这就是该函数所做的。它计算 (R, T) 使得:
R 2 = R R 1 . R_2=R R_1. R2=RR1.
T 2 = R T 1 + T . T_2=R T_1 + T. T2=RT1+T.
因此,当给定了第一个相机坐标系中的3D点的坐标表示时,可以计算出该点在第二个相机坐标系中的坐标表示:
[ X 2 Y 2 Z 2 1 ] = [ R T 0 1 ] [ X 1 Y 1 Z 1 1 ] . \begin{bmatrix} X_2 \\ Y_2 \\ Z_2 \\ 1 \end{bmatrix} = \begin{bmatrix} R & T \\ 0 & 1 \end{bmatrix} \begin{bmatrix} X_1 \\ Y_1 \\ Z_1 \\ 1 \end{bmatrix}. X2Y2Z21 =[R0T1] X1Y1Z11 .
可选地,该函数还可以计算本质矩阵 E:
E = [ 0 − T 2 T 1 T 2 0 − T 0 − T 1 T 0 0 ] R E = \begin{bmatrix} 0 & -T_2 & T_1 \\ T_2 & 0 & -T_0 \\ -T_1 & T_0 & 0 \end{bmatrix} R E= 0T2−T1−T20T0T1−T00 R
其中 Ti 是平移向量 T 的分量: T = [ T 0 , T 1 , T 2 ] T T=[T_0, T_1, T_2]^T T=[T0,T1,T2]T 。而且,该函数还可以计算基础矩阵 F:
F = c a m e r a M a t r i x 2 − T ⋅ E ⋅ c a m e r a M a t r i x 1 − 1 F = cameraMatrix2^{-T}\cdot E \cdot cameraMatrix1^{-1} F=cameraMatrix2−T⋅E⋅cameraMatrix1−1
除了立体相关的信息外,该函数还可以对两个相机进行完整的标定。然而,由于参数空间的高维性和输入数据中的噪声,函数可能会偏离正确解。如果可以高精度地单独估计每个相机的内参(例如,使用 calibrateCamera),建议这样做并将 CALIB_FIX_INTRINSIC 标志传递给该函数,同时提供计算得到的内参。否则,如果所有参数一次性估计,限制某些参数是有意义的,例如传递 CALIB_SAME_FOCAL_LENGTH 和 CALIB_ZERO_TANGENT_DIST 标志,这通常是一个合理的假设。
类似于 calibrateCamera,该函数最小化来自两个相机所有可用视图的所有点的总重投影误差。函数返回最终的重投影误差值。
代码示例
cpp
#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>
using namespace cv;
using namespace std;
int main()
{
// 假设我们有以下数据
vector< vector< Point3f > > objectPoints; // 物体坐标空间中的3D点数组
vector< vector< Point2f > > imagePoints1; // 第一个相机的图像点数组
vector< vector< Point2f > > imagePoints2; // 第二个相机的图像点数组
// 检查是否已经填充了足够的点
if ( objectPoints.empty() || imagePoints1.empty() || imagePoints2.empty() )
{
cerr << "Error: No calibration points provided." << endl;
return -1;
}
// 确保所有数组大小一致
if ( objectPoints.size() != imagePoints1.size() || objectPoints.size() != imagePoints2.size() )
{
cerr << "Error: The number of object points, image points from camera 1, and image points from camera 2 do not match." << endl;
return -1;
}
// 相机内参矩阵 (假设有初始估计)
Mat cameraMatrix1 = ( Mat_< double >( 3, 3 ) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1 );
Mat cameraMatrix2 = ( Mat_< double >( 3, 3 ) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1 );
// 畸变系数 (假设有初始估计)
Mat distCoeffs1 = Mat::zeros( 8, 1, CV_64F ); // 8参数模型
Mat distCoeffs2 = Mat::zeros( 8, 1, CV_64F ); // 8参数模型
// 图像尺寸
Size imageSize( 640, 480 );
// 输出变量
Mat R, T, E, F;
vector< Mat > rvecs, tvecs;
vector< float > perViewErrors;
// 标定标志和终止条件
int flags = CALIB_FIX_INTRINSIC;
TermCriteria criteria( TermCriteria::COUNT + TermCriteria::EPS, 30, 1e-6 );
// 执行立体相机标定
double rms = stereoCalibrate( objectPoints, // 输入的物体点
imagePoints1, // 第一个相机的图像点
imagePoints2, // 第二个相机的图像点
cameraMatrix1, // 第一个相机的内参矩阵
distCoeffs1, // 第一个相机的畸变系数
cameraMatrix2, // 第二个相机的内参矩阵
distCoeffs2, // 第二个相机的畸变系数
imageSize, // 图像尺寸
R, // 输出的旋转矩阵
T, // 输出的平移向量
E, // 输出的本质矩阵
F, // 输出的基础矩阵
rvecs, // 每个视图的旋转向量
tvecs, // 每个视图的平移向量
perViewErrors, // 每个视图的重投影误差
flags, // 标定标志
criteria // 终止条件
);
cout << "RMS re-projection error: " << rms << endl;
cout << "Rotation Matrix:\n" << R << endl;
cout << "Translation Vector:\n" << T << endl;
cout << "Essential Matrix:\n" << E << endl;
cout << "Fundamental Matrix:\n" << F << endl;
// 可选:打印每个视图的重投影误差
for ( size_t i = 0; i < perViewErrors.size(); ++i )
{
cout << "Reprojection error for view " << i << ": " << perViewErrors[ i ] << endl;
}
return 0;
}
运行结果
数据提供了就会有运行结果