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

Opencv双目校正函数 stereoRectify 详解

目录

    • 函数的[官方解释](https://docs.opencv.org/4.x/d9/d0c/group__calib3d.html#ga617b1685d4059c6040827800e72ad2b6)
      • 函数原型
      • 参数说明:
        • 输入参数:
        • 输出参数:
    • 函数代码测试

函数的官方解释

函数原型

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 ) 	

函数作用是得到矫正的双目图像所需的变换矩阵和投影矩阵,然后可以传给 initUndistortRectifyMap 函数生成矫正图像到原始图像的像素坐标的映射,最后使用 remap 函数得到校正的双目图像。
上面说的校正的双目图像特点是:1. 双目相机的成像平面为同一平面;2. 左右目图像同一极线平行且极线上所有点的 y 坐标相等。

参数说明:

输入参数:

cameraMatrix1:左目相机内参矩阵
distCoeffs1:左目相机畸变参数
cameraMatrix2:右目相机内参矩阵
distCoeffs2:右目相机畸变参数
imageSize:图像大小
R:左目相机坐标系到右目相机坐标系的旋转变换,即 R r l R_{rl} Rrl
T:左目相机坐标系到右目相机坐标系的平移变换,即 t r l t_{rl} trl

flags:如果设置为 CALIB_ZERO_DISPARITY,函数会将两个相机的 principal point 设成一样。否则就会平移图像最大化有用的图像区域。
alpha:自由缩放参数。如果设置为 -1 或者不设置,函数执行默认缩放。否则参数应为 0-1 。0:矫正图像会放大和平移使得最终图像中只有有效像素;1:图像会缩小和平移使得原始图像中所有像素都可见。
newImageSize:矫正后的图像分辨率。默认(0,0),设置为原始图像大小。设置为高的分辨率可以保持原始图像的更多细节,特别是畸变较大的时候。
validPixROI1:一个最多地包含有效像素的长方形。(左目图像)
validPixROI2:一个最多地包含有效像素的长方形。(右目图像)

输出参数:

R1:矫正旋转矩阵。将第一个相机坐标系下未矫正的点变换到第一个相机矫正坐标系下,即 R_{左矫正坐标系}{左未矫正坐标系}
R2:矫正旋转矩阵。将第二个相机坐标系下未矫正的点变换到第二个相机矫正坐标系下,即 R_{右矫正坐标系}{右未矫正坐标系}
P1:3x4左相机投影矩阵。将左矫正坐标系下的点投影到左矫正坐标系图像平面坐标系。
P2:3x4右相机投影矩阵。将左矫正坐标系下的点投影到右矫正坐标系图像平面坐标系。
Q:4x4的视差深度映射矩阵。

对于水平双目相机(大部分的双目相机),其中 P1, P2, Q 定义如下:

P2 = [ f 0 c x 2 T x ⋅ f 0 f c y 0 0 0 1 0 ] , P2 = [ f 0 c x 2 T x ⋅ f 0 f c y 0 0 0 1 0 ] , 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 ] \begin{aligned} \texttt{P2} &= \begin{bmatrix} f & 0 & cx_2 & T_x \cdot f \\ 0 & f & cy & 0 \\ 0 & 0 & 1 & 0 \end{bmatrix} ,\\ \texttt{P2} &= \begin{bmatrix} f & 0 & cx_2 & T_x \cdot f \\ 0 & f & cy & 0 \\ 0 & 0 & 1 & 0 \end{bmatrix} ,\\ \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} \end{aligned} P2P2Q= f000f0cx2cy1Txf00 ,= f000f0cx2cy1Txf00 ,= 10000100000Tx1cx1cyfTxcx1cx2

函数代码测试

我们使用EuRoC MAV数据集的双目相机参数对这个函数做测试:

