OpenCV双目相机外参标定C++
基于OpenCV库实现双目测量系统外参标定过程。通过分析双目测量系统左右相机拍摄的棋盘格标定板图像,包括角点检测、立体标定、立体校正和畸变校正的步骤,获取左右相机的相对位置关系和姿态。
a.检测每张图像中的棋盘格角点,并进行亚像素级精确化;根据左右相机的角点坐标、三维坐标、内参矩阵和畸变系数,计算旋转矩阵R和平移向量T,利用最小二乘法最小化重投影误差;根据标定结果对图像进行畸变校正。
b.输出为双目测量系统的标定结果,包括旋转矩阵、平移向量、投影矩阵和重投影矩阵,并通过imshow展示校正后的图像和视差图,以评估标定质量。
// 单目内参标定后进行双目标定
#include<opencv2/calib3d/calib3d.hpp>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/opencv.hpp>
#include<iostream>
#include <opencv2/core/utils/logger.hpp> //隐藏日志using namespace std;
using namespace cv;int n = 1;
int m = 1;int i = 1;
int j = 1;const int imagewidth = 2048;
const int imagehight = 2048;//图像的长宽
const int boardwidth = 11;
const int boardhight = 8;//图片上棋盘格标定板的内角点个数(行、列的角点数)
const int framenumber = 13;//标定图片数
const int squaresize = 50;//棋盘格标定板单个方格的大小
const cv::Size imagesize = cv::Size(imagewidth, imagehight);
const cv::Size boardsize = cv::Size(boardwidth, boardhight);vector<cv::Point3f> objectpoint;
vector<vector<cv::Point3f>> objpoint;vector<cv::Point2f> cornerL;
vector<cv::Point2f> cornerR;vector<vector<cv::Point2f>> imagepointL;
vector<vector<cv::Point2f>> imagepointR;Mat cameraMatrixL = (Mat_<double>(3, 3) << 2946.368631790921, 0, 992.3628859436697,0, 2947.324578115115, 1041.333927112967,0, 0, 1);
Mat distcoeffL = (Mat_<double>(5, 1) << -0.09314094645325596, 0.212990743663531, 0.001099206303953861, 0.0003299289802971191, 2.911155272910228);
Mat cameraMatrixR = (Mat_<double>(3, 3) << 2935.765597816497, 0, 997.8971330325958,0, 2938.133144049969, 1001.295460381107,0, 0, 1);
//获得的畸变参数
Mat distcoeffR = (Mat_<double>(5, 1) << -0.07849722399107049, -0.05592478850461548, -0.0009348860866305323, 8.96474606278152e-06, 4.375424240166232);cv::Mat R, T, E, F;
cv::Mat R1, R2, P1, P2, Q;cv::Mat maplx, maply, maprx, mapry;cv::Mat imageL, grayimageL;
cv::Mat imageR, grayimageR;cv::Rect validROIL, validROIR;void worldpoint()
{for (int i = 0; i < boardhight; i++){for (int j = 0; j < boardwidth; j++){objectpoint.push_back(cv::Point3f(i * squaresize, j * squaresize, 0.0f));}}for (int w = 1; w <= framenumber; w++){objpoint.push_back(objectpoint);}
}void outputparam()
{cv::FileStorage fs("intrinsics.yml", cv::FileStorage::WRITE);fs << "cameraMatrixL" << cameraMatrixL << "cameraDistcoeffL" << distcoeffL << "cameraMatrixR" << cameraMatrixR << "cameraDistcoeffR" << distcoeffR;fs.release();std::cout << "cameraMatrixL=:" << cameraMatrixL << endl << "cameraDistcoeffL=:" << distcoeffL << endl << "cameraMatrixR=:" << cameraMatrixR << endl << "cameraDistcoeffR=:" << distcoeffR << endl;fs.open("extrinsics.yml", cv::FileStorage::WRITE);fs << "R" << R << "T" << T << "Rl" << R1 << "Rr" << R2 << "Pl" << P1 << "Pr" << P2 << "Q" << Q << "F" << F << "E" << E;std::cout << "R=" << R << endl << "T=" << T << endl << "Rl=" << R1 << endl << "Rr=" << R2 << endl << "Pl=" << P1 << endl << "Pr=" << P2 << endl << "Q=" << Q << endl << "F=" << F << endl << "E=" << E << endl;fs.release();}int main()
{cv::utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);//不再输出日志//或//utils::logging::setLogLevel(utils::logging::LOG_LEVEL_ERROR);//只输出错误日志//读取图片int current = 1;while (current <= framenumber){char frameL[50];sprintf_s(frameL, 50, "external/l%d.bmp", n++);imageL = cv::imread(frameL);cv::cvtColor(imageL, grayimageL, cv::ColorConversionCodes::COLOR_BGR2GRAY);char frameR[50];sprintf_s(frameR, 50, "external/r%d.bmp", m++);imageR = cv::imread(frameR);cv::cvtColor(imageR, grayimageR, cv::ColorConversionCodes::COLOR_BGR2GRAY);bool foundL, foundR;foundL = cv::findChessboardCorners(imageL, boardsize, cornerL);foundR = cv::findChessboardCorners(imageR, boardsize, cornerR);if (foundL == true && foundR == true){cv::cornerSubPix(grayimageL, cornerL, cv::Size(11, 11), cv::Size(-1, -1), cv::TermCriteria(cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS, 30, 1e-6));cv::cornerSubPix(grayimageR, cornerR, cv::Size(11, 11), cv::Size(-1, -1), cv::TermCriteria(cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS, 30, 1e-6));cv::drawChessboardCorners(imageL, boardsize, cornerL, foundL);//cv::imshow("L", imageL);namedWindow("L", WINDOW_NORMAL);imshow("L", imageL);cv::waitKey(10);cv::drawChessboardCorners(imageR, boardsize, cornerR, foundR);//cv::imshow("R", imageR);namedWindow("R", WINDOW_NORMAL);imshow("R", imageR);cv::waitKey(10);imagepointL.push_back(cornerL);imagepointR.push_back(cornerR);cout << "The image " << current << " is good" << endl;}else{std::cout << "The image is bad please try again" << endl;}current++;}worldpoint();cout << "开始标定" << endl;double err = cv::stereoCalibrate(objpoint, imagepointL, imagepointR, cameraMatrixL, distcoeffL, cameraMatrixR, distcoeffR, imagesize, R, T, E, F,CALIB_USE_INTRINSIC_GUESS,cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 30, 1e-6));cout << "The err = " << err << endl;cv::stereoRectify(cameraMatrixL, distcoeffL, cameraMatrixR, distcoeffR, imagesize, R, T, R1, R2, P1, P2, Q, cv::CALIB_ZERO_DISPARITY, -1, imagesize, &validROIL, &validROIR);cv::initUndistortRectifyMap(cameraMatrixL, distcoeffL, R1, P1, imagesize, CV_32FC1, maplx, maply);cv::initUndistortRectifyMap(cameraMatrixR, distcoeffR, R2, P2, imagesize, CV_32FC1, maprx, mapry);outputparam();cv::Mat canvas;double sf;int w, h;sf = 600. / MAX(imagesize.width, imagesize.height);w = cvRound(imagesize.width * sf);h = cvRound(imagesize.height * sf);canvas.create(h, w * 2, CV_8UC3);int currents = 1;while (currents <= framenumber){char frameL[50];sprintf_s(frameL, 50, "external/l%d.bmp", i++);imageL = cv::imread(frameL);char frameR[50];sprintf_s(frameR, 50, "external/r%d.bmp", j++);imageR = cv::imread(frameR);cv::Mat rectifyImageL2, rectifyImageR2;cv::remap(imageL, rectifyImageL2, maplx, maply, cv::InterpolationFlags::INTER_LINEAR);cv::remap(imageR, rectifyImageR2, maprx, mapry, cv::InterpolationFlags::INTER_LINEAR);cv::Mat canvasPart = canvas(cv::Rect(w * 0, 0, w, h));resize(rectifyImageL2, canvasPart, canvasPart.size(), 0, 0, cv::INTER_AREA);cv::Rect vroiL(cvRound(validROIL.x * sf), cvRound(validROIL.y * sf),cvRound(validROIL.width * sf), cvRound(validROIL.height * sf));cv::rectangle(canvasPart, vroiL, cv::Scalar(0, 0, 255), 3, 8);canvasPart = canvas(cv::Rect(w, 0, w, h));resize(rectifyImageR2, canvasPart, canvasPart.size(), 0, 0, cv::INTER_LINEAR);cv::Rect vroiR(cvRound(validROIR.x * sf), cvRound(validROIR.y * sf),cvRound(validROIR.width * sf), cvRound(validROIR.height * sf));cv::rectangle(canvasPart, vroiR, cv::Scalar(0, 255, 0), 3, 8);for (int i = 0; i < canvas.rows; i += 16)line(canvas, cv::Point(0, i), cv::Point(canvas.cols, i), cv::Scalar(0, 255, 0), 1, 8);cv::imshow("rectified", canvas);if (cv::waitKey() > 0){currents++;}}return 0;
}
代码运行结果如下:
相关文章:

OpenCV双目相机外参标定C++
基于OpenCV库实现双目测量系统外参标定过程。通过分析双目测量系统左右相机拍摄的棋盘格标定板图像,包括角点检测、立体标定、立体校正和畸变校正的步骤,获取左右相机的相对位置关系和姿态。 a.检测每张图像中的棋盘格角点,并进行亚像素级精…...

【GESP】C++一级练习BCQM3055,4位数间隔输出
一级知识点取余、整除运算和格式化输出知识点应用。其实也可以用string去处理,那就属于GESP三级的知识点范畴了,孩子暂未涉及。 题目题解详见:https://www.coderli.com/gesp-1-bcqm3055/ https://www.coderli.com/gesp-1-bcqm3055/https://w…...

纯血鸿蒙的最难时刻才开始
关注卢松松,会经常给你分享一些我的经验和观点。 纯血鸿蒙(HarmonyOS NEXT)也正式发布了,绝对是一个历史性时刻,但最难的鸿蒙第二个阶段,也就是生态圈的建设,才刚刚开始。 目前,我劝你现在不要升级到鸿蒙…...
记一个mysql的坑
数据库表user, 存在一个name字段,字段为varchar类型 现在user表有这么两条记录: idnameageclass1NULL18一班2lisi20二班 假如我根据下面这一条件去更新,更新成功数据行显示为0 update user set age 19 where age 18 and class “一班”…...
Java中的设计模式:单例模式详解
摘要 单例模式(Singleton Pattern)是Java中最常用的设计模式之一,属于创建型模式。它的主要目的是确保一个类在系统中只有一个实例,并提供一个全局访问点来访问该实例。 1. 单例模式的定义 单例模式确保一个类只有一个实例&…...

