OpenCV三维解算常用方法C++

    如果标定过程是通过OpenCV张正友标定法实现的,得到的内参外参保存在.txt文件中是这样的形式:

① 内参intrinsics.txt:

 ② 外参extrinsics.txt:

    那么可以通过如下方法读取.txt文件获取左右相机内外参,主要包括三维解算需要用到的左右相机内参矩阵、畸变系数,以及左右相机构成双目系统的旋转矩阵和平移矩阵,具体代码如下:

std::string intrinsicsPath{ "D:\\Program Files\\edge下载文件\\030716.16\\030716.16\\intrinsics.txt" }; // 左右相机内参数文件路径
std::string extrinsicsPath{ "D:\\Program Files\\edge下载文件\\030716.16\\030716.16\\extrinsics.txt" }; // 左右相机外参数文件路径// 加载左右相机内参数
cv::Mat cameraMatrixL;  // 左相机内参矩阵
cv::Mat distCoeffsL;    // 左相机畸变参数
cv::Mat cameraMatrixR;  // 右相机内参矩阵
cv::Mat distCoeffsR;    // 右相机畸变参数cv::FileStorage fs(intrinsicsPath, cv::FileStorage::READ);
fs["cameraMatrixL"] >> cameraMatrixL;
fs["cameraDistcoeffL"] >> distCoeffsL;
fs["cameraMatrixR"] >> cameraMatrixR;
fs["cameraDistcoeffR"] >> distCoeffsR;
fs.release();//std::cout << "左相机内参矩阵......" << std::endl;
//std::cout << cameraMatrixL << std::endl;
//std::cout << "左相机畸变参数......" << std::endl;
//std::cout << distCoeffsL << std::endl;
//std::cout << std::endl;
//std::cout << "右相机内参矩阵......" << std::endl;
//std::cout << cameraMatrixR << std::endl;
//std::cout << "右相机畸变参数......" << std::endl;
//std::cout << distCoeffsR << std::endl;
//std::cout << std::endl;// 加载相机外参数
cv::Mat R; // 旋转矩阵
cv::Mat T; // 平移向量fs.open(extrinsicsPath, cv::FileStorage::READ);
fs["R"] >> R;
fs["T"] >> T;
fs.release();

    在得到二维像素坐标之后可以通过畸变校正,三角测量法(Triangulation) 来计算三维点坐标。通常是基于 OpenCV 提供的 cv::triangulatePoints 进行计算,这是一个标准的立体视觉技术,用于通过两个相机视角中的匹配点估算其三维坐标。

    步骤如下代码所示:

    二维坐标→畸变校正→转换到相机坐标系→三维解算。

        std::vector<cv::Point2f> leftPointsUndistort, rightPointsUndistort;cv::undistortPoints(targetsL, leftPointsUndistort, cameraMatrixL, distCoeffsL, cv::Mat(), cameraMatrixL);// targetsL是未做畸变矫正前处理得到的二维中心点坐标(左相机)cv::undistortPoints(targetsR, rightPointsUndistort, cameraMatrixR, distCoeffsR, cv::Mat(), cameraMatrixR);// targetsR是未做畸变矫正前处理得到的二维中心点坐标(右相机)// 转换到相机坐标系std::vector<cv::Point2f> leftPointsCam = pixel2cam(leftPointsUndistort, cameraMatrixL);std::vector<cv::Point2f> rightPointsCam = pixel2cam(rightPointsUndistort, cameraMatrixR);// 求解三维坐标std::vector<cv::Point3f> points3d = triangulation(leftPointsCam, rightPointsCam, R, T);

调用的函数代码:

std::vector<cv::Point3f> triangulation(const std::vector<cv::Point2f>& pts1, const std::vector<cv::Point2f>& pts2, cv::Mat& R, cv::Mat& T)
{cv::Mat T1 = (cv::Mat_<float>(3, 4) << 1, 0, 0, 0,0, 1, 0, 0,0, 0, 1, 0);R.convertTo(R, CV_64FC1);T.convertTo(T, CV_64FC1);cv::Mat T2 = (cv::Mat_<float>(3, 4) <<R.at<double>(0, 0), R.at<double>(0, 1), R.at<double>(0, 2), T.at<double>(0, 0),R.at<double>(1, 0), R.at<double>(1, 1), R.at<double>(1, 2), T.at<double>(1, 0),R.at<double>(2, 0), R.at<double>(2, 1), R.at<double>(2, 2), T.at<double>(2, 0));cv::Mat pts4d;cv::triangulatePoints(T1, T2, pts1, pts2, pts4d);std::vector<cv::Point3f> pts3d;for (int i = 0; i < pts4d.cols; ++i){float x = pts4d.at< float >(0, i) / pts4d.at< float >(3, i);float y = pts4d.at< float >(1, i) / pts4d.at< float >(3, i);float z = pts4d.at< float >(2, i) / pts4d.at< float >(3, i);pts3d.emplace_back(cv::Point3f(x, y, z));}return pts3d;
}std::vector<cv::Point2f> pixel2cam(const std::vector<cv::Point2f>& pts, const cv::Mat& cameraMatrix)
{std::vector<cv::Point2f> ptsCam;for (const auto& p : pts){cv::Point2f c((p.x - cameraMatrix.at<double>(0, 2)) / cameraMatrix.at<double>(0, 0),(p.y - cameraMatrix.at<double>(1, 2)) / cameraMatrix.at<double>(1, 1));ptsCam.emplace_back(c);}return ptsCam;
}

 注:pixel2cam 函数的作用是将像素坐标转换为归一化相机坐标(normalized camera coordinates)。这是必要的,因为 三角测量法 计算三维点的位置时,假设输入的点是在无畸变的相机坐标系下。

