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

news/2025/1/19 2:41:15/
  • 操作系统: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.ppmy.cn/news/1564291.html

相关文章

iOS - 内存屏障的使用场景

内存屏障的使用是为了解决以下几个关键问题&#xff1a; 1. CPU 乱序执行 // 没有内存屏障时&#xff0c;CPU 可能乱序执行 void example() {// 这两行代码可能被 CPU 重排序a 1; // 操作1flag true; // 操作2 }// 使用内存屏障确保顺序 void safeExample() {a 1;…

Mac上安装Label Studio

在Mac上安装Anaconda并随后安装Label Studio&#xff0c;可以按照以下步骤进行&#xff1a; 1. 在Mac上安装Anaconda 首先&#xff0c;你需要从Anaconda的官方网站下载适用于Mac的安装程序。访问Anaconda官网&#xff0c;点击“Download Anaconda”按钮&#xff0c;选择适合M…

【Elasticsearch】搜索类型介绍,以及使用SpringBoot实现,并展现给前端

Elasticsearch 提供了多种查询类型&#xff0c;每种查询类型适用于不同的搜索场景。以下是八种常见的 Elasticsearch 查询类型及其详细说明和示例。 1. Match Query 用途&#xff1a;用于全文搜索&#xff0c;会对输入的文本进行分词&#xff0c;并在索引中的字段中查找这些分…

Spring MVC复杂数据绑定-绑定集合

【图书介绍】《SpringSpring MVCMyBatis从零开始学&#xff08;视频教学版&#xff09;&#xff08;第3版&#xff09;》_【新华文轩】springspring mvcmybatis从零开始学(视频教学版) 第3版 正版-CSDN博客 《SpringSpring MVCMyBatis从零开始学(视频教学版)&#xff08;第3版…

宝塔php7.4安装报错,无法安装,php8以上可以安装,以下的不行,gd库什么的都正常

宝塔的依赖问题导致的问题&#xff0c;最后手动挂载后才解决。。。废了三天三夜终于搞好了。。。。无语&#xff5e; 建议&#xff1a;不要一直升级宝塔版本&#xff0c;升级前备份或者开服务商的实例镜像&#xff0c;方便恢复&#xff0c;不然&#xff0c;可就GG了&#xff5…

Python毕业设计选题:基于django+vue的二手电子设备交易平台设计与开发

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 用户管理 设备类型管理 设备信息管理 系统首页 设备信息…

如何通过高防服务隐藏服务器源IP

在网络安全领域&#xff0c;隐藏服务器的真实源IP地址是保护服务器免受直接攻击的重要手段之一。暴露的源IP地址容易成为黑客攻击的目标&#xff0c;尤其是DDoS攻击、端口扫描和暴力破解等威胁。高防服务&#xff08;如阿里云盾、AWS Shield等&#xff09;不仅提供强大的流量清…

数据库基础练习1(创建表,设置外键,检查,不为空,主键等约束)安装mysql详细步骤

安装MySQL详细步骤 1. 下载 MySQL 安装程序 访问 MySQL 官方网站&#xff1a;MySQL Downloads。在下载页面&#xff0c;选择 "MySQL Community (GPL) Downloads"。在 "MySQL Community Server" 部分&#xff0c;根据你的操作系统&#xff08;Windows&…