当前位置: 首页 > article >正文

相机标定(输出相机内参和畸变参数)

相机标定


这里我用笔记本电脑自带的摄像头进行相机标定
仅作示例,实际工程中要用对应的摄像头进行标定
同时代码也要相应的修改,不过修改的主要是相机的初始化
粗略的说就是打开相机那部分要修改(依据实际情况相应修改)
最终的结果会输出

重投影误差RMS( 越小越好)
相机内参矩阵cameraMatrix
畸变参数distCoeffs.t()

这里用Opencv内置的矩阵转置函数.t() ,将畸变参数从列向量转换为行向量,便于显示,不过它这个只是显示上改变,畸变参数实际存储的还是列向量。
所以下次如果没有用.t( )把它转置,那最后输出的矩阵就会是列向量

#include <opencv2/opencv.hpp>
#include <vector>
#include <iostream>
#include <iomanip>using namespace cv;
using namespace std;int main() {// ========== 参数配置 ==========const Size boardSize(12, 8);      // 内角点数量(12×8)const float squareSize = 20.0f;   // 每个棋盘格实际尺寸(mm)const int targetSamples = 20;     // 需要采集的样本数const TermCriteria criteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 30, 0.001);// ========== 准备工作 ==========// 生成理论角点坐标(世界坐标系)vector<Point3f> objectCorners;for (int i = 0; i < boardSize.height; ++i) {for (int j = 0; j < boardSize.width; ++j) {objectCorners.push_back(Point3f(j * squareSize, i * squareSize, 0));}}vector<vector<Point3f>> objectPoints;vector<vector<Point2f>> imagePoints;// ========== 初始化摄像头 ==========VideoCapture cap(0);if (!cap.isOpened()) {cerr << "ERROR: 无法打开摄像头" << endl;return -1;}// 获取初始帧确定分辨率Mat initFrame;cap >> initFrame;if (initFrame.empty()) {cerr << "ERROR: 无法获取初始帧" << endl;return -1;}Size imageSize = initFrame.size();// ========== 数据采集 ==========cout << "=== 相机标定程序 ===" << endl;cout << "棋盘规格: " << boardSize.width << "x" << boardSize.height << " (每个格子 " << squareSize << "mm)" << endl;cout << "图像分辨率: " << imageSize.width << "x" << imageSize.height << endl;cout << "需要采集 " << targetSamples << " 个有效样本" << endl;cout << "操作说明:\n  空格键 - 保存当前帧\n  ESC键 - 提前结束采集\n";int currentSamples = 0;Mat frame;while (currentSamples < targetSamples) {cap >> frame;if (frame.empty()) {cerr << "WARNING: 获取帧失败" << endl;continue;}Mat gray;cvtColor(frame, gray, COLOR_BGR2GRAY);// 查找棋盘格角点vector<Point2f> corners;bool found = findChessboardCorners(gray, boardSize, corners,CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE + CALIB_CB_FAST_CHECK);if (found) {// 亚像素级精确化cornerSubPix(gray, corners, Size(11, 11), Size(-1, -1), criteria);// 可视化结果drawChessboardCorners(frame, boardSize, corners, found);string status = format("采集进度: %d/%d | 按空格保存", currentSamples, targetSamples);putText(frame, status, Point(20, 30), FONT_HERSHEY_SIMPLEX, 0.8, Scalar(0, 255, 0), 2);imshow("Camera Calibration", frame);// 按键处理 按空格键保存当前图像到示例中int key = waitKey(1);if (key == 32) { // 空格键objectPoints.push_back(objectCorners);imagePoints.push_back(corners);currentSamples++;// 保存确认反馈Mat feedback;frame.copyTo(feedback);string msg = format("样本 %d 已保存!", currentSamples);putText(feedback, msg, Point(frame.cols/4, frame.rows/2), FONT_HERSHEY_SIMPLEX, 1.5, Scalar(0, 255, 255), 3);imshow("Camera Calibration", feedback);waitKey(300);} else if (key == 27) { // ESC键break;}} else {string msg = format("未检测到棋盘格 | 需要 %dx%d 内角点", boardSize.width, boardSize.height);putText(frame, msg, Point(20, 30), FONT_HERSHEY_SIMPLEX, 0.8, Scalar(0, 0, 255), 2);imshow("Camera Calibration", frame);if (waitKey(1) == 27) break;}}// ========== 相机标定 ==========if (objectPoints.size() >= 5) {Mat cameraMatrix, distCoeffs;vector<Mat> rvecs, tvecs;cout << "\n>>> 开始计算相机参数..." << endl;double rms = calibrateCamera(objectPoints, imagePoints, imageSize,cameraMatrix, distCoeffs, rvecs, tvecs,CALIB_FIX_K4 + CALIB_FIX_K5);// ========== 结果输出 ==========cout << fixed << setprecision(5);cout << "\n=== 标定结果 ===" << endl;cout << "重投影误差(RMS): " << rms << " (值越小越好,建议<0.5)" << endl;cout << "\n相机内参矩阵:\n" << cameraMatrix << endl;cout << "\n畸变系数(k1,k2,p1,p2,k3):\n" << distCoeffs.t() << endl;// ========== 保存结果 ==========FileStorage fs("camera_calibration.yml", FileStorage::WRITE);fs << "calibration_date" << "2024-03-20";fs << "image_width" << imageSize.width;fs << "image_height" << imageSize.height;fs << "board_width" << boardSize.width;fs << "board_height" << boardSize.height;fs << "square_size" << squareSize;fs << "camera_matrix" << cameraMatrix;fs << "distortion_coefficients" << distCoeffs;fs << "reprojection_error" << rms;fs.release();cout << "\n>>> 参数已保存到 camera_calibration.yml" << endl;// ========== 验证结果 ==========cout << "\n>>> 按ESC键退出验证..." << endl;Mat map1, map2;Mat newCameraMatrix = getOptimalNewCameraMatrix(cameraMatrix, distCoeffs,imageSize, 1, imageSize, 0);initUndistortRectifyMap(cameraMatrix, distCoeffs, Mat(),newCameraMatrix, imageSize,CV_16SC2, map1, map2);while (true) {cap >> frame;if (frame.empty()) break;// 去畸变处理Mat undistorted;remap(frame, undistorted, map1, map2, INTER_LINEAR);// 并排显示对比Mat comparison;hconcat(frame, undistorted, comparison);putText(comparison, "原始图像", Point(20, 30), FONT_HERSHEY_SIMPLEX, 0.8, Scalar(0, 0, 255), 2);putText(comparison, "去畸变图像", Point(frame.cols + 20, 30), FONT_HERSHEY_SIMPLEX, 0.8, Scalar(0, 255, 0), 2);imshow("Calibration Results", comparison);if (waitKey(1) == 27) break;}} else {cerr << "ERROR: 有效样本不足 (" << objectPoints.size() << "),至少需要5个" << endl;}// ========== 资源释放 ==========cap.release();destroyAllWindows();cout << "\n>>> 程序正常结束" << endl;return 0;
}

