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= f000f0cx2cy1Tx⋅f00 ,= f000f0cx2cy1Tx⋅f00 ,= 10000100000−Tx1−cx1−cyfTxcx1−cx2
函数代码测试
我们使用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)函数原型参数说明:输入参数:输出参数: 函数代码测试 函数的官方解释 函数原型 void cv::stereoRectify ( InputArr…...

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

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

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

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

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

k8s-项目测试环境部署
部署规划 概述 项目开发好后,我们需要部署,我们接下来就基于 阿里云云效 阿里云容器镜像服务 k8s 搭建部署环境 阿里云云效 : 放代码,可以做cicd(https://www.aliyun.com/product/yunxiao) 阿里云容器镜像服务 :…...
【Elasticsearch管理】缓存机制
文章目录 缓存Field data cache(字段数据缓存)Node query cache(节点查询缓存)Indexing buffer(索引缓冲区)Shard request cache(分片请求缓存)缓存失败启用/禁用缓存根据请求启用/禁…...

JS api基础初学
轮播图随机版 需求:当我们刷新页面,页面中的轮播图会显示不同图片以及样式 分析:①:准备一个数组对象,里面包含详细信息(素材包含) ②:随机选择一个数字,选出数组对应…...

uniapp实战:父子组件传参之子组件数量动态变化
需求说明 现有的设置单元列表,每个带有虚线加号的可以看做是一组设置单元,点击加号可以添加一组设置单元.点击设置单元右上角可以删除对应的设置单元. 实现思路说明 利用数组元素添加或是删除的方式实现页面数量动态变化.由于每个设置单元内容都相同所以单独封装了一个子组件.…...
Ubuntu绑定USB接口到固定端口
绑定端口 打开终端,输入以下命令查看USB端口信息: udevadm info -a -n /dev/ttyUSB0执行后,可以看到部分输出如下: 找到第一个,a-b:c格式的KERNELS,记住这个值,后面会用到。 linlin-B660M-D2H-DDR4:~$ u…...

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

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

【可做课设、附完整技术文档】流式、异步、实时的Django聊天室!(需进一步定制可联系本人)
介绍 完整源码以及完整项目文档请看源码链接。 此Django项目实现了一个精致易扩展的实时聊天室,可直接作为网页开发的课程设计提交,也可二次开发,比如添加更好看的样式,或者更多更酷炫的功能。 实现了如下功能: 流…...

网络编程:基于TCP和UDP的服务器、客户端
1.基于TCP通信服务器 程序代码: 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 事务可以一次执行多个命令, 并且带有以下三个重要的保证: 批量操作在发送 EXEC 命令前被放入队列缓存。 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。不具备原子性。 在事务执…...

C# WPF编程-创建项目
1.创建新项目 选择“WPF应用程序”》“下一步” 设置项目 设置项目名称,保存位置等参数>下一步 3.选择框架 4.项目创建成功 5.运行项目...
密码学及其应用(应用篇15)——0/1背包问题
1 问题背景 背包问题是一个经典的优化问题,在计算机科学和运筹学中有着广泛的应用。具体到你提到的这个问题,它是背包问题中的一个特例,通常被称为0/1背包问题。这里,我们有一系列的正整数 ,以及一个正整数,…...

基于springboot+vue的实验室管理系统(前后端分离)
博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战,欢迎高校老师\讲师\同行交流合作 主要内容:毕业设计(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
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...

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

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...

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

如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...