opencv - py_calib3d - py_calibration 相机校准

news2024/9/21 22:39:44

文章目录

    • Camera Calibration 相机校准
    • 目标
    • 基础知识
    • 代码
      • 设置
      • 校准
      • 去失真
        • 1. 使用 **cv.undistort()**
        • 2. 使用 **remapping**
    • 重新投影误差

Camera Calibration 相机校准

目标

在本节中,我们将学习

  • 相机造成的失真类型
  • 如何找到相机的内在和外在属性
  • 如何根据这些属性消除图像失真

基础知识

一些针孔相机会给图像带来严重的失真。两种主要的失真是径向失真和切向失真。

径向失真会导致直线出现弯曲。点距离图像中心越远,径向失真就越大。例如,下面显示了一个图像,其中棋盘的两个边缘用红线标记。但是,您可以看到棋盘的边框不是直线,与红线不匹配。所有预期的直线都凸出。请访问 失真(光学) 了解更多详细信息。

在这里插入图片描述

径向畸变可以表示如下:

x d i s t o r t e d = x ( 1 + k 1 r 2 + k 2 r 4 + k 3 r 6 ) y d i s t o r t e d = y ( 1 + k 1 r 2 + k 2 r 4 + k 3 r 6 ) x_{distorted} = x( 1 + k_1 r^2 + k_2 r^4 + k_3 r^6) \\ y_{distorted} = y( 1 + k_1 r^2 + k_2 r^4 + k_3 r^6) xdistorted=x(1+k1r2+k2r4+k3r6)ydistorted=y(1+k1r2+k2r4+k3r6)

同样,切向畸变的发生是因为拍摄镜头与成像平面没有完全平行。因此,图像中的某些区域可能看起来比预期的更近。切向畸变量可以表示如下:

x d i s t o r t e d = x + [ 2 p 1 x y + p 2 ( r 2 + 2 x 2 ) ] y d i s t o r t e d = y + [ p 1 ( r 2 + 2 y 2 ) + 2 p 2 x y ] x_{distorted} = x + [ 2p_1xy + p_2(r^2+2x^2)] \\ y_{distorted} = y + [ p_1(r^2+ 2y^2)+ 2p_2xy] xdistorted=x+[2p1xy+p2(r2+2x2)]ydistorted=y+[p1(r2+2y2)+2p2xy]

简而言之,我们需要找到五个参数,称为畸变系数,如下所示:

D i s t o r t i o n    c o e f f i c i e n t s = ( k 1 k 2 p 1 p 2 k 3 ) Distortion \; coefficients=(k_1 \hspace{10pt} k_2 \hspace{10pt} p_1 \hspace{10pt} p_2 \hspace{10pt} k_3) Distortioncoefficients=(k1k2p1p2k3)

除此之外,我们还需要一些其他信息,例如相机的内在参数和外在参数。内在参数特定于相机。它们包括焦距 ( f x , f y f_x,f_y fx,fy)和光学中心( c x , c y c_x, c_y cx,cy)等信息。焦距和光学中心可用于创建相机矩阵,该矩阵可用于消除由于特定相机的镜头而导致的失真。相机矩阵对于特定相机是唯一的,因此一旦计算出来,就可以在同一相机拍摄的其他图像上重复使用。它表示为 3x3 矩阵:

[ c a m e r a    m a t r i x = [ f x 0 c x 0 f y c y 0 0 1 ] ] [camera \; matrix = \left [ \begin{matrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{matrix} \right ]] [cameramatrix= fx000fy0cxcy1 ]

外部参数对应于将 3D 点的坐标转换为坐标系的旋转和平移向量。

对于立体应用,需要首先纠正这些失真。为了找到这些参数,我们必须提供一些定义明确的图案(例如棋盘)的样本图像。我们找到一些我们已经知道相对位置的特定点(例如棋盘上的方角)。我们知道这些点在现实世界空间中的坐标,也知道它们在图像中的坐标,因此我们可以求解失真系数。为了获得更好的结果,我们至少需要 10 个测试图案。

代码

如上所述,我们需要至少 10 个测试模式来进行相机校准。OpenCV 附带了一些棋盘图像(参见 samples/data/left01.jpg – left14.jpg),因此我们将利用这些图像。考虑一张棋盘图像。相机校准所需的重要输入数据是一组 3D 真实世界点和这些点在图像中对应的 2D 坐标。2D 图像点是可以的,我们可以从图像中轻松找到它们。(这些图像点是棋盘中两个黑色方块相互接触的位置)

那么来自真实世界空间的 3D 点呢?这些图像是从静态相机拍摄的,棋盘放置在不同的位置和方向。所以我们需要知道 ( X , Y , Z ) (X,Y,Z) (X,Y,Z) 值。但为了简单起见,我们可以说棋盘在 XY 平面上保持静止(因此 Z=0 始终),相机相应地移动。这种考虑有助于我们仅找到 X、Y 值。现在对于 X、Y 值,我们可以简单地将点传递为 (0,0)、(1,0)、(2,0) …,这表示点的位置。在这种情况下,我们得到的结果将以棋盘正方形的大小为单位。但如果我们知道正方形的大小(例如 30 毫米),我们可以将值传递为 (0,0)、(30,0)、(60,0) …。因此,我们得到的结果以毫米为单位。(在这种情况下,我们不知道正方形的大小,因为我们没有拍摄这些图像,所以我们以正方形大小的形式传递)。

3D 点称为对象点,2D 图像点称为图像点

设置

因此,要查找棋盘中的图案,我们可以使用函数 cv.findChessboardCorners()。我们还需要传递我们正在寻找的图案类型,例如 8x8 网格、5x5 网格等。在此示例中,我们使用 7x6 网格。(通常棋盘有 8x8 个方格和 7x7 个内角)。它返回角点和 retval,如果获得图案,则为 True。这些角将按顺序排列(从左到右、从上到下)

@note 此函数可能无法在所有图像中找到所需的图案。因此,一个不错的选择是编写代码,使其启动相机并检查每一帧是否需要图案。获得图案后,找到角并将其存储在列表中。此外,在读取下一帧之前提供一些间隔,以便我们可以向不同方向调整棋盘。继续此过程,直到获得所需数量的良好图案。即使在这里提供的示例中,我们也不确定给出的 14 张图像中有多少张是好的。因此,我们必须读取所有图像并仅选取好的图像。

@note 除了棋盘,我们还可以使用圆形网格。在这种情况下,我们必须使用函数 cv.findCirclesGrid() 来查找模式。使用圆形网格执行相机校准所需的图像较少。

一旦我们找到角点,我们就可以使用 cv.cornerSubPix() 来提高其准确性。我们还可以使用 cv.drawChessboardCorners() 绘制图案。所有这些步骤都包含在以下代码中:

import numpy as np
import cv2 as cv
import glob

# 终止标准
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 准备对象点,如 (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((6*7,3), np.float32)
objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2)
# 用于存储所有图像中的对象点和图像点的数组。
objpoints = [] # 3d point in real world space
imgpoints = [] # 2d points in image plane.

images = glob.glob('*.jpg')

for fname in images:
    img = cv.imread(fname)
    gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
	# Find the chess board corners 找到棋盘角
	ret, corners = cv.findChessboardCorners(gray, (7,6), None)
	
	# If found, add object points, image points (after refining them)
	# 如果找到,则添加对象点、图像点(细化之后)
	if ret == True:
	    objpoints.append(objp)
	
	    corners2 = cv.cornerSubPix(gray,corners, (11,11), (-1,-1), criteria)
	    imgpoints.append(corners)
	
	    # Draw and display the corners 绘制并显示角
	    cv.drawChessboardCorners(img, (7,6), corners2, ret)
	    cv.imshow('img', img)
	    cv.waitKey(500)
		cv.destroyAllWindows()


下面是一张绘制有图案的图像:
在这里插入图片描述

校准

现在我们有了物体点和图像点,我们就可以进行校准了。我们可以使用函数 cv.calibrateCamera(),它返回相机矩阵、失真系数、旋转和平移向量等。

ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)

去失真

现在,我们可以拍摄一张图片并对其进行去失真处理。OpenCV 提供了两种方法来实现这一点。但是首先,我们可以使用 cv.getOptimalNewCameraMatrix() 根据自由缩放参数优化相机矩阵。如果缩放参数 alpha=0,它将返回无失真图像,其中不需要的像素最少。因此,它甚至可以删除图像角落的一些像素。如果 alpha=1,则保留所有像素,并添加一些额外的黑色图像。此函数还返回一个图像 ROI,可用于裁剪结果。

因此,我们拍摄一张新图像(本例中为 left12.jpg。这是本章中的第一张图片)

img = cv.imread('left12.jpg')
h,  w = img.shape[:2]
newcameramtx, roi = cv.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h))
1. 使用 cv.undistort()

这是最简单的方法。只需调用该函数并使用上面获得的 ROI 来裁剪结果。

dst = cv.undistort(img, mtx, dist, None, newcameramtx)
# 裁剪图像
x, y, w, h = roi
dst = dst[y:y+h, x:x+w]
cv.imwrite('calibresult.png', dst)

