python相机校准

news2025/1/21 4:53:54

文章目录

    • 张正友标定法
    • 角点检测
    • 标定
    • 去畸变

张正友标定法

相片是三维世界在二维平面上的投射,故而其深度信息是损失掉了的。但是,如果把拍照看作理想的小孔成像过程,那么相片中的每个像素,都将通过一个锥体与世界中真实的点一一对应,这时如果再来一条参考光线,那么理论上就可以实现二维图像的三维重构了。

然而,实际相机并不理想,从真实世界到图片的映射过程,实则是四个坐标系之间的变换过程,即世界坐标系、相机坐标系、图像坐标系以及像素坐标系,这些坐标系之间的关系,可以通过矩阵来表示,相机校准,目的就是求解出这些矩阵。

一个非常直观是思路是,用相机拍下一组平面,并根据平面之间点的变换关系,来拟合相机矩阵。所以,一个比较关键的问题是,如何提取平面中一一对应的点,换言之,平面中什么样的点最好提取?

答案是棋盘格的黑白交界处,这便是大名鼎鼎的张正友棋盘格标定法。

opencv提供了一系列函数以实现这个方法,其标定流程和用到的主要函数为

  1. 角点检测 findChessboardCorners
  2. 相机校准 calibrateCamera
  3. 参数优化 getOptimalNewCameraMatrix
  4. 去畸变 cv_undistort

【opencv】内置了张正友的棋盘格标定法,通过一些姿态各异的棋盘格图像,就能标定相机的内外参数。

角点检测

角点检测的目的,就是获取棋盘格上黑白相交的点的位置,直观一点,就是实现下面的结果。

在这里插入图片描述

为了得到上面的图像,需分三步走,第一步自然是准备好棋盘格数据。

import numpy as np
import cv2
import os

#  数据准备,path是图像文件夹的路径
path = 'imgs'
fs = os.listdir(path)
grays = []
for f in fs:
    fName = os.path.join(path, f)
    img = cv2.imread(fName)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    grays.append(gray)

第二步是核心步骤,即完成亚像素角点检测。opencv提供了【findChessboardCorners】函数用于角点检测,其输入参数包括棋盘格图像、角点个数以及标志位。在提取角点之后,可通过【cornerSubPix】函数来进一步进行亚像素角点检测,以提高精度。

# 角点检测
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 
    30, 0.001)
pImgs = []
for g in grays:
   ret, cs = cv2.findChessboardCorners(g, (11, 8), None)
   # 亚像素角点检测
   pImg = cv2.cornerSubPix(g, cs.astype(np.float32), (5, 5), (-1,   -1), criteria)
   pImgs.append(np.squeeze(pImg))

其中,pImg用于存放像素坐标中的二维点。

最后一步,画图,opencv自带的工具就像下面这样就可以,

cv2.drawChessboardCorners(grays[0], (w, h), pImgs[0], None)
cv2.imshow('findCorners', grays[0])
cv2.waitKey(1000)

考虑到此前已经学习过【matplotlib】模块,下面是【plt】的绘制流程。

import matplotlib.pyplot as plt

pts = pImgs[0].squeeze().reshape(-1,2).T
plt.imshow(grays[0])
plt.scatter(pts[0], pts[1], marker='*', c='red')
plt.show()

标定

【calibrateCamera】函数可用于图像标定,只需将现实世界的点和相机坐标系中的角点的一一对应关系输入,便能得到相应的相机矩阵。其中,现实世界中的三维点,一般成为对象点,由于棋盘格中每个方块都是等距的,故可直接建立为类似 ( 1 , 0 , 0 ) , ( 2 , 0 , 0 ) ⋯ (1,0,0), (2,0,0)\cdots (1,0,0),(2,0,0)即可

objp = np.zeros((6*7,3), np.float32)
objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2)

pObj = np.zeros((w*h, 3), np.float32)
pObj[:,:2] = np.mgrid[0:w, 0:h].T.reshape(-1,2)
pObjs = [pObj for _ in range(len(pImgs))]

ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(pObjs, pImgs, 
    grays[0].shape[::-1], None, None)

其中,rec为成功标志,为True时表示标定成功。

mtx为内参矩阵,差不多是

[ f x 0 c x 0 f y c y 0 0 1 ] = [ 5572.47 0 1314.18 0 5573.04 1008.16 0 0 1 ] \begin{bmatrix}f_x&0&c_x\\0&f_y&c_y\\0&0&1\end{bmatrix} =\begin{bmatrix} 5572.47&0&1314.18\\0&5573.04&1008.16\\0&0&1 \end{bmatrix} fx000fy0cxcy1 = 5572.470005573.0401314.181008.161

dist为畸变参数,最多有8个,分别表示 k 1 , k 2 , p 1 , p 2 , k 3 , k 4 , k 5 , k 6 k_1,k_2,p_1,p_2,k_3,k_4,k_5,k_6 k1,k2,p1,p2,k3,k4,k5,k6,本次标定得到的结果为