最终代码实现功能


  1. 完整的错误处理

    • 摄像头初始化检查
    • 帧获取失败处理
    • 样本数量验证
      注意:这里样本数量检测的并不是棋盘格的数量,而是指成功检测到棋盘格角点并保存的有效图像帧数
      (你仔细想想,一张图片就直接测出相机的畸变参数,那误差得有多大啊)
  2. 增强的棋盘格检测

    CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE + CALIB_CB_FAST_CHECK
    

在相机标定代码中,增强的棋盘格检测主要通过OpenCV的findChessboardCorners函数结合特定的标志位(flags)实现。以下是详细解析:


1. 核心代码段

bool found = findChessboardCorners(gray,                  // 输入灰度图像boardSize,             // 棋盘格内角点数量(如12x8)corners,               // 输出的角点坐标CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE + CALIB_CB_FAST_CHECK    // 增强检测的标志位组合
);

2. 标志位的作用

通过以下三个标志位的组合,显著提升了棋盘格检测的鲁棒性和效率:

(1) CALIB_CB_ADAPTIVE_THRESH
  • 功能
    使用自适应阈值法二值化图像,替代全局阈值。
  • 解决的问题
    光照不均时(如部分阴影或反光),全局阈值可能导致棋盘格部分区域无法检测。
  • 效果
    对每个局部区域独立计算阈值,确保棋盘格线条清晰。