2. 使用 remapping

这种方式稍微有点困难。首先,找到一个从失真图像到未失真图像的映射函数。然后使用重映射函数。

mapx, mapy = cv.initUndistortRectifyMap(mtx, dist, None, newcameramtx, (w,h), 5)
dst = cv.remap(img, mapx, mapy, cv.INTER_LINEAR)
# 裁剪图像
x, y, w, h = roi
dst = dst[y:y+h, x:x+w]
cv.imwrite('calibresult.png', dst)

尽管如此,两种方法的结果相同。请参见下面的结果:
在这里插入图片描述

您可以在结果中看到所有边缘都是直的。

现在,您可以使用 NumPy 中的写入函数(np.savez、np.savetxt 等)存储相机矩阵和失真系数,以备将来使用。

重新投影误差

重新投影误差可以很好地估计找到的参数的精确度。重新投影误差越接近零,我们找到的参数就越准确。给定固有矩阵、失真矩阵、旋转矩阵和平移矩阵,我们必须首先使用 cv.projectPoints() 将对象点转换为图像点。然后,我们可以计算通过变换得到的值与角点查找算法得到的值的绝对范数。为了找到平均误差,我们计算所有校准图像计算出的误差的算术平均值。

mean_error = 0
for i in range(len(objpoints)):
    imgpoints2, _ = cv.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
    error = cv.norm(imgpoints[i], imgpoints2, cv.NORM_L2)/len(imgpoints2)
    mean_error += error

print( "total error: {}".format(mean_error/len(objpoints)) )

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

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

相关文章

代码随想录——一和零(Leetcode474)