NanoTrack原理与转tensorrt推理
文章目录 前言一、NanoTrack 工作原理二、运行demo与转换tensorrt模型2.1 运行pt模型demo2.2 转onnx模型2.3 转tensorrt模型2.4 运行trt模型推理 三、推理速度对比总结 前言 NanoTrack 是一种轻量级且高效的目标跟踪算法,基于Siamese网络架构,旨在在资源…...

YOLO11改进 | 卷积模块 | 卷积模块替换为选择性内核SKConv【附完整代码一键运行】
秋招面试专栏推荐 :深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 💡💡💡本专栏所有程序均经过测试,可成功执行💡💡💡 本文给大家带来的教程是将YOLO11的卷积替…...

CentOS进入单用户模式进行密码重置
一、单用户模式介绍 单用户模式是一种特殊的启动模式,主要用于系统维护和故障排除。在单用户模式下,系统以最小化的状态启动,只有最基本的系统服务会被加载,通常只有root用户可以登录。这种模式提供了对系统的完全控制࿰…...
bitpoke- mysql-operator cluster
sidecar版本只支持到8.0.35,35可以支持到mysql8.0.35 . 默认镜像是5.7的。需要自己打sidecar的镜像: # Docker image for sidecar containers # https://github.com/bitpoke/mysql-operator/tree/master/images/mysql-operator-sidecar-8.0 # 参考5…...
第5课 基本数据类型
一、数据类型的诞生 在Python的世界里,万物皆对象,每个对象都有自己的若干属性,每一个属性都能描述对象的某一个方面。就像我们每个人,都有自己的身高、年龄、姓名、性别等很多方面的信息,这里的身高、年龄、姓名、性…...

