当前位置: 首页 > 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…...

力扣热门100题之跳跃游戏

核心思路维护一个变量&#xff1a;当前能跳到的最远位置 maxReach遍历每一位&#xff1a;如果当前位置 已经跳不到了&#xff08;i > maxReach&#xff09;&#xff0c;直接 return false更新 maxReach如果 maxReach > 最后一位下标&#xff0c;提前 return true解释i n…...

3.2 Java 运算符(字符串和字符的加操作)

一、核心概念在 Java 中&#xff0c; 运算符 不仅仅用于数值相加&#xff0c;它还具有 字符串拼接功能。 当表达式中包含 String 类型时&#xff0c; 会优先执行 字符串拼接 操作。关键点&#xff1a; 只要有一个操作数是 String&#xff0c;整个表达式就变成字符串拼接&#x…...

直播推流避坑指南:为什么你的抖音直播总卡顿?可能是选错了流类型

直播推流避坑指南&#xff1a;为什么你的抖音直播总卡顿&#xff1f;可能是选错了流类型 最近帮几个主播朋友排查直播卡顿问题&#xff0c;发现80%的案例都栽在同一个坑里——推流类型选择错误。明明用的是旗舰级设备&#xff0c;千兆宽带&#xff0c;OBS参数也调得飞起&#x…...

避坑指南:在Windows/Mac上部署Sherpa-ONNX语音识别时,VAD和热词匹配的那些常见问题

跨平台部署Sherpa-ONNX语音识别系统的实战避坑手册 当开发者将基于Sherpa-ONNX的语音识别系统从Linux迁移到Windows或macOS平台时&#xff0c;往往会遭遇一系列意想不到的"水土不服"。本文将从实战角度剖析VAD参数调优、热词匹配失效、依赖冲突等高频问题&#xff0c…...

告别单调图表!用C# DevExpress ChartControl打造酷炫数据看板(附甘特图、环形图实战代码)

用C# DevExpress ChartControl构建企业级数据可视化看板实战指南 在数字化转型浪潮中&#xff0c;数据可视化已成为企业决策的核心支撑。传统报表的静态表格早已无法满足现代业务对数据洞察的实时性、交互性和美观性需求。DevExpress ChartControl作为.NET生态中最强大的可视化…...

论文定稿前的最后一道底气

写毕业论文的那段日子&#xff0c;大概是每个大学生、研究生最煎熬的时光。没有固定的上下班时间&#xff0c;没有明确的进度节点&#xff0c;只有堆成山的文献、改不完的初稿&#xff0c;以及导师一句“再完善完善”带来的无尽焦虑。我曾以为&#xff0c;只要多花时间、多查资…...

新版Edge的copilot消失问题解决方案

1.打开C:\Users\用户名\AppData\Local\Microsoft\Edge\User Data2.找到目录下的Local State文件&#xff0c;记事本打开修改variations country为US(或者其他允许使用copilot插件的国家)3.改完保存退出&#xff0c;邮件更改Local State文件的属性为只读更改完成&#xff0c;重启…...

Xilinx FIFO IP 复位与清空:实战场景下的时序控制与设计要点

1. Xilinx FIFO IP 复位机制深度解析 第一次用Xilinx FIFO IP核时&#xff0c;我被复位信号折腾得不轻。明明按照手册操作&#xff0c;仿真时却总出现数据残留。后来才发现&#xff0c;FIFO的复位逻辑藏着不少门道。复位电平配置是第一个关键点&#xff0c;在IP核定制界面有个&…...

当有限元方法遇上神经网络:Deep Ritz Method为何能成为PDE求解的新宠?

当有限元方法遇上神经网络&#xff1a;Deep Ritz Method为何能成为PDE求解的新宠&#xff1f; 在科学计算领域&#xff0c;偏微分方程&#xff08;PDE&#xff09;的数值求解一直是核心挑战。传统方法如有限元法&#xff08;FEM&#xff09;经过半个多世纪的发展已形成完整体系…...

Figma中文插件终极指南:3分钟让Figma界面说中文

Figma中文插件终极指南&#xff1a;3分钟让Figma界面说中文 【免费下载链接】figmaCN 中文 Figma 插件&#xff0c;设计师人工翻译校验 项目地址: https://gitcode.com/gh_mirrors/fi/figmaCN 如果你是一位中文设计师&#xff0c;每天面对Figma全英文界面感到困扰&#…...