(2) CALIB_CB_NORMALIZE_IMAGE
  • 功能
    在二值化前对图像进行直方图归一化
  • 解决的问题
    低对比度或亮度偏暗/偏亮的图像。
  • 效果
    增强图像对比度,使黑白棋盘格更分明。
(3) CALIB_CB_FAST_CHECK
  • 功能
    启用快速检查模式,先粗略验证棋盘格是否存在。
  • 解决的问题
    避免在无棋盘格的图像上浪费计算资源。
  • 效果
    显著提升检测速度(尤其对视频流实时处理)。

3. 为何需要这些增强?

场景无增强的检测增强后的检测
光照不均可能漏检部分角点自适应阈值保证全图检测
低对比度图像角点检测不稳定归一化后对比度提升
快速视频处理每帧都完整计算,耗时快速检查跳过无棋盘格的帧
棋盘格部分遮挡易失败自适应阈值+归一化提高容错性

总结

  • 核心标志位
    ADAPTIVE_THRESH + NORMALIZE_IMAGE + FAST_CHECK 是应对复杂场景的黄金组合。
  • 适用场景
    光照变化、低对比度、实时视频流、部分遮挡等情况。
  • 性能权衡
    增强检测会略微增加单次计算量,但通过FAST_CHECK和提前终止机制,整体效率更高。
  1. 详细的用户引导

    • 实时显示采集进度
    • 控制台输出标定参数
    • 保存完整的标定结果文件
  2. 直观的结果验证

    • 并排显示原始/去畸变图像
    • 显示重投影误差(RMS)
  3. 规范的变量管理

    • 使用const定义配置参数
    • 合理的作用域控制
    • 图像尺寸单独保存

编译命令

终端g++编译示例(但是不太推荐)

g++ camera_calibration.cpp -o calibration `pkg-config --cflags --libs opencv4` -std=c++11

最好用CMake或者一些其他的编译工具,会更方便一点
这里就不多赘述了

输出文件示例(camera_calibration.yml)

%YAML 1.0
calibration_date: "2024-03-20"
image_width: 640
image_height: 480
board_width: 12
board_height: 8
square_size: 20.0
camera_matrix: !!opencv-matrixrows: 3cols: 3dt: ddata: [ 5.12345678e+02, 0., 3.19500000e+02, 0., 5.12345678e+02, 2.39500000e+02, 0., 0., 1. ]
distortion_coefficients: !!opencv-matrixrows: 5cols: 1dt: ddata: [ -2.123456e-01, 8.765432e-02, 1.234567e-03, -2.345678e-04, 0. ]
reprojection_error: 0.12345

相关文章:

相机标定(输出相机内参和畸变参数)

相机标定 这里我用笔记本电脑自带的摄像头进行相机标定 仅作示例&#xff0c;实际工程中要用对应的摄像头进行标定 同时代码也要相应的修改&#xff0c;不过修改的主要是相机的初始化 粗略的说就是打开相机那部分要修改&#xff08;依据实际情况相应修改&#xff09; 最终的结果…...

单页面应用的特点,什么是路由,VueRouter的下载,安装和使用,路由的封装抽离,声明式导航的介绍和使用