题目链接 0-1背包 class Solution {public int findMaxForm(String[] strs, int m, int n) {// 本题m,n为背包两个维度// dp[i][j]:最多右i个0和j个1的strs的最大子集大小int[][] dp new int[m 1][n 1];// 遍历strs中字符串for(String str : strs){int num0 …

通信原理-思科实验三:无线局域网实验

实验三 无线局域网实验 一:无线局域网基础服务集 实验步骤: 进入物理工作区,导航选择 城市家园; 选择设备 AP0,并分别选择Laptop0、Laptop1放在APO范围外区域 修改笔记本的网卡,从以太网卡切换到无线网卡WPC300N 切…

若依 ruoyi poi Excel合并行的导入

本文仅针对文字相关的合并做了处理 ,图片合并及保存需要另做处理!! 目标:Excel合并行内容的导入 结果: 1. ExcelUtil.java 类,新增方法:判断是否是合并行 /*** 新增 合并行相关代码:…

文件解析的终极工具:Apache Tika

文件解析的终极工具:Apache Tika Apache Tika 简介 Apache Tika 是一个开源的、跨平台的库,用于检测、提取和解析各种类型文件的元数据。 它支持多种文件格式,包括文档、图片、音频和视频。 Tika是一个底层库,经常用于搜索引擎…

Windows下ORACLE数据泵expdp和impdp使用

Windows下ORACLE数据泵expdp和impdp使用 一、基础环境 操作系统:Windows server 2008; 数据库版本:Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production 数据库工具:PL/SQL 12.0.7 实验内容&…

springboot配置文件如何读取pom.xml的值

比如想读取profile.active的值&#xff0c;默认属性为pro 在maven中加入以下插件&#xff1a; <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-resources-plugin</artifactId><version>3.2.0</version>&l…

FastGPT 源码调试配置

目录 一、添加 launch.json 文件 二、调试 本文简单介绍如何通过 vscode 对 FastGPT 进行调试。 这里假设已经安装 vsocde 和 FastGPT本地部署。 一、添加 launch.json 文件 vscode 打开 FastGPT 项目,点击 调试 -> 显示所有自动调试配置 -> 添加配置 -> Node.j…

AI视频生成器,堪称自媒体人的神器

Vozo Rewrite & Redub 是一款创新的视频编辑工具&#xff0c;可以通过简单的提示重写视频脚本、然后这个工具会自动给视频重新配音、翻译语音并口型同步&#xff0c;然后生成新的视频。 无论是将经典视频转变为病毒视频宣传片&#xff0c;还是将普通视频变成喜剧&#xff…

Vue2高级用法

Vue2高级用法 1、mixin复用【vue不会用了&#xff0c;了解一下】1.1 基础使用1.2 选项合并1.3 全局混入1.4 细数 mixin 存在的问题 2、vue.js 动画特效&#xff06; 常见组件库介绍2.1 进入/离开基础使用示例2.2 进入/离开自定义过度类名2.3 进入/离开动画钩子2.4 多组件过渡与…

pdf2image:将PDF文档转化为图像的Python魔法

标题&#xff1a;探索pdf2image&#xff1a;将PDF文档转化为图 像的Python魔法 背景 在数字时代&#xff0c;我们经常需要处理各种格式的文档&#xff0c;尤其是PDF文件。PDF以其跨平台的可读性和稳定性而广受欢迎。然而&#xff0c;有时我们需要将PDF文件转换成图像格式&am…

AI大模型学习笔记-gpt

多模态&#xff08;Multimodal&#xff09;技术指的是处理和整合来自多种不同类型数据&#xff08;模态&#xff09;的信息和方法。在计算机科学和人工智能领域&#xff0c;这种技术可以应用于多个方面&#xff0c;比如图像、文字、语音、视频等。这种技术的主要目标是通过整合…

Java中包,final,权限修饰符,代码块学习

&#xff08;这一小节我看的是面向对象进阶-13-包和final_哔哩哔哩_bilibili&#xff09; 包&#xff1a; final: Ctriln可以搜索包 细节&#xff1a;引用是记录的地址值&#xff0c;实际上final修饰的基本类型&#xff0c;引用类型实际上的数据都是不发生改变的。 权限修饰符…

什么是图纸加密软件,图纸加密软件推荐

图纸加密软件是一类专门用于保护CAD图纸和其他设计文件安全的工具。随着技术的发展&#xff0c;工程设计和制造业中的图纸和设计文件越来越多地以数字化形式存储和传输&#xff0c;这使得这些数据容易成为潜在的攻击目标。图纸加密软件通过对图纸文件进行加密保护&#xff0c;确…

AI学习记录 - 图像识别的基础入门

代码实现&#xff0c;图像识别入门其实非常简单&#xff0c;这里使用的是js&#xff0c;其实就是把二维数组进行公式化处理&#xff0c;处理方式如上图&#xff0c;不同的公式代表的不同的意义&#xff0c;这些意义网上其实非常多&#xff0c;这里就不细讲了。 const getSpecif…

python黑马笔记

运算符&#xff1a; 算术运算符&#xff1a; 加 - 减 * 乘 / 除 // 整除 % 取余 ** 求平方 除法计算得出的结果都是小数 赋值运算符&#xff1a; 标准赋值&#xff1a; 复合赋值&#xff1a; 、 - 、 * 、 / 、// 、 ** 字符串&#xff1a; 字符串拓展内容&#xf…

前端开发知识(三)-javascript

javascript是一门跨平台、面向对象的脚本语言。 一、引入方式 1.内部脚本&#xff1a;使用<script> &#xff0c;可以放在任意位置&#xff0c;也可以有多个&#xff0c;一般是放在<body></body>的下方。 2.外部脚本&#xff1a;单独编写.js文件&#xff…

十、SpringBoot 统⼀功能处理【拦截器、统一数据返回格式、统一异常处理】

十、SpringBoot 统⼀功能处理 1. 拦截器【HandlerInterceptor、WebMvcConfig】1.1 拦截器快速⼊⻔⾃定义拦截器&#xff1a;实现HandlerInterceptor接⼝&#xff0c;并重写其所有⽅法注册配置拦截器&#xff1a;实现WebMvcConfigurer接⼝&#xff0c;并重写addInterceptors⽅法…

从C到C++入门篇(三)引用;引用的本质

Reference & 引用 变量名&#xff0c;本身是一段内存的引用&#xff0c;即别名(alias)。此处引入的引用&#xff0c;是为己有变 量起一个别名。 int a 500; //变量名.实质是一段内存空间的别名 (int)0x0002345500; 引用的规则 引用&#xff0c;是一种关系型声明&#xff0…

【电路笔记】-D类放大器

D类放大器 文章目录 D类放大器1、概述2、D类放大器介绍3、调制4、放大5、滤波6、效率7、总结1、概述 在之前的文章中,放大器的导通角与其效率之间建立了重要的联系。 事实上,基于高导通角的放大器提供非常好的线性度,例如 A 类放大器,但效率非常有限,通常约为 20% 至 30%…

MATLAB基础:数据和变量

今天我们开始学习MATLAB基础知识 1、常用非运算符及其作用 1、“,” 作为程序运行的分隔符&#xff0c;起到分隔语句的作用 2、“;” 同样作为分隔符&#xff0c;与“,”不同的是“;”会在程序运行时隐藏该行语句 如下图&#xff1a; 3、“...” 三个英文句点表示续行符…