当前位置: 首页 > article >正文

OpenCV相机标定与3D重建(60)用于立体校正的函数stereoRectify()的使用

  • 操作系统:ubuntu22.04
  • OpenCV版本:OpenCV4.9
  • IDE:Visual Studio Code
  • 编程语言:C++11

算法描述

为已校准的立体相机的每个头计算校正变换。
cv::stereoRectify 是 OpenCV 中用于立体校正的函数,它基于已知的相机参数和相对位置(通过 cv::stereoCalibrate 或其他方法获得),计算出两个相机的投影矩阵和重映射变换,使得从两个相机获取的图像能够被矫正为仿佛它们是并排且光学轴平行的状态。这样做的目的是为了简化后续的视差图计算。

函数原型

void cv::stereoRectify	
(
	InputArray 	cameraMatrix1,
	InputArray 	distCoeffs1,
	InputArray 	cameraMatrix2,
	InputArray 	distCoeffs2,
	Size 	imageSize,
	InputArray 	R,
	InputArray 	T,
	OutputArray 	R1,
	OutputArray 	R2,
	OutputArray 	P1,
	OutputArray 	P2,
	OutputArray 	Q,
	int 	flags = CALIB_ZERO_DISPARITY,
	double 	alpha = -1,
	Size 	newImageSize = Size(),
	Rect * 	validPixROI1 = 0,
	Rect * 	validPixROI2 = 0 
)		

参数

  • 参数cameraMatrix1:第一个相机的内参矩阵。
  • 参数distCoeffs1:第一个相机的畸变参数。
  • 参数cameraMatrix2:第二个相机的内参矩阵。
  • 参数distCoeffs2:第二个相机的畸变参数。
  • 参数imageSize:用于立体校准的图像尺寸。
  • 参数R:从第一个相机坐标系到第二个相机坐标系的旋转矩阵,详见 stereoCalibrate。
  • 参数T:从第一个相机坐标系到第二个相机坐标系的平移向量,详见 stereoCalibrate。
  • 参数R1:第一个相机的输出3x3校正变换(旋转矩阵)。该矩阵将未校正的第一个相机坐标系中的点转换为校正后的第一个相机坐标系中的点。更技术性地说,它执行了从未校正的第一个相机坐标系到校正后的第一个相机坐标系的基础变换。
  • 参数R2:第二个相机的输出3x3校正变换(旋转矩阵)。该矩阵将未校正的第二个相机坐标系中的点转换为校正后的第二个相机坐标系中的点。同样地,它执行了从未校正的第二个相机坐标系到校正后的第二个相机坐标系的基础变换。
  • 参数P1:第一个相机在新的(校正后)坐标系中的输出3x4投影矩阵,即它将校正后的第一个相机坐标系中的点投影到校正后的第一个相机的图像中。
  • 参数P2:第二个相机在新的(校正后)坐标系中的输出3x4投影矩阵,即它将校正后的第一个相机坐标系中的点投影到校正后的第二个相机的图像中。
  • 参数Q:输出4×4视差到深度映射矩阵(见 reprojectImageTo3D)。
  • 参数flags:操作标志,可以是零或 CALIB_ZERO_DISPARITY。如果设置了该标志,则函数会使每个相机的主点在校正后的视图中具有相同的像素坐标。如果没有设置该标志,函数可能会沿水平或垂直方向(取决于极线的方向)移动图像,以最大化有用图像区域。
  • 参数alpha:自由缩放参数。如果它是 -1 或未指定,函数将执行默认缩放。否则,参数应在 0 和 1 之间。alpha=0 表示校正后的图像被缩放和移动,使得只有有效的像素可见(校正后没有黑色区域)。alpha=1 表示校正后的图像被减少和移动,使得来自原始图像的所有像素都保留在校正后的图像中(不丢失源图像像素)。任何中间值都会产生这两个极端情况之间的中间结果。
  • 参数newImageSize:校正后的新图像分辨率。应该传递给 initUndistortRectifyMap(见 OpenCV 示例目录中的 stereo_calib.cpp 样本)。当传递 (0,0)(默认值)时,它被设置为原始 imageSize。设置为更大的值可以帮助保留原始图像中的细节,尤其是在存在较大径向畸变的情况下。
  • 参数validPixROI1:可选输出矩形,在校正后的图像中包含所有有效像素的区域。如果 alpha=0,则 ROI 覆盖整个图像。否则,它们可能较小(见下图)。
  • 参数validPixROI2:同上,适用于第二个相机。