OceanBase 首席科学家阳振坤:大模型时代的数据库思考
2024年 OceanBase 年度大会 即将于10月23日,在北京举行。 欢迎到现场了解更多“SQL AI ” 的探讨与分享! 近期,2024年金融业数据库技术大会在北京圆满举行,聚焦“大模型时代下数据库的创新发展”议题,汇聚了国内外众多…...
国内知名的几个镜像源
在国内,有许多常用的Python库镜像源可以帮助加速库的下载。以下是几个知名的镜像源: 1. 清华大学TUNA协会 网址: https://pypi.tuna.tsinghua.edu.cn/simple命令示例:pip install numpy --index-url https://pypi.tuna.tsinghua.edu.cn/simple2. 阿里云…...

海外著名新闻门户媒体软文发稿之华盛顿独立报-大舍传媒
在当今全球化的时代,信息传播的速度和范围达到了前所未有的程度。对于企业和个人而言,如何在国际舞台上有效地展示自己、传递信息,成为了一项至关重要的任务。而海外媒体发稿,特别是通过像华盛顿独立报这样的知名新闻门户…...
青少年编程与数学 02-002 Sql Server 数据库应用 13课题、函数的编写
青少年编程与数学 02-002 Sql Server 数据库应用 13课题、函数的编写 课题摘要:一、函数内置函数用户定义的函数 (User-Defined Functions, UDFs)使用示例主要特点 二、内置函数数学函数(Mathematical Functions)字符串函数(String Functions…...

关于LaTeX的floatrow包导入后标题无法直接放到浮动体上方
排版一个文章,标题怎么弄都弄不到表格上方,经过阅读帮助文档才发现问题。又是一个坑。 标题位置控制 使用floatrow包之后,类似 \begin{table}[htbp]\caption{xxx。}\label{table1}\centering\begin{tabular}{lcccc}\toprule& \multicol…...

Flutter Image和Text图文组件实战案例
In this section, we’ll go through the process of building a user interface that showcases a product using the Text and Image widgets. We’ll follow Flutter’s best practices to ensure a clean and effective UI structure. 在本节中,我们将使用“Te…...
使用 xlrd 和 xlwt 库进行 Excel 文件操作
使用 xlrd 和 xlwt 库进行 Excel 文件操作 在数据分析和处理的过程中,Excel 文件是最常用的数据存储格式之一。Python 提供了多种库来处理 Excel 文件,其中 xlrd 和 xlwt 是两个经典的库,分别用于读取和写入 Excel 文件。本文将详细介绍如何使用这两个库进行 Excel 文件的操…...
03.04、化栈为队
03.04、化栈为队 1、题目描述 实现一个 MyQueue 类,该类用两个栈来实现一个队列。 2、解题思路 本题要求使用两个栈来实现一个队列。队列遵循先进先出(FIFO)的原则,而栈遵循后进先出(LIFO)的原则。因此…...

Coppelia Sim (v-REP)仿真 机器人3D相机手眼标定与实时视觉追踪 (二)
coppelia sim[V-REP]仿真实现 机器人于3D相机手眼标定与实时视觉追踪 二 zmq API接口python调用python获取3D相机的数据获取彩色相机的数据获取深度相机的数据用matpolit显示 python控制机器人运动直接控制轴的位置用IK运动学直接移动到末端姿态 相机内参的标定记录拍照点的位置…...

苏州金龙技术创新赋能旅游新质生产力
2024年10月23日,备受瞩目的“2024第六届旅游出行大会”在云南省丽江市正式开幕。作为客车行业新质生产力标杆客车,苏州金龙在大会期间现场展示了新V系V12商旅版、V11和V8E纯电车型,为旅游出行提供全新升级方案。 其中,全新15座V1…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

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

优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...

Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...

算法打卡第18天
从中序与后序遍历序列构造二叉树 (力扣106题) 给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。 示例 1: 输入:inorder [9,3,15,20,7…...
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里 脚本1 #!/bin/bash #定义变量 ip10.1.1 #循环去ping主机的IP for ((i1;i<10;i)) doping -c1 $ip.$i &>/dev/null[ $? -eq 0 ] &&am…...