OpenCV单目相机内参标定C++
基于OpenCV 实现单目相机内参标定:
a.使用OpenCV库实现内参标定过程。通过角点检测、亚像素角点定位、角点存储与三维坐标生成和摄像机标定分别获取左右相机的内参。
b.具体地,使用库函数检测两组图像(左右相机拍摄图像)中棋盘格的角点;对检测到的角点进行亚像素级别的精确化,以提高角点位置的准确性;根据左右相机拍摄的棋盘格图像及角点位置,计算出两个相机的内参矩阵及畸变系数;生成畸变校正和图像校正的映射表。
c.内参标定模块的输出是左右相机的内参矩阵和畸变系数,以及每幅图像的旋转向量和平移向量,最后计算并输出标定结果的总体平均误差,以评估标定过程的准确性。
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/calib3d/calib3d.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <fstream>
#include<sstream>
#include<opencv2/imgproc/types_c.h>
#include <opencv2/core/utils/logger.hpp> //隐藏日志using namespace std;
using namespace cv;
int main()
{cv::utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);//不再输出日志//或//utils::logging::setLogLevel(utils::logging::LOG_LEVEL_ERROR);//只输出错误日志int image_count = 20;//检测的图片数量Size image_size;Size board_size = Size(11, 8);//图片上棋盘格(标定板)的内角点个数(行、列的角点数)vector<Point2f> image_corners;//缓存每幅图像上检测到的角点vector<vector<Point2f>> corners_Seq;//保存检测到的所有角点vector<Mat> image_Seq;int count = 0;/*********************读入图像,检测角点********************************************/for (int i = 0; i < image_count; i++){//读入一系列图片string imageFileName, imageFileName1;stringstream StrStm;StrStm << "left";//StrStm << "F:/vsprojects2022/ConsoleApplication6/ConsoleApplication6/20241022/right";StrStm << i + 1;StrStm >> imageFileName;StrStm.str("");//清除数据流,以便下次使用imageFileName += ".bmp";cout << imageFileName << endl;Mat image = imread(imageFileName);image_size = image.size();Mat image_gray;Mat dstImage1;//cvtColor(image, image_gray, CV_RGB2GRAY); // opencv4版本中应改为 COLOR_RGB2GRAYcvtColor(image, image_gray, COLOR_RGB2GRAY);/*imshow("1",image_gray);waitKey(0);*///检测标定板的角点bool patternfound = findChessboardCorners(image, board_size, image_corners, CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE);//+ CALIB_CB_FAST_CHECK);if (!patternfound){cout << "Can not find chessboard corners!" << endl;return -1;}else{/* 亚像素精确化 */cornerSubPix(image_gray, image_corners, Size(11, 11), Size(-1, -1), TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1));/* 绘制检测到的角点并保存 *//*drawChessboardCorners(image, Size(7, 7), image_corners, patternfound);//红色为先检测的点resize(image, dstImage1, Size(image.cols / 5, image.rows / 5), 0, 0, INTER_LINEAR);imshow("1", dstImage1);waitKey(0);*/Mat imageTemp = image.clone();for (int j = 0; j < image_corners.size(); j++){circle(imageTemp, image_corners[j], 3, Scalar(0, 0, 255), -1, 8, 0);}string imageFileName;std::stringstream StrStm;StrStm << i + 1;StrStm >> imageFileName;imageFileName += "_corner.jpg";imwrite(imageFileName, imageTemp);//保存角点检测结果图count = count + image_corners.size();corners_Seq.push_back(image_corners);}image_Seq.push_back(image);}/****************************摄像机标定****************************************/Size square_size = Size(50, 50); /**** 实际测量得到的标定板上每个棋盘格的大小 ****/vector<vector<Point3f>> object_Points; /**** 保存标定板上角点的三维坐标 ****/Mat image_points = Mat(1, count, CV_32FC2, Scalar::all(0)); /***** 保存提取的所有角点 *****/vector<int> point_counts; /***** 每幅图像中角点的数量 ****/Mat intrinsic_matrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); /***** 摄像机内参数矩阵 ****///Mat distortion_coeffs = Mat(1, 4, CV_32FC1, Scalar::all(0)); /* 摄像机的4个畸变系数:k1,k2,p1,p2 */Mat distortion_coeffs = Mat(1, 5, CV_32FC1, Scalar::all(0)); /* 摄像机的4个畸变系数:k1,k2,p1,p2 */vector<cv::Mat> rotation_vectors; /* 每幅图像的旋转向量 */vector<cv::Mat> translation_vectors; /* 每幅图像的平移向量 *//* 初始化标定板上角点的三维坐标 */for (int t = 0; t < image_count; t++){vector<Point3f> tempPointSet;for (int i = 0; i < board_size.height; i++){for (int j = 0; j < board_size.width; j++){/* 假设标定板放在世界坐标系中z=0的平面上 */Point3f tempPoint;tempPoint.x = i * square_size.width;tempPoint.y = j * square_size.height;tempPoint.z = 0;tempPointSet.push_back(tempPoint);}}object_Points.push_back(tempPointSet);}/* 初始化每幅图像中的角点数量,这里我们假设每幅图像中都可以看到完整的标定板 */for (int i = 0; i < image_count; i++){point_counts.push_back(board_size.width * board_size.height);}/*************************开始标定 *******************************///根据角点的世界坐标和像素坐标求参数calibrateCamera(object_Points, corners_Seq, image_size, intrinsic_matrix, distortion_coeffs, rotation_vectors, translation_vectors, 0);/***********************显示标定结果***************************/Mat rotation_matrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); /* 保存每幅图像的旋转矩阵 */cout << "相机的内参矩阵" << intrinsic_matrix << endl;cout << "相机的畸变系数" << distortion_coeffs << endl;ofstream outfile;outfile.open("内参标定结果.txt");outfile << "相机的内参矩阵为:" << endl << intrinsic_matrix << endl;outfile << "相机的畸变系数为:" << endl << distortion_coeffs << endl;outfile.close();cout << "输出每幅图的旋转向量和平移向量" << endl;for (int i = 0; i < image_count; i++){cout << "第" << i + 1 << "幅图的旋转向量" << rotation_vectors[i] << endl;/* 将旋转向量转换为相对应的旋转矩阵 */Rodrigues(rotation_vectors[i], rotation_matrix);cout << "第" << i + 1 << "幅图像的旋转矩阵:" << endl;cout << rotation_matrix << endl;cout << "第" << i + 1 << "幅图像的平移向量:" << endl;cout << translation_vectors[i] << endl;}/******************对标定结果进行评价**********************************/cout << "开始评价标定结果………………" << endl;double total_err = 0.0; /* 所有图像的平均误差的总和 */double err = 0.0; /* 每幅图像的平均误差 */vector<Point2f> image_points2; /**** 保存重新计算得到的投影点 ****/for (int i = 0; i < image_count; i++){vector<Point3f> tempPointSet = object_Points[i];/**** 通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点 ****/projectPoints(tempPointSet, rotation_vectors[i], translation_vectors[i], intrinsic_matrix, distortion_coeffs, image_points2);/* 计算新的投影点和旧的投影点之间的误差*/vector<Point2f> tempImagePoint = corners_Seq[i];Mat tempImagePointMat = Mat(1, tempImagePoint.size(), CV_32FC2);Mat image_points2Mat = Mat(1, image_points2.size(), CV_32FC2);for (size_t i = 0; i != tempImagePoint.size(); i++){image_points2Mat.at<Vec2f>(0, i) = Vec2f(image_points2[i].x, image_points2[i].y);tempImagePointMat.at<Vec2f>(0, i) = Vec2f(tempImagePoint[i].x, tempImagePoint[i].y);}err = norm(image_points2Mat, tempImagePointMat, NORM_L2);total_err += err /= point_counts[i];cout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;}cout << "总体平均误差:" << total_err / image_count << "像素" << endl;Mat mapx = Mat(image_size, CV_32FC1);Mat mapy = Mat(image_size, CV_32FC1);Mat R = Mat::eye(3, 3, CV_32F);cout << "保存校正图像" << endl;for (int i = 0; i != image_count; i++){Mat newCameraMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0));//根据标定结果进行图像的修正initUndistortRectifyMap(intrinsic_matrix, distortion_coeffs, R, getOptimalNewCameraMatrix(intrinsic_matrix, distortion_coeffs, image_size, 1, image_size, 0), image_size, CV_32FC1, mapx, mapy);Mat t = image_Seq[i].clone();cv::remap(image_Seq[i], t, mapx, mapy, INTER_LINEAR);string imageFileName;std::stringstream StrStm;StrStm << i + 1;StrStm >> imageFileName;imageFileName += "_校正后图像.jpg";imwrite(imageFileName, t);//保存畸变校正结果图}cout << "保存结束" << endl;//waitKey(0);system("pause");return 0;
}
代码运行结果如下:
相关文章:

OpenCV单目相机内参标定C++
基于OpenCV 实现单目相机内参标定: a.使用OpenCV库实现内参标定过程。通过角点检测、亚像素角点定位、角点存储与三维坐标生成和摄像机标定分别获取左右相机的内参。 b.具体地,使用库函数检测两组图像(左右相机拍摄图像)中棋盘格…...

基于MATLAB(DCT DWT)
第三章 图像数字水印的方案 3.1 图像数字水印的技术方案 在数据库中存储在国际互联网上传输的水印图像一般会被压缩,有时达到很高的压缩比。因此,数字水印算法所面临的第一个考验就是压缩。JPEG和EZW(Embedded Zero-Tree Wavelet࿰…...

渗透基础-rcube_webmail版本探测
简介 本文介绍了开源产品RoundCube webmail邮件系统的版本探测思路,并用go语言实现工具化、自动化探测。 正文 0x01 探测思路研究 探测系统版本,最理想的方法就是系统主页html代码中有特定的字符串,比如特定版本对应的hash在主页的html代…...

linux下编译鸿蒙版boost库
我在上一篇文章中介绍了curl和openssl的编译方式(linux下编译鸿蒙版curl、openssl-CSDN博客),这篇再介绍一下boost库的编译。 未经许可,请勿转载! 一.环境准备 1.鸿蒙NDK 下载安装方式可以参考上篇文章,…...

滚雪球学Redis[6.3讲]:Redis分布式锁的实战指南:从基础到Redlock算法
全文目录: 🎉前言🚦Redis分布式锁的概念与应用场景🍃1.1 什么是分布式锁?🍂1.2 应用场景 ⚙️使用Redis实现分布式锁🌼2.1 基本思路🌻2.2 示例代码🥀2.3 代码解析 &#…...

springboot二手汽车交易平台-计算机毕业设计源码82053
目录 1 绪论 1.1研究背景 1.2研究意义 1.3国内外研究现状 2 二手汽车交易平台系统分析 2.1 可行性分析 2.2 系统流程分析 2.3 功能需求分析 2.4 性能需求分析 3 二手汽车交易平台概要设计 3.1 系统体系结构设计 3.2总体功设计 3.3子模块设计设计 3.4 数据库设计 …...

typescript 中的类型推断
在 TypeScript 中,类型推断(Type Inference)是一种编译器自动确定变量或表达式类型的能力。这大大减少了需要显式声明类型的代码量,使得代码更加简洁和易读。TypeScript 的类型推断机制非常强大,可以在很多情况下自动推…...

linux 隐藏文件
在Linux中,隐藏文件以点(.)开头的文件或文件夹被认为是隐藏文件。隐藏文件通常用于存储系统配置文件或敏感文件。 以下是几种不同的方法来隐藏文件或文件夹: 方法1:在文件或文件夹名字前面加上点(.&#…...

【网络协议栈】Tcp协议(上)结构的解析 和 Tcp中的滑动窗口(32位确认序号、32位序号、4位首部长度、6位标记位、16为窗口大小、16位紧急指针)
绪论 “没有那么多天赋异禀,优秀的人总是努力翻山越岭。”本章主要讲到了再五层网络协议从上到下的第二层传输层中使用非常广泛的Tcp协议他的协议字段结构,通过这些字段去认识其Tcp协议运行的原理底层逻辑和基础。后面将会再写一篇Tcp到底是通过什么调…...

手表玻璃盖板视觉贴合
在手表生产过程中,贴合加工是一个至关重要的环节,它涉及将手表的盖板与LCM模组或各种功能片进行精准贴合。这一过程不仅要求高度的精度,还追求效率与稳定性,以满足现代可穿戴设备日益增长的市场需求。然而,当前行业在这…...

电信和互联网行业数据安全评估师CCRC-DSA人才强基计划
“电信和互联网行业数据安全人才强基计划”(以下简称“强基计划”)自 2022 年 4 月启动伊始,始终秉持以人才需求为导向,以体系化能力建设为重点,扎实铸就数据安全人才培养品牌,力促行业数据安全人才培养工作…...

MQTTnet 4.3.7.1207 (最新版)使用体验,做成在线客服聊天功能,实现Cefsharp的物联的功能(如远程打开新网址)
一、MQTTnet 4.3.x版本客户端 将客户端集成到 cefsharp 定制浏览器中,实现物联网功能 网上很多代码是3.x版本代码,和4.x版本差异性较大,介绍较为简单或不系统 二、部分代码说明 初始化,初始化》连接服务端》发布上线信息(遗嘱)ConnectAsync等 订阅主题:SubscribeAsync 接…...

将java项目jar包打包成exe服务
1.结构展示 2.注意事项 前提: 环境准备:jdk8 和 .net支持 { 1.控制面板》程序和功能》启用和关闭windows功能》.net的勾选》2.jdk8自行百度安装环境3.其他项目必须的软件环境安装等(数据库...) }第一次准备: 1.将打包好的jar包放到premiumServices.exe…...

Django请求响应对象
在 Django 中,请求(request)和响应(response)对象是处理 HTTP 请求和返回 HTTP 响应的核心。它们分别由 Django 的 HttpRequest 和 HttpResponse 类表示。 HttpRequest 对象 HttpRequest 对象包含了客户端发送的所有…...

DevExpress中文教程 - 如何在静态SSR模式下使用Blazor Drawer组件?
Microsoft的 .NET 8 UI框架引入了静态服务器端呈现模式(静态SSR)——组件在服务器端呈现,然后返回到客户端,没有任何交互,DevExpress Blazor Drawer组件需要交互式呈现模式来动态地改变其IsOpen状态。 在本文中&#…...

商汤科技十周年公布新战略,将无缝集成算力、模型及应用
10月18日,恰逢商汤科技十周年庆典,“2024商汤十周年国际论坛:迈向AI 2.0共融新时代”在香港科学园成功举办。 据「TMT星球」了解,来自全球的行业领袖、政府代表、AI专家共聚于此,共同探讨AI行业的未来。 活动上&…...

【如何获取股票数据07】Python、Java等多种主流语言实例演示获取股票行情api接口之沪深A股历史分时MA数据获取实例演示及接口API说明文档
最最近一两年内,股票量化分析逐渐成为热门话题。而从事这一领域工作的第一步,就是获取全面且准确的股票数据。因为无论是实时交易数据、历史交易记录、财务数据还是基本面信息,这些数据都是我们进行量化分析时不可或缺的宝贵资源。我们的主要…...

Rust语法基础
注释 所有的开发者都在努力使他们的代码容易理解,但有时需要额外的解释。在这种情况下,开发者在他们的源码中留下注释,编译器将会忽略掉这些内容,但阅读源码的人可能会发现有用。 和大多数的编程语言一样,主要有一下两种: 单行注释 // 多行注释 /* */ 基本数据类型 Ru…...

AWS WAF实现API安全防护
在当今的互联网环境中,API安全防护变得越来越重要。本文将介绍如何使用AWS WAF(Web Application Firewall)来实现有效的API安全防护策略。 背景 我们有一个API服务,其URL模式如下: https://dev.example.com/bff-app/sec/v1/module-a/feature-a/sub-feature-a我们需要使用AWS…...

vue将table转换为pdf导出
安装依赖: 首先,你需要安装 jspdf 和 html2canvas 这两个库。 npm install jspdf html2canvas创建Vue组件: 创建一个Vue组件,用于显示表格并提供导出PDF的功能。 <template> <div> <div id"table-contain…...

20240818 字节跳动 笔试
文章目录 1、编程题1.11.21.31.4岗位:BSP驱动开发工程师-OS 题型:4 道编程题 1、编程题 1.1 小红的三消游戏: 小红在玩一个三消游戏,游戏中 n 个球排成一排,每个球都有一个颜色。若有 3 个颜色相同的球连在一起,则消除这 3 个球,然后剩下的球会重新连在一起。在没有 …...

在Debian上安装向日葵
说明: 因为之前服务器上安装了 PVE (Proxmox VE),之前是用 Proxmox VE 进行服务器资源管理的。出于某些原因,现在不再通过 PVE构建的虚拟机来使用计算资源,而是通过 PVE 自带的 Debian 系统直接使用虚拟机资源(因为积…...

13.2 Linux_网络编程_UNIX域套接字
概述 什么是UNIX域套接字: UNIX域套接字是使用套接字进行本地通信,TCP/UDP是使用套接字进行网络通信。UNIX域套接字也有域流式套接字和域数据报套接字,这两种形式域TCP/UDP的含义类似,使用步骤也完全一致。 bind时绑定的结构体…...

10.22 多进程间通信-共享内存、信号量集
练习:通过信号量集完成对共享内存的同步操作 案例代码: 分文件编译:信号量集部分 sem.h #ifndef __SEM_H__ #define __SEM_H__ #include <myhead.h> union semun {int val; /* Value for SETVAL */struct semid_ds…...

输入输出管理器的使用
解释 InputMgr 是一个输入管理器,主要用于检测并管理用户的输入事件(例如键盘和鼠标输入)。它通过监听输入事件,并利用事件中心 (EventCenter) 来触发相应的事件。在这里,你可以管理多种输入类型,如按下、…...

windows连接linux服务器上的jupyter lab
文章目录 服务器上开启jupyter lab本地cmd将端口8888映射到服务器的8889上本地浏览器打开8888端口 服务器上开启jupyter lab jupyter-lab --ip 0.0.0.0 --port 8889 --no-browser --allow-root本地cmd将端口8888映射到服务器的8889上 ssh -N -f -L localhost:8888:localhost:…...

golang生成并分析cpu prof文件
1. 定义一个接口,请求接口时,生成cpu.prof文件 在主协程中新启一个协程,当请求接口时,生成一个60秒的cpu.prof文件 go func() {http.HandleFunc("/prof", startProfileHandler)http.ListenAndServe(":9092"…...

【Python爬虫实战】XPath与lxml实现高效XML/HTML数据解析
🌈个人主页:https://blog.csdn.net/2401_86688088?typeblog 🔥 系列专栏:https://blog.csdn.net/2401_86688088/category_12797772.html 目录 前言 一、为什么学习xpath和lxml (一)高效解析和提取数据 …...

软件测试学习笔记丨Selenium学习笔记:元素定位与操作
本文转自测试人社区,原文链接:https://ceshiren.com/t/topic/22510 本文为霍格沃兹测试开发学社的学习经历分享,写出来分享给大家,希望有志同道合的小伙伴可以一起交流技术,一起进步~ 说明:本篇博客基于sel…...

在 HTML 中,<input> 元素支持的事件汇总
在 HTML 中,<input> 元素支持多种事件,这些事件可以在用户与输入字段交互时触发。以下是一些常见的 <input> 事件: input: 当 <input> 元素的值发生变化时触发。适用于文本、数字、日期等类型的输入。 change: 当 <inp…...