该函数计算每个相机的旋转矩阵,这些矩阵(虚拟地)使两个相机的图像平面成为同一平面。因此,这使得所有的极线平行,从而简化了密集立体对应问题。函数以 stereoCalibrate 计算的矩阵作为输入,并提供两个旋转矩阵以及两个新坐标系中的投影矩阵作为输出。根据相机的相对位置,函数区分以下两种情况:
水平立体
第一个和第二个相机视图主要沿 x 轴相对偏移(可能有小的垂直偏移)。在校正后的图像中,左右相机中的对应极线是水平的并且具有相同的 y 坐标。P1 和 P2 的形式如下:
P1 = [ f 0 c x 1 0 0 f c y 0 0 0 1 0 ] \texttt{P1} = \begin{bmatrix} f & 0 & cx_1 & 0 \\ 0 & f & cy & 0 \\ 0 & 0 & 1 & 0 \end{bmatrix} P1= f000f0cx1cy1000
P2 = [ f 0 c x 2 T x ⋅ f 0 f c y 0 0 0 1 0 ] , \texttt{P2} = \begin{bmatrix} f & 0 & cx_2 & T_x \cdot f \\ 0 & f & cy & 0 \\ 0 & 0 & 1 & 0 \end{bmatrix} , P2= f000f0cx2cy1Txf00 ,
Q = [ 1 0 0 − c x 1 0 1 0 − c y 0 0 0 f 0 0 − 1 T x c x 1 − c x 2 T x ] \texttt{Q} = \begin{bmatrix} 1 & 0 & 0 & -cx_1 \\ 0 & 1 & 0 & -cy \\ 0 & 0 & 0 & f \\ 0 & 0 & -\frac{1}{T_x} & \frac{cx_1 - cx_2}{T_x} \end{bmatrix} Q= 10000100000Tx1cx1cyfTxcx1cx2
其中 Tx 是相机之间的水平偏移,如果设置了 CALIB_ZERO_DISPARITY,则cx1=cx2。

垂直立体
第一个和第二个相机视图主要沿垂直方向相对偏移(可能有一点水平偏移)。在校正后的图像中,极线是垂直的并且具有相同的 x 坐标。P1 和 P2 的形式如下:
P1 = [ f 0 c x 0 0 f c y 1 0 0 0 1 0 ] \texttt{P1} = \begin{bmatrix} f & 0 & cx & 0 \\ 0 & f & cy_1 & 0 \\ 0 & 0 & 1 & 0 \end{bmatrix} P1= f000f0cxcy11000
P2 = [ f 0 c x 0 0 f c y 2 T y ⋅ f 0 0 1 0 ] , \texttt{P2} = \begin{bmatrix} f & 0 & cx & 0 \\ 0 & f & cy_2 & T_y \cdot f \\ 0 & 0 & 1 & 0 \end{bmatrix}, P2= f000f0cxcy210Tyf0 ,
Q = [ 1 0 0 − c x 0 1 0 − c y 1 0 0 0 f 0 0 − 1 T y c y 1 − c y 2 T y ] \texttt{Q} = \begin{bmatrix} 1 & 0 & 0 & -cx \\ 0 & 1 & 0 & -cy_1 \\ 0 & 0 & 0 & f \\ 0 & 0 & -\frac{1}{T_y} & \frac{cy_1 - cy_2}{T_y} \end{bmatrix} Q= 10000100000Ty1cxcy1fTycy1cy2

其中 Ty 是相机之间的垂直偏移,如果设置了 CALIB_ZERO_DISPARITY,则cy1=cy2。

可以看到,P1 和 P2 的前三列实际上会成为新的“校正”相机矩阵。这些矩阵连同 R1 和 R2 可以传递给 initUndistortRectifyMap 来初始化每个相机的校正映射。

示例截图
下面是来自 stereo_calib.cpp 示例的截图。一些红色水平线通过对应的图像区域,这意味着图像已经很好地进行了校正,这是大多数立体对应算法所依赖的。绿色矩形是 roi1 和 roi2。可以看到,它们内部都是有效的像素。
在这里插入图片描述

代码示例

#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>

using namespace cv;
using namespace std;

// 生成测试图像函数
void generateTestImages( Size imageSize, Mat& img1, Mat& img2 )
{
    img1 = Mat::zeros( imageSize, CV_8UC3 );
    img2 = Mat::zeros( imageSize, CV_8UC3 );

    // 在第一张图像上画水平线,在第二张图像上画稍微偏移的水平线模拟立体图像
    for ( int y = 50; y < imageSize.height; y += 50 )
    {
        line( img1, Point( 0, y ), Point( imageSize.width, y ), Scalar( 0, 255, 0 ), 2 );
        line( img2, Point( 0, y + 10 ), Point( imageSize.width, y + 10 ), Scalar( 0, 255, 0 ), 2 );
    }
}