这里的 (xc,yc) 是归一化相机坐标,表示 光学中心 归一化后的坐标,它们不再依赖于摄像机的焦距和像素比例,因此可以用于三角测量。 

三角测量的数学原理

  • 三角测量基于 两个不同视角的相机投影矩阵(Projection Matrix)。

  • 如果使用 像素坐标,那么投影矩阵应该是 P=K[R∣T](包含相机内参)。

  • 但如果使用 归一化相机坐标,那么投影矩阵可以简化为 P=[R∣T](去除了相机内参)。

  • 这样可以直接利用 相机外参(R, T) 进行计算,提高准确性。

三角测量计算的流程

  1. 将像素坐标转换为归一化相机坐标pixel2cam

  2. 构造相机投影矩阵

    • 第一个相机位于世界坐标系的原点(通常以左相机为原点):

      P1=[I∣0]
    • 第二个相机的投影矩阵由外参 旋转矩阵 R平移向量 T 给出:

      P2=[R∣T]
  3. 使用 OpenCV 的 triangulatePoints 进行三角测量

    • 通过求解一组线性方程,得到齐次坐标 (X,Y,Z,W)

    • 通过 X′=X/W,Y′=Y/W,Z′=Z/W 得到真实的三维坐标

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

栈和队列相关知识题目

栈的底层原理 栈&#xff08;Stack&#xff09;是一种后进先出&#xff08;LIFO&#xff09;​的线性数据结构&#xff0c;所有操作&#xff08;如插入、删除&#xff09;仅在栈顶进行。它的底层实现可以是数组或链表&#xff0c;具体取决于编程语言和应用场景。 1.基于数组实…

【实战案例】永洪vividime:精准赋能零售行业,实现数据洞察与业务增长

在零售食品行业变革加速、市场竞争白热化的背景下&#xff0c;XX集团作为休闲食品领域头部企业&#xff0c;面临消费趋势变化、宏观经济承压及业绩增长乏力的多重挑战。为破解增长困境&#xff0c;集团将“收入增长金额”确立为核心战略指标&#xff08;北极星指标&#xff09;…

一些题目记录

别人面经题目记录 https://zhuanlan.zhihu.com/p/32626732052 实现 NMS&#xff0c;七八次&#xff0c;很高频&#xff1b; 实现 MultiHeadSelfAttention&#xff0c;大概 三四次&#xff1b; 用 Numpy 或者 List 实现MLP 的前向和反向&#xff0c;4次&#xff1b; Leetcode …

面试题分享-多线程顺序打印奇偶数

目录 1.题目详情 2.解题思路 2.1.分析题目 2.2.解析思路 3.代码实现 4.运行结果 1.题目详情 昨天刷抖音&#xff0c;遇到一个面试题&#xff0c;描述如下&#xff1a; 请使用两个线程&#xff0c;分别顺序交替打印奇数和偶数&#xff0c;直到10为止。例如有两个线程&#…

模型 杜根定律

系列文章分享模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。信心>能力、行动导向、未来时态。 1 杜根定律的应用 1.1 公共政策博弈——底特律市长杜根的保险改革攻坚战 核心挑战&#xff1a;底特律市长Mike Duggan面临汽车保险费率畸高导致居民陷入贫困循环的…

关于在vscode中的Linux 0.11 应用程序项目的生成和运行

首先我们需要需要查看镜像文件 查看软盘镜像文件 floppyb.img 中的内容 在 VSCode 的“Terminal”菜单中选择“Run Build Task...”&#xff0c;会在 VSCode 的顶部中间位置弹出一个 可以执行的 Task 列表&#xff0c;选择其中的“打开 floppyb.img”后会使用 Floppy Editor …

使用CSS3实现炫酷的3D视差滚动效果

使用CSS3实现炫酷的3D视差滚动效果 这里写目录标题 使用CSS3实现炫酷的3D视差滚动效果项目概述核心技术实现1. 3D空间的创建2. 视差层级设置3. 动画效果实现流星动画月亮发光效果 技术难点与解决方案1. 层级重叠问题2. 性能优化3. 响应式适配 开发心得总结 项目概述 在这个项目…

作业12 (2023-05-15 指针概念)