>>> print(dist)
[[-8.36577030e-02 -1.68977185e-01 -1.12233478e-03  9.45685802e-04
  -2.04246147e+01]]

这些畸变参数的物理意义如下

x ′ = x z , y ′ = y z , r = x ′ 2 + y ′ 2 K = 1 + k 1 r 2 + k 2 r 4 + k 3 r 6 1 + k 4 r 2 + k 5 r 4 + k 6 r 6 x ′ ′ = K x ′ + 2 p 1 x ′ y ′ + p 2 ( r 2 + 2 x ′ 2 ) u = f x x ′ ′ + c x v = f y y ′ ′ + c y \begin{aligned} x'&=\frac{x}{z},\quad y'=\frac{y}{z},\quad r=\sqrt{x'^2+y'^2}\\ K& = \frac{1+k_1r^2+k2_r^4+k_3r^6}{1+k_4r^2+k_5r^4+k_6r^6}\\ x'' &= Kx'+2p_1x'y'+p_2(r^2+2x'^2)\\ u&=f_xx''+c_x\\ v&=f_yy''+c_y\\ \end{aligned} xKx′′uv=zx,y=zy,r=x′2+y′2 =1+k4r2+k5r4+k6r61+k1r2+k2r4+k3r6=Kx+2p1xy+p2(r2+2x′2)=fxx′′+cx=fyy′′+cy

rvecstvecs分别表示每个标定板对应的旋转和平移向量。

去畸变

【getOptimalNewCameraMatrix】函数可以进一步优化相机参数,然后通过【undistort】函数可以修正图像畸变。

mat, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, size, 0, size)
dst = cv2.undistort(grays[0], mtx, dist, None, mat)

fig = plt.figure()
ax = fig.add_subplot(121)
ax.imshow(grays[0])
ax = fig.add_subplot(122)
ax.imshow(dst)
plt.show()

对比效果如下

在这里插入图片描述

最后,可通过对比反投影误差,来评估标定结果

errs = []
for i in range(len(pObjs)):
    pIm2, _ = cv2.projectPoints(pObjs[i], rvecs[i], tvecs[i], mtx, dist)
    err = cv2.norm(pImgs[i], pIm2, cv2.NORM_L2) / len(pIm2)
    errs.append(err)

plt.bar(np.arange(len(errs)), errs)
plt.show()

结果如下

在这里插入图片描述

# 去畸变
img2 = cv2.imread('03.jpg')
h, w = img2.shape[:2]

# 反投影误差
# 通过反投影误差,我们可以来评估结果的好坏。越接近0,说明结果越理想。
total_error = 0
for i in range(len(pObjs)):
    pIm2, _ = cv2.projectPoints(pObjs[i], rvecs[i], tvecs[i], mtx, dist)
    error = cv2.norm(pImgs[i], pIm2, cv2.NORM_L2) / len(pIm2)
    total_error += error

print("total error: ", total_error / len(pObjs))

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

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

相关文章

docker进行jenkins接口自动化测试持续集成实战

文章目录 一、接口功能自动化测试项目源码讲解二、接口功能自动化测试运行环境配置1、下载jdk,maven,git,allure并配置对应的环境变量2、使用docker安装jenkins3、配置接口测试的运行时环境选择对应节点4、jenkins下载插件5、jenkins配置环境…

开源大语言模型(LLM)汇总(持续更新中)

随着ChatGPT的火爆,越来越多人希望在本地运行一个大语言模型。为此我维护了这个开源大语言模型汇总,跟踪每天不发的大语言模型和精调语言模型。 我将根据个模型采用的基础大模型进行分类,每个大模型下列出各派生模型。 Alpaca (Stanford) 斯…

[lesson08]函数重载分析(上)

函数重载分析(上) 自然语言中的上下文 你知道下面词汇中"洗"字的含义吗? 结论: 能和“洗”字搭配的词汇有很多“洗”字和不同的词汇搭配有不同的含义 重载的概念 重载 如果一个标识符在不同的上下文有不同的意义如: “洗”和不…

SRS 实时视频服务器搭建及使用

一、SRS 介绍 SRS是一个开源的(MIT协议)简单高效的实时视频服务器,支持RTMP、WebRTC、HLS、HTTP-FLV、SRT、MPEG-DASH和GB28181等协议。 SRS媒体服务器和FFmpeg、OBS、VLC、 WebRTC等客户端配合使用,提供流的接收和分发的能力&am…

Memcached 教程之 PHP 连接 Memcached 服务(十)

PHP 连接 Memcached 服务 在前面章节中我们已经介绍了如何安装 Memcached 服务,接下来我们为大家介绍 PHP 如何使用 Memcached 服务。 PHP Memcache 扩展安装 PHP Memcache 扩展包下载地址:PECL :: Package :: memcache,你可以下载最新稳定…

MySQL-排序与分页

1. 排序 如果没有使用排序操作,默认情况下查询返回的数据是按照添加数据的顺序显示的。 SELECT * FROM employees;1.1 基本使用 1)使用 ORDER BY 对查询到的数据进行排序操作。 升序:ASC(ascend)降序:DESC (descend) 练习&am…