int main()
{
    // 假设我们已经有以下数据(来自 stereoCalibrate 或其他来源)
    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( 5, 1, CV_64F );  // 简化的畸变系数模型
    Mat distCoeffs2 = Mat::zeros( 5, 1, CV_64F );

    // 假定的旋转和平移矩阵(根据实际情况调整)
    Mat R = ( Mat_< double >( 3, 3 ) << 0.999, 0.001, -0.044, -0.002, 0.998, 0.061, 0.044, -0.061, 0.998 );

    Mat T = ( Mat_< double >( 3, 1 ) << 60.0, 0.0, 0.0 );  // 假设两个相机沿X轴平移了60个单位

    Size imageSize( 640, 480 );  // 图像尺寸

    // 输出变量
    Mat R1, R2;                       // 校正后的旋转矩阵
    Mat P1, P2;                       // 新的投影矩阵
    Mat Q;                            // 视差到深度映射矩阵
    Rect validPixROI1, validPixROI2;  // 有效像素区域

    // 操作标志和自由缩放参数
    int flags         = CALIB_ZERO_DISPARITY;
    double alpha      = -1;
    Size newImageSize = imageSize;  // 使用原始图像尺寸

    // 执行立体校正
    stereoRectify( cameraMatrix1, distCoeffs1, cameraMatrix2, distCoeffs2, imageSize, R, T, R1, R2, P1, P2, Q, flags, alpha, newImageSize, &validPixROI1, &validPixROI2 );

    cout << "Rectification matrices for the first camera:\n" << R1 << endl;
    cout << "Rectification matrices for the second camera:\n" << R2 << endl;
    cout << "Projection matrix for the first camera:\n" << P1 << endl;
    cout << "Projection matrix for the second camera:\n" << P2 << endl;
    cout << "Disparity-to-depth mapping matrix:\n" << Q << endl;

    // 初始化重映射
    Mat map1x, map1y, map2x, map2y;
    initUndistortRectifyMap( cameraMatrix1, distCoeffs1, R1, P1, newImageSize, CV_32FC1, map1x, map1y );
    initUndistortRectifyMap( cameraMatrix2, distCoeffs2, R2, P2, newImageSize, CV_32FC1, map2x, map2y );

    // 生成一对测试图像
    Mat img1, img2;
    generateTestImages( imageSize, img1, img2 );

    // 应用重映射
    Mat rectifiedImg1, rectifiedImg2;
    remap( img1, rectifiedImg1, map1x, map1y, INTER_LINEAR );
    remap( img2, rectifiedImg2, map2x, map2y, INTER_LINEAR );

    // 显示结果
    imshow( "Original Image 1", img1 );
    imshow( "Original Image 2", img2 );
    imshow( "Rectified Image 1", rectifiedImg1 );
    imshow( "Rectified Image 2", rectifiedImg2 );

    waitKey( 0 );  // 等待按键关闭窗口

    return 0;
}

运行结果

在这里插入图片描述
命令行输出:

Rectification matrices for the first camera:
[0.9990327505785522, 0.002838302200161476, -0.0438806029137316;
 -0.001498542304358349, 0.9995325828506972, 0.03053473744403527;
 0.04394675917987673, -0.03043944579710149, 0.9985700388541362]
Rectification matrices for the second camera:
[1, 2.782197548109867e-17, -9.101507439329915e-16;
 -4.437342568756724e-17, 0.9995349355631407, -0.03049446816700398;
 9.047190730013251e-16, 0.03049446816700397, 0.9995349355631405]
Projection matrix for the first camera:
[521, 0, 340.8940467834473, 0;
 0, 521, 249.6826610565186, 0;
 0, 0, 1, 0]
Projection matrix for the second camera:
[521, 0, 340.8940467834473, 31260;
 0, 521, 249.6826610565186, 0;
 0, 0, 1, 0]
Disparity-to-depth mapping matrix:
[1, 0, 0, -340.8940467834473;
 0, 1, 0, -249.6826610565186;
 0, 0, 0, 521;
 0, 0, -0.01666666666666667, 0]


http://www.kler.cn/a/511288.html

相关文章:

  • GraphRAG: Auto Prompt Tuning 实践
  • redis性能优化参考——筑梦之路
  • SVG(Scalable Vector Graphics)全面解析
  • 头歌答案--爬虫实战
  • RabbitMQ基础篇
  • 【PyCharm】连接Jupyter Notebook
  • 51c自动驾驶~合集48
  • 设计模式:责任链模式——行为型模式
  • 【从零开始入门unity游戏开发之——C#篇46】C#补充知识点——命名参数和可选参数
  • Markdown学习笔记(2)
  • C# 控制打印机:从入门到实践
  • 胶质母细胞瘤浸润的个性化预测:数学模型、物理信息神经网络和多模态扫描|文献速递-视觉大模型医疗图像应用
  • 机器学习——什么是代价函数?
  • BUUCTF_Web(October 2019 Twice SQL injection)
  • 【物联网】ARM核介绍
  • C++实现矩阵Matrix类 实现基本运算
  • Python网络自动化运维---SSH模块
  • 《HTML在网络安全中的多面应用:从防范攻击到安全审查》
  • Tarjan算法笔记
  • Py之cv2:cv2(OpenCV,opencv-python)库的简介、安装、使用方法(常见函数、图像基本运算等)
  • golang 在线词典
  • 【gin】gin中使用protbuf消息传输go案例
  • Rust Actix Web 项目实战教程 mysql redis swagger:构建用户管理系统
  • ubuntu的邮件管理软件有哪些
  • HuLa:Github上少有的开源聊天方案,基于Tauri、Vite 5、Vue 3 和 TypeScript 构建的即时通讯系统,拿下它
  • Kotlin数据类