opencv - py_calib3d - py_calibration 相机校准

文章目录

    • 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 += errorprint( "total error: {}".format(mean_error/len(objpoints)) )

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

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

相关文章

防护勒索攻击:一场勒索攻击与智能防护的较量

近年来,云上数据安全已经成为企业的又一大难题,尤其是勒索攻击频发,有组织的黑客攻击目标已经从核心数据窃取扩展到金融、交通、能源、通信等行业的关键信息基础设施,对企业数据安全构成了极大的威胁和挑战。 同时AI时代&#xff…

tcache attack

Tcache Attack tcache让堆利用更加简单: tcache回顾: 在 tcache 中新增了两个结构体,分别是 tcache_entry 和 tcache_perthread_struct: /* We overlay this structure on the user-data portion of a chunk when the chunk is …

功能教学——如何快速理解并使用数据助手

在企业的数字化管理中,数据处理的重要性不容小觑。它如同一根红线,贯穿企业运营的各个环节,对于提升决策效率、优化运营流程以及增强市场竞争力等方面都发挥着至关重要的作用。而百数的数据助手,作为一款高效的数据处理工具&#…

【MySQL】:表操作语法大全

表内容的操作 增删改查 CRUD (create、retrieve、update、delete) 新增 基本语法 语法为: insert into 表名 values (值,值,值...);这里的列数和类型,要和表结构匹配插入中文的话,要确保数据库创建的时候要设置字…

代码随想录算法训练营Day 63| 图论 part03 | 417.太平洋大西洋水流问题、827.最大人工岛、127. 单词接龙

代码随想录算法训练营Day 63| 图论 part03 | 417.太平洋大西洋水流问题、827.最大人工岛、127. 单词接龙 文章目录 代码随想录算法训练营Day 63| 图论 part03 | 417.太平洋大西洋水流问题、827.最大人工岛、127. 单词接龙17.太平洋大西洋水流问题一、DFS二、BFS三、本题总结 82…

基于SpringBoot的矩形范围面时空分析-以震中附近历史地震为例

目录 前言 1、分析的必要性 2、分析的紧迫性 一、数据库物理模型及空间分析实现 1、数据库物理模型 2、空间数据库中的空间查询分析 二、Java后台程序开发 1、模型层设计 2、业务层的设计与实现 三、WebGIS功能设计与实现 1、同时展示4幅地图 2、初始化地图 3、展示…

CID引流-拼多多案例

目前已实现拼多多等CID链路,本文以拼多多链路为例,说明具体实施过程 1. 拼多多链路流程 以下是通过前期调研,得到的具体业务流程: 商家申请资质以及授权部分,进行商品推广自研落地页,生成落地页在巨量引…

MAVSDK-Rust源码示例编译与使用

1.启动PX4模拟器并监听14540端口 2.编译并运行MAVSDK_Server 编译: 安装: 运行mavsdk_server 3.克隆并编译MAVSDK-Rust 运行info示例 运行其它示例,参考git 在RustRover中运行示例

169. 多数元素【 力扣(LeetCode) 】

一、题目描述 给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 二、测试用例 示例 1: 输入:nums [3,2,…

结合el-upload上传组件,验证文件格式及大小

结合el-upload上传组件&#xff0c;验证文件格式及大小 效果如下&#xff1a; 代码如下&#xff1a; upgradeFirmwareInfo.vue页面 <template><div><el-dialog title"新增固件升级包" :visible.sync"dialogFormVisible"top"7vh&qu…

PyTorch深度学习实战——使用深度Q学习进行Pong游戏

PyTorch深度学习实战——使用深度Q学习进行Pong游戏 0. 前言1. 结合固定目标网络的深度 Q 学习模型1.1 模型输入1.2 模型策略2. 实现深度 Q 学习进行 Pong 游戏相关链接0. 前言 我们已经学习了如何利用深度 Q 学习来进行 Gym 中的 CartPole 游戏。在本节中,我们将研究更复杂的…

广州机房服务器搬迁布线方案

友力科技&#xff08;广州&#xff09;有限公司&#xff0c;专业从事数据中心机房建设、改造和搬迁工程。友力科技&#xff08;广州&#xff09;有限公司提供的服务内容包括但不限于&#xff1a;原数据中心设备下架准备、沿途运输、新数据中心上架运行、数据迁移等。 友力科技…

【实在RPA案例集】实在智能助力中国烟草11省40余家多场景自动化!

近年来&#xff0c;为深入贯彻行业数字化转型战略部署和发展新质生产力体制机制&#xff0c;诸多省市烟草公司及中烟公司大力推进烟草行业数字化转型&#xff0c;然而烟草行业在数字化转型过程中始终存在一个核心痛点&#xff0c;即数据整合的复杂性、系统间的兼容性问题&#…

LabVIEW汽车动态信号模拟系统

随着汽车工业的快速发展&#xff0c;对汽车电子控制单元&#xff08;ECU&#xff09;的测试与仿真需求日益增加。开发了一种基于LabVIEW软件开发的汽车动态信号模拟系统&#xff0c;该系统能有效模拟ECU在实车环境下的工作状态&#xff0c;为ECU的开发和测试提供了一个高效、经…

python项目通过docker部署到Linux系统并实现远程访问

背景需求&#xff1a;在Windows系统编写了简单的python代码&#xff0c;希望能通过docker打包到Linux Ubuntu系统中&#xff0c;并运行起来&#xff0c;并且希望在本地Windows系统中能通过postman访问。 目录 一、原本的python代码 二、创建一个简单的Flask应用程序 三、创…

MySQL 查询 limit 100000000, 10 和 limit 10 速度一样快吗?

MySQL 查询 limit 100000000, 10 和 limit 10 速度一样快吗&#xff1f; MySQL内部分为server层和存储引擎层。一般情况下存储引擎都用innodb。 server层有很多模块&#xff0c;其中需要关注的是执行器是用于跟存储引擎打交道的组件。 执行器可以通过调用存储引擎提供的接口&…

整合StarRocks更新表全部知识点

总结StarRocks更新表的全部内容的集合&#xff08;V3.2版本&#xff09; 一、基本功能 聚合函数replace的聚合表主键被主键表替代采用Merge-On-Read的策略&#xff0c;读取时需要在线Merge多个版本的数据文件&#xff0c;谓词和索引无法下推至底层数据&#xff0c;会严重影响…

LeetCode:对称的二叉树(C语言)

1、问题概述&#xff1a;给一个二叉树&#xff0c;看是否按轴对称 2、示例 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true 示例 2&#xff1a; 输入&#xff1a;root [1,2,2,null,3,null,3] 输出&#xff1a;false 3、分析 &#xff08;1&a…

教学用MQTT工具的思考

前言 我在《智能物联网应用设计》课程中有个实验就是学习MQTT的使用&#xff0c;目前使用的是以下几个工具&#xff1a; 客户端使用MQTTX本地服务器使用的是mosquitto 云端服务器采用的巴法云协议分析软件采用的是Wireshark 这里Wireshark基本没有啥可以替代的&#xff0c;而…

Vue3 study

Vue3 工程 创建 还是能像 vue2 一样通过 vue-cli 创建&#xff0c;即 vue create projectName 但是官方更推荐 vite 创建&#xff0c;即 npm create vuelatest&#xff0c;然后从项目名开始配置 总结&#xff1a;入口在 index.html&#xff0c;它会引入 main.ts&#xff0c;…