OpenCV基础(28)使用OpenCV进行摄像机标定Python和C++
摄像头是机器人、监控、太空探索、社交媒体、工业自动化甚至娱乐业等多个领域不可或缺的一部分。 对于许多应用,必须了解相机的参数才能有效地将其用作视觉传感器。
在这篇文章中,您将了解相机校准所涉及的步骤及其意义。 我们还共享 C++ 和 Python 代码以及棋盘图案的示例图像。
1.什么是相机标定
估计相机参数的过程称为相机标定。
这意味着我们拥有确定现实世界中的 3D 点与其在该校准相机捕获的图像中对应的 2D 投影(像素)之间的准确关系所需的有关相机的所有信息(参数或系数)。
通常这意味着恢复两种参数:
- 相机/镜头系统的内部参数。例如。镜头的焦距、光学中心和径向畸变系数。
- 外部参数:这是指相机相对于某个世界坐标系的方向(旋转和平移)。
在下面的图像中,使用几何标定估计出的镜头参数来消除图像的畸变。
2.使用OpenCV进行摄像机标定
要找到 3D 点在图像平面上的投影,我们首先需要使用外部参数(旋转和平移)将点从世界坐标系转换到相机坐标系。
接下来,使用相机的内部参数,我们将点投影到图像平面上。
将世界坐标中的 3D 点 (X_w, Y_w, Z_w)
与其在图像坐标中的投影 (u, v)
相关联的方程如下所示:
其中,P
是一个 3×4
投影矩阵,由两部分组成 - 包含内在参数的内在矩阵 K
和由 3×3
旋转矩阵R
和 3×1
平移向量t
组合而成的外在矩阵 [R|t]
。
如前所述,内在矩阵 K
是上三角形矩阵。
fx,fyf_x, f_yfx,fy是 x 和 y 焦距(是的,它们通常是相同的)。
cx,cyc_x, c_ycx,cy是图像平面中光学中心的 x 和 y 坐标。使用图像的中心通常是一个足够好的近似值。
γ\gammaγ是轴之间的偏斜。通常为 0。
3.相机标定的目标
相机标定的目标是使用一组已知的 3D 点(Xw,Yw,Zw)(X_w, Y_w, Z_w)(Xw,Yw,Zw)及其对应的图像坐标(u,v)(u,v)(u,v)找到 3×3 矩阵KKK、3×3 旋转矩阵RRR和 3×1 平移向量ttt。当我们得到内在和外在参数的值时,相机就被称为是经过标定的。
综上所述,一个相机标定算法有以下输入和输出
- 输入:一组图像,其中包含已知 2D 图像坐标和 3D 世界坐标的点。
- 输出:3×3 相机内在矩阵,每个图像的旋转和平移。
注意:在 OpenCV 中,相机内在矩阵没有 skew
参数。所以矩阵的形式是:
4.不同类型的相机标定方法
- 标定板:当我们完全控制成像过程时,执行标定的最佳方法是从不同的视点捕获物体或已知尺寸图案的多张图像。我们将在这篇文章中学习的基于棋盘格的方法属于这一类。我们也可以使用已知尺寸的圆形图案代替棋盘图案。
- 几何线索:有时我们在场景中还有其他几何线索,例如可用于标定的直线和消失点。
- 基于深度学习的方法:当我们对成像设置几乎没有控制权时(例如,我们只有一个场景图像),仍然可以使用基于深度学习的方法获得相机的标定信息。
5.相机标定步骤
5.1 用棋盘图案定义真实世界的坐标
世界坐标系 :我们的世界坐标由附在房间墙壁上的棋盘图案固定。我们的 3D 点是棋盘中正方形的角。上面棋盘的任意一个角都可以选择为世界坐标系的原点。XXX和YYY轴沿墙,ZZZ轴垂直于墙。因此,棋盘上的所有点都在 XY 平面上(即 ZZZ= 0)。
在标定过程中,我们通过一组已知的 3D(Xw,Yw,Zw)(X_w, Y_w, Z_w)(Xw,Yw,Zw) 点及其在图像中对应的像素位置(u,v)(u,v)(u,v)来计算相机参数。
对于 3D 点,我们在许多不同方向拍摄具有已知尺寸的棋盘图案。世界坐标附加到棋盘上,由于所有角点都位于一个平面上,我们选择每个点ZwZ_wZw坐标为 0。因为点在棋盘中是等距的,所以每个3D点的(Xw,Yw)(X_w, Y_w)(Xw,Yw)坐标很容易定义,方法是取一个点作为参考(0,0)(0,0)(0,0),并根据该参考点定义其余的点。
为什么棋盘图案在校准中得到如此广泛的应用?
棋盘图案在图像中是独特且易于检测的。不仅如此,棋盘上正方形的角对于定位它们来说是非常理想的,因为它们在两个方向上都有明显的梯度。此外,这些角也与棋盘线的交点有关。所有这些事实都被用来确定棋盘图案中正方形的角。
5.2 从不同的角度捕捉棋盘的多个图像
以上这些图像用于相机标定。
接下来,我们保持棋盘不动,并通过移动摄像机获取棋盘的多个图像。
或者,我们也可以保持相机不变,拍摄不同方向的棋盘图案。这两种情况在数学上是相似的。
5.3 查找棋盘的二维坐标
我们现在有多个棋盘图像。我们还知道棋盘上点在世界坐标中的 3D 位置。我们需要的最后一件事是图像中这些棋盘角的 2D 像素位置。
5.3.1 找到棋盘角
OpenCV 提供了一个名为 findChessboardCorners
的内置函数,它查找棋盘并返回角的坐标。让我们看看下面代码块中的用法。
C++
bool findChessboardCorners(InputArray image, Size patternSize, OutputArray corners, int flags = CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE )
Python
retval, corners = cv2.findChessboardCorners(image, patternSize, flags)
image
:源棋盘视图。它必须是 8 位灰度或彩色图像。patternSize
: 每个棋盘行和列的内角数(patternSize = cvSize (points_per_row, points_per_colum) = cvSize(columns,rows)
)。corners
: 检测到角的输出数组。flags
: 各种操作标志。只有当事情不顺利时,您才需要担心这些。使用默认值。
输出为true或false,取决于是否检测到棋盘格。
5.3.2 细化棋盘角
一切都是为了标定精度的。为了获得良好的结果,重要的是获得具有亚像素级别精度的角点位置。 OpenCV 的函数cornerSubPix
获取原始图像和角点位置,并在原始位置的一个小邻域内寻找最佳角点位置。该算法本质上是迭代的,因此我们需要指定终止标准(例如迭代次数和/或准确性)
C++
void cornerSubPix(InputArray image, InputOutputArray corners, Size winSize, Size zeroZone, TermCriteria criteria)
Python
cv2.cornerSubPix(image, corners, winSize, zeroZone, criteria)
image
:输入图片。corners
:输入角的初始坐标,输出提供的细化坐标。winSize
: 搜索窗口边长的一半。zeroZone
: 搜索区域中间的死亡区域的一半大小,下面的公式中没有对其求和。它有时用来避免自相关矩阵可能出现的奇点。(-1,-1)
表示不存在该大小。criteria
终止角细化迭代过程的准则。也就是说,角位置细化过程在criteria.maxCount
迭代之后或在某个迭代中角位置移动小于criteria.epsilon
时停止。
5.4 相机标定
校准的最后一步是将世界坐标中的 3D 点及其在所有图像中的 2D 位置传递给 OpenCV 的 calibrateCamera
方法。该实现基于Zhengyou Zhang 的一篇论文。数学有点复杂,需要线性代数背景。
让我们看看 calibrateCamera
的语法
C++
double calibrateCamera(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints, Size imageSize, InputOutputArray cameraMatrix, InputOutputArray distCoeffs, OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs)
Python
retval, cameraMatrix, distCoeffs, rvecs, tvecs = cv2.calibrateCamera(objectPoints, imagePoints, imageSize)
objectPoints
: 3D 点向量的向量。 外部向量包含与视图数量一样多的元素。imagePoints
: 二维图像点的向量。imageSize
: 图像的大小cameraMatrix
: 内在相机矩阵distCoeffs
: 镜头畸变系数。这些系数将在以后的文章中解释。rvecs
: 为 3×1 旋转向量。向量的方向指定旋转轴,向量的大小指定旋转角度。tvecs
: 3×1 平移向量。
6.相机标定完整代码
下面分享了使用 Python 和 C++ 进行相机标定的代码。
6.1 用于相机标定的 Python 代码
请通读代码注释,它们解释了每个步骤的作用。
#!/usr/bin/env pythonimport cv2
import numpy as np
import os
import glob# 定义棋盘格的尺寸
CHECKERBOARD = (6,9)
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)# 创建向量以存储每个棋盘图像的 3D 点向量
objpoints = []
# 创建向量以存储每个棋盘图像的 2D 点向量
imgpoints = [] # 定义 3D 点的世界坐标
objp = np.zeros((1, CHECKERBOARD[0] * CHECKERBOARD[1], 3), np.float32)
objp[0,:,:2] = np.mgrid[0:CHECKERBOARD[0], 0:CHECKERBOARD[1]].T.reshape(-1, 2)
prev_img_shape = None# 提取存储在给定目录中的单个图像的路径
images = glob.glob('./images/*.jpg')
for fname in images:img = cv2.imread(fname)gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)# 找到棋盘角# 如果在图像中找到所需数量的角,则 ret = trueret, corners = cv2.findChessboardCorners(gray, CHECKERBOARD, cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_FAST_CHECK + cv2.CALIB_CB_NORMALIZE_IMAGE)"""如果检测到所需数量的角, 我们细化像素坐标并可视化"""if ret == True:objpoints.append(objp)# 细化给定二维点的像素坐标。corners2 = cv2.cornerSubPix(gray, corners, (11,11),(-1,-1), criteria)imgpoints.append(corners2)# 绘制并显示角img = cv2.drawChessboardCorners(img, CHECKERBOARD, corners2, ret)cv2.imshow('img',img)cv2.waitKey(0)cv2.destroyAllWindows()h,w = img.shape[:2]"""
通过传递已知 3D 点 (objpoints) 的值 和检测到的角点(imgpoints)对应的像素坐标 实现相机标定
"""
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)print("Camera matrix : \n")
print(mtx)
print("dist : \n")
print(dist)
print("rvecs : \n")
print(rvecs)
print("tvecs : \n")
print(tvecs)
6.2 用于相机标定的 C++ 代码
#include <opencv2/opencv.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <stdio.h>
#include <iostream>// 定义棋盘的尺寸
int CHECKERBOARD[2]{6,9}; int main()
{// 创建向量以存储每个棋盘图像的 3D 点向量std::vector<std::vector<cv::Point3f> > objpoints;// 创建向量以存储每个棋盘图像的 2D 点向量std::vector<std::vector<cv::Point2f> > imgpoints;// 定义 3D 点的世界坐标std::vector<cv::Point3f> objp;for(int i{0}; i<CHECKERBOARD[1]; i++){for(int j{0}; j<CHECKERBOARD[0]; j++)objp.push_back(cv::Point3f(j,i,0));}// 提取存储在给定目录中的单个图像的路径std::vector<cv::String> images;// 包含棋盘图像的文件夹的路径std::string path = "./images/*.jpg";cv::glob(path, images);cv::Mat frame, gray;// 用于存储检测到的棋盘角的像素坐标的向量std::vector<cv::Point2f> corner_pts;bool success;/* 循环遍历目录中的所有图像 */for(int i{0}; i<images.size(); i++){frame = cv::imread(images[i]);cv::cvtColor(frame,gray,cv::COLOR_BGR2GRAY);// 寻找棋盘角// 如果在图像中找到所需数量的角,则成功 = truesuccess = cv::findChessboardCorners(gray, cv::Size(CHECKERBOARD[0], CHECKERBOARD[1]), corner_pts, CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_FAST_CHECK | CV_CALIB_CB_NORMALIZE_IMAGE);/* 如果检测到所需数量的角,们细化像素坐标并在棋盘格图像上显示它们*/if(success){cv::TermCriteria criteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 30, 0.001);// 细化给定二维点的像素坐标。cv::cornerSubPix(gray,corner_pts,cv::Size(11,11), cv::Size(-1,-1),criteria);// 在棋盘上显示检测到的角点cv::drawChessboardCorners(frame, cv::Size(CHECKERBOARD[0], CHECKERBOARD[1]), corner_pts, success);objpoints.push_back(objp);imgpoints.push_back(corner_pts);}cv::imshow("Image",frame);cv::waitKey(0);}cv::destroyAllWindows();cv::Mat cameraMatrix,distCoeffs,R,T;/*通过传递已知 3D 点 (objpoints) 的值 和检测到的角点(imgpoints)对应的像素坐标 实现相机标定*/cv::calibrateCamera(objpoints, imgpoints, cv::Size(gray.rows,gray.cols), cameraMatrix, distCoeffs, R, T);std::cout << "cameraMatrix : " << cameraMatrix << std::endl;std::cout << "distCoeffs : " << distCoeffs << std::endl;std::cout << "Rotation vector : " << R << std::endl;std::cout << "Translation vector : " << T << std::endl;return 0;
}
参考目录
https://learnopencv.com/camera-calibration-using-opencv/
相关文章:

OpenCV基础(28)使用OpenCV进行摄像机标定Python和C++
摄像头是机器人、监控、太空探索、社交媒体、工业自动化甚至娱乐业等多个领域不可或缺的一部分。 对于许多应用,必须了解相机的参数才能有效地将其用作视觉传感器。 在这篇文章中,您将了解相机校准所涉及的步骤及其意义。 我们还共享 C 和 Python 代码以…...

APB总线详解及手撕代码
本文的参考资料为官方文档AMBA™3 APB Protocol specification文档下载地址: https://pan.baidu.com/s/1Vsj4RdyCLan6jE-quAsEuw?pwdw5bi 提取码:w5bi APB端口介绍介绍总线具体握手规则之前,需要先熟悉一下APB总线端口,APB的端口…...
【Linux/Windows】源文件乱码问题解决方法总结
🐚作者简介:花神庙码农(专注于Linux、WLAN、TCP/IP、Python等技术方向)🐳博客主页:花神庙码农 ,地址:https://blog.csdn.net/qxhgd🌐系列专栏:Linux技术&…...

Python 四大主流 Web 编程框架
目前Python的网络编程框架已经多达几十个,逐个学习它们显然不现实。但这些框架在系统架构和运行环境中有很多共通之处,本文带领读者学习基于Python网络框架开发的常用知识,及目前的4种主流Python网络框架:Django、Tornado、Flask、Twisted。 …...

学UI设计,可以向哪些方向发展?该怎么学?
1、什么是UI设计?UI设计,全称 User Interface,翻译成中文意思叫做用户界面设计。2、UI设计的类型UI设计按用户和界面来分可分成四种UI设计。分别是移动端UI设计,PC端UI设计,游戏UI设计,以及其它UI设计。第一…...

【C++】初识CC++内存管理
前言 我们都知道C&C是非常注重性能的语言,因此对于C&C的内存管理是每一个C/C学习者必须重点掌握的内容,本章我们并不是深入讲解C&C内存管理,而是介绍C&C内存管理的基础知识,为我们以后深入理解C&C内存管理做铺…...

Nacos快速使用指南
简单例子:springboot快速集成nacos官方github文档命名空间是绝对隔离的。group之间可以通过配置实现跨 group访问配置中心Nacos config官方文档应用级别的默认配置文件名(dataId)dataId 的完整格式如下:${prefix}-${spring.profil…...

复旦发布国内首个类ChatGPT模型MOSS,和《流浪地球》有关?
昨晚,复旦大学自然语言处理实验室邱锡鹏教授团队发布国内首个类ChatGPT模型MOSS,现已发布至公开平台https://moss.fastnlp.top/ ,邀公众参与内测。 MOSS和ChatGPT一样,开发的过程也包括自然语言模型的基座训练、理解人类意图的对…...

国家级高新区企业主要经济指标(2012-2021年)
数据来源:国家统计局 时间跨度:2012-2021 区域范围:全国(及各分类统计指标) 指标说明:手工提取最新的中国统计年鉴数据中各个excel指标表,形成各个指标文件的多年度数据,便于多年…...

SpringBoot2核心技术-核心功能【05、Web开发】
目录 1、SpringMVC自动配置概览 2、简单功能分析 2.1、静态资源访问 1、静态资源目录 2、静态资源访问前缀 2.2、欢迎页支持 2.3、自定义 Favicon 2.4、静态资源配置原理 3、请求参数处理 0、请求映射 1、rest使用与原理 2、请求映射原理 1、普通参数与基本注解 …...
2021-03 青少年软件编程(C语言)等级考试试卷(六级)解析
2021-03 青少年软件编程(C语言)等级考试试卷(六级)解析T1. 生日相同 2.0 在一个有180人的大班级中,存在两个人生日相同的概率非常大,现给出每个学生的名字,出生月日。试找出所有生日相同的学生。 时间限制:1000 内存限制:65536 输入 第一行为整数n,表示有n个学生,n …...
数据库的多租户隔离
数据库的多租户隔离有三种方案 1、独立数据库 一个租户一个数据库,这种方案的用户数据隔离级别最高,安全性最好,成本也最高 优点:为不同的租户提供独立的数据库,有助于简化数据模型的扩展设计,满足不同租…...
网络输入分辨率是否越大越好
目标检测比如 yolov5,训练输入图像大小默认是 640*640,这个是不是越大训练的效果越好 ? 这个肯定不是的。而且,如果仅调整输入图像的分辨率,不改变网络结构的话,检测准确率反而会下降的。首先,…...
离线采集普遍解决方案
简介 使用Datax每日全量相关全量表,使用Maxwell增量采集到Kafka然后到Flume然后到Hdfs。 DataX全量 生成模板Json gen_import_config.py # codingutf-8 import json import getopt import os import sys import MySQLdb#MySQL相关配置,需根据实际情…...
SAP ABAP 数据类型P类型详解
ABAP中比较难以理解的是P类型的使用,P类型是一种压缩类型,主要用于存储小数,定义时要指定字节数和小数点位数,定义语法如下: DATA: name(n) TYPE P decimals m,n代表字节数,最大为16,m是小…...
应用沙盒seccomp的使用
应用沙盒原理参考https://zhuanlan.zhihu.com/p/513688516 1、什么是Seccomp? seccomp 是 secure computing 的缩写,其是 Linux kernel 从2.6.23版本引入的一种简洁的 sandboxing 机制。 系统调用: 在Linux中,将程序的运行空间分为内核与用户空间(内核态和用户态),在逻辑…...

C++项目——高并发内存池(2)——thread_cache的基础功能实现
1.并发内存池concurrent memory pool 组成部分 thread cache、central cache、page cache thread cache:线程缓存是每个线程独有的,用于小于64k的内存的分配,线程从这里申请内存不需要加锁,每个线程独享一个cache,这…...

【C进阶】数据的存储
文章目录:star:1. 数据类型:star:2. 整形在内存中的存储2.1 存储规则2.2 存储模式2.3 验证大小端模式:star:3. 数据范围3.1 整形溢出3.2 数据范围的求解3.3 练习:star:4. 浮点型在内存中的存储4.1 浮点数的存储规则4.2 练习5. :star::star:总结(思维导图)⭐️1. 数据类型 在了…...

【已解决】异常断电文件损坏clickhouse启动不了:filesystem error Structure needs cleaning
问题 办公室有一台二手服务器,作为平时开发测试使用。由于机器没放在机房,会偶发断电异常断电后,文件系统是有出问题的可能的,尤其是一些不断在读写合并的文件春节后,发现clickhouse启动不了,使用systemct…...

FlinkSQL行级权限解决方案及源码
FlinkSQL的行级权限解决方案及源码,支持面向用户级别的行级数据访问控制,即特定用户只能访问授权过的行,隐藏未授权的行数据。此方案是实时领域Flink的解决方案,类似离线数仓Hive中Ranger Row-level Filter方案。 源码地址: https…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...

【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...

20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...

搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...