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]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/955759.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AWS物联网连接的数据记录器在冰川环境中的性能比较:Campbell CR1000X与ESP32开源

论文标题 中文&#xff1a;AWS物联网连接的数据记录器在冰川环境中的性能比较&#xff1a;Campbell CR1000X与ESP32开源 英文&#xff1a;Performance comparison of AWS IoT connected dataloggers in glacier environments: Campbell CR1000X vs. ESP32 Open source 作者信…

K8S 节点选择器

今天我们来实验 pod 调度的 nodeName 与 nodeSelector。官网描述如下&#xff1a; 假设有如下三个节点的 K8S 集群&#xff1a; k8s31master 是控制节点 k8s31node1、k8s31node2 是工作节点 容器运行时是 containerd 一、镜像准备 1.1、镜像拉取 docker pull tomcat:8.5-jre8…

Python爬虫学习前传 —— Python从安装到学会一站式服务

早上好啊&#xff0c;大佬们。我们的python基础内容的这一篇终于写好了&#xff0c;啪唧啪唧啪唧…… 说实话&#xff0c;这一篇确实写了很久&#xff0c;一方面是在忙其他几个专栏的内容&#xff0c;再加上生活学业上的事儿&#xff0c;确实精力有限&#xff0c;另一方面&…

【书生大模型实战营】Git 基础知识-L0G3000

本文是书生大模型实战营系列的第三篇文章&#xff0c;本文的主题是&#xff1a;Git基础知识点。 原始教程链接&#xff1a;Tutorial/docs/L0/git/readme.md at camp4 InternLM/Tutorial 1.Git总览 什么是Git&#xff1f; Git是一个分布式版本控制系统&#xff0c;广泛用于…

基于SpringBoot+Vue旅游管理系统的设计和实现(源码+文档+部署讲解)

个人名片 &#x1f525; 源码获取 | 毕设定制| 商务合作&#xff1a;《个人名片》 ⛺️心若有所向往,何惧道阻且长 文章目录 个人名片环境需要技术栈功能介绍功能说明 环境需要 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 数据库&…

python如何解析word文件格式(.docx)

python如何解析word文件格式&#xff08;.docx&#xff09; .docx文件遵从开源的“Office Open XML标准”&#xff0c;这意味着我们能用python的文本操作对它进行操作&#xff08;实际上PPT和Excel也是&#xff09;。而且这并不是重复造轮子&#xff0c;因为市面上操作.docx的…

Visual Studio2019调试DLL

1、编写好DLL代码之后&#xff0c;对DLL项目的属性进行设置&#xff0c;选择待注入的DLL&#xff0c;如下图所示 2、生成DLL文件 3、将DLL设置为启动项目之后&#xff0c;按F5启动调试。弹出选择注入的exe的界面之后&#xff0c;使用代码注入器注入步骤2中生成的dll&#xff…

nginx 配置防爬虫

今天早上查看服务器&#xff0c;发现昨天发布的一个在线解析充电桩协议的网页工具有大量的访问记录&#xff0c;应该是爬虫在爬api接口数据。该工具api接口后台用的是python写的&#xff0c;和大多数项目一样也采用nginx反向代理&#xff0c;由于采用nginx&#xff0c;可以利用…

日志收集Day001

1.ElasticSearch 作用&#xff1a;日志存储和检索 2.单点部署Elasticsearch与基础配置 rpm -ivh elasticsearch-7.17.5-x86_64.rpm 查看配置文件yy /etc/elasticsearch/elasticsearch.yml&#xff08;这里yy做了别名&#xff0c;过滤掉空行和注释行&#xff09; yy /etc/el…

微信小程序-base64加解密

思路&#xff1a;先创建一个base64.js的文件&#xff0c;这个文件可以作为专门加解密的文件模块&#xff0c;需要时就引用&#xff1b;创建好后&#xff0c;引用base64.js里的加解密函数。 注意&#xff1a;引用模块一定要引用正确的路径&#xff0c;否则会报错。 base64.js:…

【网络协议】【http】【https】AES-TLS1.2