文章目录 一.什么是单页面应用?二.什么是路由?生活中的路由和Vue中的路由 三.VueRouter(重点)0.引出1.介绍2.下载与使用(5个基本步骤2个核心步骤)2.1 五个基本步骤2.2 两个核心步骤 四.路由的封装抽离五.声明式导航1.导航链接特点一:能跳转特点二:能高亮 2.两个高亮类名2.1.区…...

数字ic后端设计从入门到精通2(含fusion compiler, tcl教学)

上篇回顾 上一篇文章需要讨论了net,pin的基础用法&#xff0c;让我们来看一下高级一点的用法 instance current_instance current_instance 是 Synopsys 工具&#xff08;如 Fusion Compiler 或 Design Compiler&#xff09;中用于在设计层次结构中导航的关键命令。它允许用…...

STM32---外部中断EXTI

目录 一、中断向量表 二、EXTI工作原理图 三、NVIC模块 四、GPIO设置为EXTI的结构 五、C语言示例代码 在STM32中&#xff0c;中断是一个非常重要的结构&#xff0c;他能让我们在执行主函数的时候&#xff0c;由硬件检测一些外部或内部产生的中断信号&#xff0c;跳转到中断…...

Itext进行PDF的编辑开发

这周写了一周的需求&#xff0c;是制作一个PDF生成功能&#xff0c;其中用到了Itext来制作PDF的视觉效果。其中一些功能不是很懂&#xff0c;仅作记录&#xff0c;若要学习请仔细甄别正确与否。 开始之前&#xff0c;我还是想说&#xff0c;这傻福需求怎么想出来的&#xff0c…...

Hibernate的组件映射

在实际的开发中,使用的是非常多的&#xff0c;还有几种比较特殊的关系映射: 组件映射继承映射 先看一下组件映射: 组件映射中, 组件也是一个类, 但是这个类它不独立称为一个实体, 也就是说, 数据库中没有一个表格单独的和它对应, 具体情况呢, 看演示&#xff1a;...

C++ 操作符重载Operator

C可以重载大多数操作符&#xff0c;如算术运算符号&#xff0c;-号。 位操作符<<,>> 下标符号[]等都可以重载。 重载的意思&#xff0c;是让这些符号&#xff0c;按你定义的行为来执行代码&#xff0c;但是这种自定义&#xff0c;是有限制的&#xff0c;必须有一…...

Docker 镜像、容器和 Docker Compose的区别

前言&#xff1a;Docker 的镜像、容器和 Docker Compose 是容器化技术的核心组件&#xff0c;以下是对它们的详细解析及使用场景说明。 ​​1、Docker 镜像&#xff08;Image&#xff09;​​ ​​定义​​&#xff1a; 镜像是只读模板&#xff0c;包含运行应用程序所需的代码、…...

Linux深度探索:进程管理与系统架构

1.冯诺依曼体系结构 我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺依曼体系。 截至目前&#xff0c;我们所认识的计算机&#xff0c;都是由⼀个个的硬件组件组成。 输入设备&#xff1a;键盘&#xff0c;鼠标…...

一段式端到端自动驾驶:VAD:Vectorized Scene Representation for Efficient Autonomous Driving

论文地址&#xff1a;https://github.com/hustvl/VAD 代码地址&#xff1a;https://arxiv.org/pdf/2303.12077 1. 摘要 自动驾驶需要对周围环境进行全面理解&#xff0c;以实现可靠的轨迹规划。以往的方法依赖于密集的栅格化场景表示&#xff08;如&#xff1a;占据图、语义…...

17.整体代码讲解

从入门AI到手写Transformer-17.整体代码讲解 17.整体代码讲解代码 整理自视频 老袁不说话 。 17.整体代码讲解 代码 import collectionsimport math import torch from torch import nn import os import time import numpy as np from matplotlib import pyplot as plt fro…...