#include <opencv2/core.hpp>
#include <opencv2/calib3d.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
#include <Eigen/Core>
#include <Eigen/Geometry>using namespace cv;
using namespace std;int main(int argc, char **argv)
{if(argc != 3){cerr << "Error: please input 2 images." << endl;return -1;}// Euroc 数据集双目相机内参Mat Kl = ( Mat_<float>(3,3) << 458.654, 0., 367.215, 0., 457.296, 248.375, 0., 0., 1.);Mat Kr = ( Mat_<float>(3,3) << 457.587, 0., 379.999, 0., 456.134, 255.238, 0., 0., 1.);Mat Dl = ( Mat_<float>(1,4) << -0.28340811, 0.07395907, 0.00019359, 1.76187114e-05 );       // 畸变参数Mat Dr = ( Mat_<float>(1,4) << -0.28368365,  0.07451284, -0.00010473, -3.55590700e-05 );// 双目相机相对位姿Mat R_rl = ( Mat_<double>(3,3) <<    9.99997256e-01,   2.31206719e-03,   3.76008102e-04, -2.31713572e-03,   9.99898049e-01,   1.40898358e-02,-3.43393121e-04,  -1.40906685e-02,   9.99900663e-01 );Mat t_rl = ( Mat_<double>(3,1) << -0.11007381,  0.00039912, -0.0008537 );       // 变换的数据类型需要时double的,不然之后执行opencv的函数会报错Mat img_src_l = imread(argv[1], IMREAD_UNCHANGED);    // 读取左右目图像Mat img_src_r = imread(argv[2], IMREAD_UNCHANGED);cout << "read images finished. " << endl;int width = img_src_l.cols, height = img_src_l.rows;Mat Rl, Rr, Pl, Pr, Q;Mat undistmap1l, undistmap2l, undistmap1r, undistmap2r;stereoRectify( Kl, Dl, Kr, Dr, Size(width, height), R_rl, t_rl, Rl, Rr, Pl, Pr, Q, cv::CALIB_ZERO_DISPARITY, 0 );cout << "stereo rectify finished. " << endl;initUndistortRectifyMap( Kl, Dl, Rl, Pl, cv::Size(width,height), CV_16SC2, undistmap1l, undistmap2l );initUndistortRectifyMap( Kr, Dr, Rr, Pr, cv::Size(width,height), CV_16SC2, undistmap1r, undistmap2r );// 将 R_21 和 t_21 转换为两个校正相机坐标系的变换cout << "R_rl before rectification: " << endl << R_rl << endl;R_rl = Rr * R_rl * Rl.t();cout << "R_rl after rectification: " << endl << R_rl << endl;cout << "t_rl before rectification: " << endl << t_rl.t() << endl;t_rl = Rr * t_rl;cout << "t_rl after rectification: " << endl << t_rl.t() << endl;// 打印投影矩阵cout << "Pl: " << endl << Pl << endl;cout << "Pr: " << endl << Pr << endl;// 得到校正图像Mat img_rtf_l, img_rtf_r;remap( img_src_l, img_rtf_l, undistmap1l, undistmap2l, cv::INTER_LINEAR );remap( img_src_r, img_rtf_r, undistmap1r, undistmap2r, cv::INTER_LINEAR );Mat img_src, img_rtf;hconcat(img_src_l, img_src_r, img_src);hconcat(img_rtf_l, img_rtf_r, img_rtf);cvtColor(img_src, img_src, COLOR_GRAY2BGR);cvtColor(img_rtf, img_rtf, COLOR_GRAY2BGR);// 绘制平行线for(int i = 1, iend = 8; i < iend; i++){int h = height/iend * i;line(img_src, Point2i(0, h), Point2i(width*2, h), Scalar(0,0,255));line(img_rtf, Point2i(0, h), Point2i(width*2, h), Scalar(0,0,255));}imshow("image_src", img_src);imshow("image_rtf", img_rtf);waitKey(0);return 0;
}

校正前后 左目相机到右目相机的旋转矩阵:

R_rl before rectification: 
[0.999997256, 0.00231206719, 0.000376008102;-0.00231713572, 0.999898049, 0.0140898358;-0.000343393121, -0.0140906685, 0.999900663]
R_rl after rectification: 
[0.999999999522207, -2.395107227679009e-12, 6.39099232960692e-12;-2.39902704968578e-12, 1.000000000493077, -4.695780200869464e-11;6.390414450718838e-12, -4.698293904886045e-11, 1.00000000036201]

校正后的旋转矩阵几乎与单位矩阵相等,说明两个相机的成像平面平行。

校正前后 左目相机到右目相机的平移向量:

t_rl before rectification: 
[-0.11007381, 0.00039912, -0.0008537]
t_rl after rectification: 
[-0.1100778440394819, -1.899859817072477e-16, 5.956099065901966e-16]

校正后两个相机的位置仅在x方向上存在偏移,又因为两个相机成像平面平行,所以两个相机的成像平面为为同一平面。

校正后的投影矩阵:

Pl: 
[436.2345881250716, 0, 364.4412384033203, 0;0, 436.2345881250716, 256.9516830444336, 0;0, 0, 1, 0]
Pr: 
[436.2345881250716, 0, 364.4412384033203, -48.01976295625924;0, 436.2345881250716, 256.9516830444336, 0;0, 0, 1, 0]

左侧 3x3 的矩阵为内参矩阵。
因为参数 flags 设置为 CALIB_ZERO_DISPARITY,所以校正后的两个相机的 principal point 相等,进而内参矩阵相等。

在校正前后双目图像中绘制与 x 轴平行的直线:

在这里插入图片描述
在这里插入图片描述
校正后左右目图像同一极线平行且极线上所有点的 y 坐标相等。

相关文章:

Opencv双目校正函数 stereoRectify 详解

目录 函数的[官方解释](https://docs.opencv.org/4.x/d9/d0c/group__calib3d.html#ga617b1685d4059c6040827800e72ad2b6)函数原型参数说明&#xff1a;输入参数&#xff1a;输出参数&#xff1a; 函数代码测试 函数的官方解释 函数原型 void cv::stereoRectify ( InputArr…...

实现前端开发几个常用技巧

如何知道iframe下载完成 定时器轮询监听readyState的状态&#xff0c;如果是 complete 或者 interactive 说明文件加载完成。 常用的全屏居中 JS 函数 JS实现deepCopy 生成星级评分 JS数组扁平化之简单方法实现 toString 优点&#xff1a;简单&#xff0c;方便&#xff0c;对…...

Vue3 在SCSS中使用v-bind

template 先创建一个通用的页面结构 <template><div class"v-bubble-bg"></div> </template>js 在JS中先对需要用的数据进行定义&#xff1a; 可以是参数&#xff0c;也可以是data <script setup>const props defineProps({bgCol…...

玩转地下管网三维建模:MagicPipe3D系统

地下管网是保障城市运行的基础设施和“生命线”。随着实景三维中国建设的推进&#xff0c;构建地下管网三维模型与地上融合的数字孪生场景&#xff0c;对于提升智慧城市管理至关重要&#xff01;针对现有三维管线建模数据差异大、建模交互弱、模型效果差、缺乏语义信息等缺陷&a…...

11.以太网交换机工作原理

目录 一、以太网协议二、以太网交换机原理三、交换机常见问题思考四、同网段数据通信全过程五、跨网段数据通信全过程六、关键知识七、调试命令 前言&#xff1a;在网络中传输数据时需要遵循一些标准&#xff0c;以太网协议定义了数据帧在以太网上的传输标准&#xff0c;了解以…...

【通信基础知识】完整通信系统的流程图及各模块功能详解

2024.2.29 抱歉最近在写毕设大论文&#xff0c;因此没有太多时间更新。然而&#xff0c;在写论文的过程中&#xff0c;发现自己对通信系统的了解还不够全明白&#xff0c;因此差了一些硕博论文总结了一个完整的通信系统流程图。若有不对的地方请多多指正//部分内容有参考ChatGP…...

k8s-项目测试环境部署

部署规划 概述 项目开发好后&#xff0c;我们需要部署&#xff0c;我们接下来就基于 阿里云云效 阿里云容器镜像服务 k8s 搭建部署环境 阿里云云效 : 放代码&#xff0c;可以做cicd&#xff08;https://www.aliyun.com/product/yunxiao&#xff09; 阿里云容器镜像服务 :…...

【Elasticsearch管理】缓存机制

文章目录 缓存Field data cache&#xff08;字段数据缓存&#xff09;Node query cache&#xff08;节点查询缓存&#xff09;Indexing buffer&#xff08;索引缓冲区&#xff09;Shard request cache&#xff08;分片请求缓存&#xff09;缓存失败启用/禁用缓存根据请求启用/禁…...

JS api基础初学

轮播图随机版 需求&#xff1a;当我们刷新页面&#xff0c;页面中的轮播图会显示不同图片以及样式 分析&#xff1a;①&#xff1a;准备一个数组对象&#xff0c;里面包含详细信息&#xff08;素材包含&#xff09; ②&#xff1a;随机选择一个数字&#xff0c;选出数组对应…...

uniapp实战:父子组件传参之子组件数量动态变化

需求说明 现有的设置单元列表,每个带有虚线加号的可以看做是一组设置单元,点击加号可以添加一组设置单元.点击设置单元右上角可以删除对应的设置单元. 实现思路说明 利用数组元素添加或是删除的方式实现页面数量动态变化.由于每个设置单元内容都相同所以单独封装了一个子组件.…...

Ubuntu绑定USB接口到固定端口

绑定端口 打开终端&#xff0c;输入以下命令查看USB端口信息&#xff1a; udevadm info -a -n /dev/ttyUSB0执行后&#xff0c;可以看到部分输出如下: 找到第一个&#xff0c;a-b:c格式的KERNELS&#xff0c;记住这个值&#xff0c;后面会用到。 linlin-B660M-D2H-DDR4:~$ u…...

解决gogs勾选“使用选定的文件和模板初始化仓库”报错500,gogs邮件发送失败,gogs邮件配置不生效,gogs自定义模板等问题

解决gogs勾选“使用选定的文件和模板初始化仓库”报错500,gogs邮件发送失败,gogs邮件配置不生效,gogs自定义模板等问题 前几天出了教程本地部署gogs&#xff0c;在后期运行时发现两个问题&#xff1a; 第一&#xff1a;邮件明明配置了&#xff0c;后台显示未配置&#xff0c;…...

数字后端——DEF文件格式

文章目录 MACRO的不同orientationDEF中在macro orientation定义前需要留空格 MACRO的不同orientation DEF中在macro orientation定义前需要留空格 像下图中这种方向和分号之间没有空格的情况&#xff0c;就是有问题的格式。...

【可做课设、附完整技术文档】流式、异步、实时的Django聊天室!(需进一步定制可联系本人)

介绍 完整源码以及完整项目文档请看源码链接。 此Django项目实现了一个精致易扩展的实时聊天室&#xff0c;可直接作为网页开发的课程设计提交&#xff0c;也可二次开发&#xff0c;比如添加更好看的样式&#xff0c;或者更多更酷炫的功能。 实现了如下功能&#xff1a; 流…...

网络编程:基于TCP和UDP的服务器、客户端

1.基于TCP通信服务器 程序代码&#xff1a; 1 #include<myhead.h>2 #define SER_IP "192.168.126.121"//服务器IP3 #define SER_PORT 8888//服务器端口号4 int main(int argc, const char *argv[])5 {6 //1.创建用于监听的套接字7 int sfd-1;8 sf…...

kubectl 命令行管理K8S(上)

目录 陈述式资源管理方式 介绍 命令 项目的生命周期 创建 kubectl create命令 发布 kubectl expose命令 更新 kubectl set 回滚 kubectl rollout 删除 kubectl delete 应用发布策略 金丝雀发布 陈述式资源管理方式 介绍 1.kubernetes 集群管理集群资源…...

Redis 之四:Redis 事务和乐观锁

事务特点 Redis 事务可以一次执行多个命令&#xff0c; 并且带有以下三个重要的保证&#xff1a; 批量操作在发送 EXEC 命令前被放入队列缓存。 收到 EXEC 命令后进入事务执行&#xff0c;事务中任意命令执行失败&#xff0c;其余的命令依然被执行。不具备原子性。 在事务执…...

C# WPF编程-创建项目

1.创建新项目 选择“WPF应用程序”》“下一步” 设置项目 设置项目名称&#xff0c;保存位置等参数>下一步 3.选择框架 4.项目创建成功 5.运行项目...

密码学及其应用(应用篇15)——0/1背包问题

1 问题背景 背包问题是一个经典的优化问题&#xff0c;在计算机科学和运筹学中有着广泛的应用。具体到你提到的这个问题&#xff0c;它是背包问题中的一个特例&#xff0c;通常被称为0/1背包问题。这里&#xff0c;我们有一系列的正整数 &#xff0c;以及一个正整数&#xff0c…...

基于springboot+vue的实验室管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

k8s业务程序联调工具-KtConnect

概述 原理 工具作用是建立了一个从本地到集群的单向VPN&#xff0c;根据VPN原理&#xff0c;打通两个内网必然需要借助一个公共中继节点&#xff0c;ktconnect工具巧妙的利用k8s原生的portforward能力&#xff0c;简化了建立连接的过程&#xff0c;apiserver间接起到了中继节…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案&#xff0c;如果正确地操作&#xff0c;重启Eureka集群中的节点&#xff0c;对已经注册的服务影响非常小&#xff0c;甚至可以做到无感知。 但如果操作不当&#xff0c;可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度

文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...