【网络协议】【http】【https】AES-TLS1.2 https并不是一个协议 而是在传输层之间添加了SSL/TLS协议TLS TLS 协议用于应用层协议&#xff08;如 HTTP&#xff09;和传输层&#xff08;如 TCP&#xff09;之间&#xff0c;增加了一层安全性来解决 HTTP 存在的问题&#xff0c;H…

打造更安全的Linux系统:玩转PAM配置文件

在Linux系统中&#xff0c;用户认证是确保系统安全的关键步骤。PAM&#xff08;可插拔认证模块&#xff09;为我们提供了一个非常灵活的框架&#xff0c;帮助我们管理各种服务的认证过程。其中&#xff0c;/etc/pam.d目录是PAM配置的核心部分&#xff0c;这里存放了每个服务所需…

无人机技术架构剖析!

一、飞机平台系统 飞机平台系统是无人机飞行的主体平台&#xff0c;主要提供飞行能力和装载功能。它由机体结构、动力装置、电气设备等组成。 机体结构&#xff1a;无人机的机身是其核心结构&#xff0c;承载着其他各个组件并提供稳定性。常见的机身材料包括碳纤维、铝合金、…

【西藏乡镇界面】图层arcgis格式shp数据有乡镇名称和编码2020年wgs84坐标内容测评

西藏乡镇界面图层arcgis格式shp数据有乡镇名称和编码2020年wgs84坐标无偏移

【QT】: 初识 QWidget 控件 | QWidget 核心属性(API) | qrc 文件

&#x1f525; 目录 1. 控件概述 控件体系的发展阶段 2. QWidget 核心属性 2.1 核心属性概览2.2 用件可用&#xff08;Enabled&#xff09; 2.3 坐标系&#xff08;Geometry&#xff09; **实例 1: 控制按钮的位置**实例 2: 表白 程序 2.4 窗口标题&#xff08;windowTiltle&a…

PCM5142集成32位384kHz PCM音频立体声114dB差分输出DAC编解码芯片

目录 PCM5142 简介PCM5142功能框图PCM5142特性 参考原理图 PCM5142 简介 PCM514x 属于单片 CMOS 集成电路系列&#xff0c;由立体声数模转换器 (DAC) 和采用薄型小外形尺寸 (TSSOP) 封装的附加支持电路组成。PCM514x 使用 TI 最新一代高级分段 DAC 架构产品&#xff0c;可实现…

python学opencv|读取图像(三十四)阈值处理-彩色图像

【1】引言 前序已经掌握了使用阈值处理函数控制灰度图的RGB值&#xff0c;相关链接为&#xff1a; python学opencv|读取图像&#xff08;三十三&#xff09;阈值处理图像-限定像素-CSDN博客 在更早的学习中&#xff0c;灰度图的RGB只有一个通道&#xff0c;也就是各个像素点…

jmeter事务控制器-勾选Generate Parent Sample

1、打开jmeter工具&#xff0c;添加线程组&#xff0c;添加逻辑控制器-事务控制器 2、在事务控制器&#xff0c;勾选Generate parent sample&#xff1a;生成父样本&#xff1b;说明勾选后&#xff0c;事务控制器会作为父节点&#xff0c;其下面的请求作为子节点 3、执行&#…

C++ ——— 学习并使用 string 类

目录 学习 string 类 使用 string 类 实例化一个无参数的 string 对象 实例化一个带参数的 string 对象 合并两个字符串&#xff08;重载了加运算符&#xff09; 通过重载[]访问每个字符 迭代器遍历每个字符 范围 for 遍历每个字符 学习 string 类 何为 string 类&…

拟合算法 (matlab工具箱)

拟合算法&#xff1a; 1线性最小二乘法拟合 使用matlab进行求解 拟合优度&#xff1a;R^2 拟合优度的matlab代码&#xff1a; 2,Matlab工具箱的教学 一些函数: 拟合算法&#xff1a; 插值算法中&#xff0c;得到的多项式f(x)要经过所有样本点。但是如果样本点太多&#…