把dll模块注入到游戏进程的方法_基于文件修改的注入方式

1、概述 本文主要是介绍两种基于文件修改的注入方式,一种是“DLL劫持”,另一种是“修改导入表”。这两种注入方式都是利用操作系统加载PE时的特点来实现的,我们在实现这两种注入方式时只需专注于注入dll的实现,而不用花费额外的精力去关注注入器的实现。要想深入了解这两种…...

4月21日星期一今日早报简报微语报早读

4月21日星期一&#xff0c;农历三月廿四&#xff0c;早报#微语早读。 1、女子伸腿阻止列车关门等待同行人员&#xff0c;被深圳铁路警方行政拘留&#xff1b; 2、北理工再通报&#xff1a;开除宫某党籍&#xff0c;免去行政职务&#xff0c;解除聘用关系&#xff1b; 3、澳门…...

Spark(20)spark和Hadoop的区别

Apache Spark 和 Apache Hadoop 都是广泛使用的开源大数据处理框架&#xff0c;但它们在设计理念、架构、性能和适用场景等方面存在显著区别。以下是它们的主要区别&#xff1a; ### **1. 架构设计** - **Hadoop**&#xff1a; - **HDFS&#xff08;Hadoop Distributed File…...

Kubeflow 快速入门实战(二) - Pipelines / Katib / KServer

承接前文博客 Kubeflow 快速入门实战(一) Kubeflow 快速入门实战(一) - 简介 / Notebooks-CSDN博客文章浏览阅读441次&#xff0c;点赞19次&#xff0c;收藏6次。本文主要介绍了 Kubeflow 的主要功能和能力&#xff0c;适用场景&#xff0c;基本用法。以及Notebook&#xff0c…...

【JavaEE初阶】多线程重点知识以及常考的面试题-多线程进阶(一)

本篇博客给大家带来的是多线程中常见的所策略和CAS知识点. &#x1f40e;文章专栏: JavaEE初阶 &#x1f680;若有问题 评论区见 ❤ 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作的动力 . 王子,公主请阅&#x1f680; 要开心要快…...

ECA 注意力机制:让你的卷积神经网络更上一层楼

ECA 注意力机制&#xff1a;让你的卷积神经网络更上一层楼 在深度学习领域&#xff0c;注意力机制已经成为提升模型性能的重要手段。从自注意力&#xff08;Self-Attention&#xff09;到各种变体&#xff0c;研究人员不断探索更高效、更有效的注意方法。今天我们要介绍一种轻…...

基于定时器查询模式的LED闪烁(STC89C52单片机)

#include <reg52.h> sbit LED P0^0; sbit ADDR0 P1^0; sbit ADDR1 P1^1; sbit ADDR2 P1^2; sbit ADDR3 P1^3; sbit ENLED P1^4; void main() { unsigned char cnt 0; //定义一个计数变量&#xff0c;记录T0溢出次数 ENLED 0; //使能U3&#xff0c;选择…...

SAP系统生产跟踪报表入库数异常

生产跟踪报表入库数异常 交库21820,入库43588是不可能的 原因排查: 报表的入库数取值,是取移动类型321 (即系检验合格后过账到非限制使用)的数. 查凭证,101过账2次21807,321过账了2次21794,然后用102退1次21794.就是说这批物料重复交库了. 解决&#xff1a; 方案一:开发增强设…...

Kubernetes控制平面组件:调度器Scheduler(一)

云原生学习路线导航页&#xff08;持续更新中&#xff09; kubernetes学习系列快捷链接 Kubernetes架构原则和对象设计&#xff08;一&#xff09;Kubernetes架构原则和对象设计&#xff08;二&#xff09;Kubernetes架构原则和对象设计&#xff08;三&#xff09;Kubernetes控…...

08-DevOps-向Harbor上传自定义镜像