利用Idea实现Ajax登录(maven工程)

一、新建一个maven工程(不会建的小伙伴可以参考Idea引入maven工程依赖(保姆级)-CSDN博客),工程目录如图 ​​​​​​​ js文件可以上up网盘提取 链接:https://pan.baidu.com/s/1yOFtiZBWGJY64fa2tM9CYg?pwd5555 提取码&…

ChatGPT 的核心 GPT 模型:探究其生成式预训练变换架构的革新与应用潜力

GPT(Generative Pre-trained Transformer)模型是一种深度学习模型,由OpenAI于2018年首次提出,并在随后的几年中不断迭代发展,包括GPT-2、GPT-3以及最新的GPT-4。GPT模型在自然语言处理(NLP)领域…

齐护机器人方位传感器指南针罗盘陀螺仪

一、方位传感器原理及功能说明 齐护方位传感器是一款集成了三轴磁传感器芯片的方位传感器模块。适用于无人机、机器人、移动和个人手持设备中的罗盘(指南针)、导航和游戏等高精度应用。模块可以感应XYZ平面角度外,还可实现1至2的水平面角度罗…

【瑞萨RA6M3】1. 基于 vscode 搭建开发环境

基于 vscode 搭建开发环境 1. 准备2. 安装2.1. 安装瑞萨软件包2.2. 安装编译器2.3. 安装 cmake2.4. 安装 openocd2.5. 安装 ninja2.6. 安装 make 3. 生成初始代码4. 修改 cmake 脚本5. 调试准备6. 仿真 1. 准备 需要瑞萨仓库中的两个软件: MDK_Device_Packs.zipse…

故障诊断 | 一文解决,PLS偏最小二乘法的故障诊断(Matlab)

效果一览 文章概述 故障诊断 | 一文解决,PLS偏最小二乘法的故障诊断(Matlab) 模型描述 偏最小二乘法(Partial Least Squares, PLS)是一种统计建模方法,用于建立变量之间的线性关系模型。它是对多元线性回归方法的扩展,特别适用于处理高维数据和具有多重共线性的数据集。…

element UI中设置图片的高度并支持PC和手机自适应

系列文章目录 一、elementui 导航菜单栏和Breadcrumb 面包屑关联 二、elementui 左侧导航菜单栏与main区域联动 三、elementui 中设置图片的高度并支持PC和手机自适应 四、elementui 实现一个固定位置的Pagination(分页)组件 文章目录 系列文章目录…

github中git clone需要username和password问题

username:一般指你的昵称 password:一般指Creating a fine-grained personal access token 这个的获取办法如下(注意,在11步的时候,记得打开你仓库对应的一些access 权限): Note: Fine-graine…

clickhouse MPPDB数据库--新特性使用示例

clickhouse 新特性: 从clickhouse 22.3至最新的版本24.3.2.23,clickhouse在快速发展中,每个版本都增加了一些新的特性,在数据写入、查询方面都有性能加速。 本文根据clickhouse blog中的clickhouse release blog中,学…

wordpress全站开发指南-面向开发者及深度用户(全中文实操)--php函数

php函数 wordpress会封装一部分函数&#xff0c;比如bloginfo该函数的作用是直接调用你设置的你的网站的名称 示例 This is our amazing custom theme <?php echo 22; function myfirstfunction(){ echo 33; echo "<p>Hello ,this is my first function</…

Node.JS多线程PromisePool之promise-pool库实现

什么是Promise Pool Map-like, concurrent promise processing for Node.js. Promise-Pool是一个用于管理并发请求的JavaScript库&#xff0c;它可以限制同时进行的请求数量&#xff0c;以避免过多的请求导致服务器压力过大。使用Promise-Pool可以方便地实现对多个异步操作的并…

基于spring boot的漫画之家系统

基于spring boot的漫画之家系统设计与实现 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&…

云计算面临的威胁

目录 一、概述 二、威胁建模分析 2.1 威胁建模的概念 2.2 威胁建模起到的作用 2.3 威胁建模的流程 2.3.1 威胁建模流程图 2.3.2 威胁建模流程内容 2.3.2.1 绘制数据流图 2.3.2.2 威胁识别与分析 2.3.2.2.1 STRIDE威胁分析方法论 2.3.2.3 制定消减措施 2.3.2.3.1 消减…

注解,自定义注解和元注解

1.注解 1.1.注解概述、作用 注解&#xff08;Annotation&#xff09;&#xff0c;也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性&#xff0c;与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面&#xff0…

后端灰度发布

在软件开发中&#xff0c;"灰度"通常指的是渐进式地将新功能、更新或改进引入到生产环境中&#xff0c;但只对一小部分用户或流量进行部署和测试的过程。这种方法允许开发团队在生产环境中逐步测试新功能&#xff0c;以确保其稳定性、可靠性和用户体验&#xff0c;同…