第1题/共11题【单选题】 关于指针的概念,错误的是:( ) A.指针变量是用来存放地址的变量 B.指针变量中存的有效地址可以唯一指向内存中的一块区域 C.野指针也可以正常使用 D.局部指针变量不初始化就是野指针 回答正确 答案解析: A:正确,指针变量中存储的是一个地址,指…

【ESP32S3】esp32获取串口数据并通过http上传到前端

通过前面的学习&#xff08;前面没发过&#xff0c;因为其实就是跑它的demo&#xff09;了解到串口配置以及开启线程实现功能的工作流程&#xff0c;与此同时还有esp32作为STA节点&#xff0c;将数据通过http发送到服务器。 将这两者联合 其实是可以得到一个&#xff1a;esp32获…

《鸿蒙携手AI:解锁智慧出行底层逻辑》

在科技飞速发展的当下&#xff0c;智慧出行成为人们对未来交通的美好期许&#xff0c;而鸿蒙系统与人工智能的深度融合&#xff0c;正为这一愿景的实现提供强大助力。从技术原理角度深入剖析&#xff0c;鸿蒙系统究竟如何支撑人工智能在智慧出行场景中的应用呢&#xff1f;这背…

MyBatis-Plus缓存机制深度解析与SpringBoot整合实战

一、MyBatis-Plus缓存机制全景解析 MyBatis-Plus在MyBatis原生缓存基础上进行了深度增强,形成了多层次的缓存体系: 1. 缓存层级架构 应用层 ├── MP扩展缓存(多租户/逻辑删除) ├── 二级缓存(Mapper级别,跨Session共享) └── 一级缓存(SqlSession级别,默认开…

Day38 | 1365. 有多少小于当前数字的数字、941. 有效的山脉数组、1207. 独一无二的出现次数、283. 移动零、189. 轮转数组

1365. 有多少小于当前数字的数字 题目链接&#xff1a;1365. 有多少小、于当前数字的数字 - 力扣&#xff08;LeetCode&#xff09; 题目难度&#xff1a;简单 代码&#xff1a; class Solution {public int[] smallerNumbersThanCurrent(int[] nums) {Map<Integer,Inte…

数据人的进阶之路:四年数仓实践与成长思考

前言 在数据仓库开发的过程中&#xff0c;常常会遇到很多值得思考的问题&#xff0c;它们不仅关乎技术的深度&#xff0c;也涉及业务理解、个人的成长&#xff0c;甚至是数据行业未来的价值。回顾过去的经历&#xff0c;有很多问题反复出现&#xff0c;甚至成为绕不开的课题&am…

大文件分片上传及断点续传实现

使用 支持分片上传及断点续传 前端使用 vue 2 后端使用 springboot 源码在私信

图解AUTOSAR_SWS_IOHardwareAbstraction

AUTOSAR IO硬件抽象层详解 基于AUTOSAR标准的IO硬件抽象层设计与实现指南 目录 1. 概述2. 架构设计 2.1 模块架构概览2.2 内部组件结构2.3 与其他模块的交互接口 3. 状态机 3.1 状态定义3.2 状态转换3.3 状态行为 4. ADC信号处理流程 4.1 初始化流程4.2 转换请求和处理4.3 通知…

Python正则表达式(一)

目录 一、正则表达式的基本概念 1、基本概念 2、正则表达式的特殊字符 二、范围符号和量词 1、范围符号 2、匹配汉字 3、量词 三、正则表达式函数 1、使用正则表达式&#xff1a; 2、re.match()函数 3、re.search()函数 4、findall()函数 5、re.finditer()函数 6…

北京交通大学第三届C语言积分赛

作者有言在先&#xff1a; 题解的作用是交流思路&#xff0c;不是抄作业的。可以把重点放在思路分析上而不是代码上&#xff0c;毕竟每个人的代码风格是不一样的&#xff0c;看别人的代码就跟做程序填空题一样。先看明白思路再看代码。 还有就是&#xff0c;deepseek真的很好用…

机器学习之条件概率

1. 引言 概率模型在机器学习中广泛应用于数据分析、模式识别和推理任务。本文将调研几种重要的概率模型,包括EM算法、MCMC、朴素贝叶斯、贝叶斯网络、概率图模型(CRF、HMM)以及最大熵模型,介绍其基本原理、算法流程、应用场景及优势。 2. EM算法(Expectation-Maximizati…

硬件基础--03_电流

电流 十九世纪初:[电流方向]是指正电荷的移动方向。 后来:对于金属导体&#xff0c;正电荷没移动&#xff0c;其实是电子在移动。 为了定义的统一性[电流方向]仍然定义为正电荷的移动方向 所以:[电流方向]与[电子移动方向]是相反的。 概念:电荷的定向移动&#xff0c;形成了电…

multi paxos协议

1. Redo Log 同步的核心目标 ​数据一致性&#xff1a;确保所有副本在事务提交后具有相同的数据视图。​容错性&#xff1a;在主副本故障时&#xff0c;从副本能快速接管并恢复数据。​高吞吐&#xff1a;通过批量同步和并行处理提升效率。 2. Multi Paxos 协议的同步流程 M…