harbor创建完成&#xff0c;往harbor镜像仓库中上传自定义的镜像&#xff0c;包括新建项目、docker配置镜像地址、镜像重命名、登录harbor、推送镜像这几个步骤&#xff0c;具体操作如下&#xff1a; harbor中新建项目 访问级别公开&#xff0c;代表任何人都可以拉取仓库中的镜…...

Vue v-for 循环DOM 指定dom个数展示一行

在Vue.js中&#xff0c;如果想根据v-for循环的结果来控制哪些元素应该在一行中展示&#xff0c;你可以通过计算属性或者方法来实现。这里使用CSS改变样式和js脚本两种方式做到这一点&#xff0c;根据你的具体需求选择适合的方法。 方法1&#xff1a;使用计算属性 如果你想要基…...

mysql控制单表数据存储及单实例表创建

1. 单表数据存储不要过大 主流建议 保守建议。100万以内保持最佳性能其他。不超过2000万 理论依据。 B树层级可能变多。从3增加到4。导致索引查询路径边长&#xff0c;增加IO开销 优化 加索引。对高频查询字段增加索引。避免全表扫描低频历史数据通过分区表或归档隔离。足够的…...

极验4滑块笔记:整理思路--填坑各种问题

最近在研究某验4逆向分析&#xff0c;以前没弄过这种&#xff0c;所以爬了很多坑&#xff0c;就是把分享给大家~ 1.这个gcaptcha4.js需要逆向&#xff0c;我的方法很笨就是将_ᕶᕴᕹᕶ()这个蝌蚪文打印处来&#xff0c;全局替换一下&#xff0c;然后Unicode这种代码&#xff0…...

LX3-初识是单片机

初识单片机 一 什么是单片机 单片机:单片微型计算机单片机的组成:CPU,RAM(内存),flash(硬盘),总线,时钟,外设…… 二 Coretex-M系列介绍 了解ARM公司与ST公司ARM内核系列: A 高性能应用,如手机,电脑…R 实时性强,如汽车电子,军工…M 超低功耗,如消费电子,家电,医疗器械 三…...

2025年渗透测试面试题总结-拷打题库10(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 2025年渗透测试面试题总结-拷打题库10 1. CSRF成因及防御措施 | 非Token防御 2. XSS Worm原理 3. Co…...

Linux系统下docker 安装 MySQL

踩坑解决&#xff1a; 1、docker安装mysql&#xff0c;不需要执行search 2、pull时&#xff0c;需要指定版本号 3、连接Navicat需要看阿里云端口号是否开启 在拉取镜像的时候&#xff0c;如果不使用代理服务器&#xff0c;docker search mysql不需要执行 本人在未使用代理服…...

配置 VS Code 使用 ESLint 格式化

1、在设置里面搜索Default Formatter&#xff0c;下拉框里选择eslint 2、并勾选Enables ESlint as a formatter 3、再在settings.json文件中添加配置代码&#xff0c;如下所示&#xff1a; 1&#xff09; 、打开 VS Code 设置 快捷键&#xff1a;Ctrl ,&#xff08;Mac: ⌘ ,…...

从代码实现理解Vision Permutator:WeightedPermuteMLP模型解析

从代码实现理解Vision Permutator&#xff1a;WeightedPermuteMLP模型解析 随着人工智能的快速发展&#xff0c;视觉识别任务变得越来越重要。最近提出的Vision Permutator架构为这一领域带来了新的思路&#xff0c;它通过可学习的排列操作重新定义了特征交互的方式。 今天我…...

Web开发:ABP框架10——使用数据库存储文件,完成文件的下载和上传

一、简要介绍 字节数组&#xff1a;字节数组是存储数据的字节序列&#xff0c;常用于二进制数据&#xff08;如图片、音视频、文档等&#xff09;的表示。 文件和字节的关系&#xff1a;文件是由字节构成&#xff0c;字节是文件内容的基本单位。 文件以字节形式存储在服务器数…...