OpenCV-Python(42):摄像机标定

目标

  • 学习摄像机畸变以及摄像机的内部参数和外部参数
  • 根据摄像机相关参数对畸变图像进行修复

基础说明 

        今天的低价单孔摄像机(照相机)会给图像带来很多畸变。畸变主要有两种:径向畸变和切向畸变。如下图所示用红色直线将棋盘的两个边标注出来,但是你会发现棋盘的边界并不和红线重合。所有我们认为应该是直线的也都凸出来了。

这种畸变可以通过下面的方程组进行纠正:

        于此相似,另外一个畸变是切向畸变,这是由于透镜与成像平面不可能绝对平行造成的。这种畸变会造成图像中的某些点看上去的位置会比我们认为的位置要近一些。它可以通过下列方程组进行校正:

        简单来说,如果我们想对畸变的图像进行校正就必须找到五个造成畸变的系数:

        除此之外,我们还需要再找到一些信息,比如摄像机的内部和外部参数。内部参数是摄像机特异的。它包括的信息有焦距(fx, fy),光学中心(cx, cy)等。这也被称为摄像机矩阵。它完全取决于摄像机自本身,只需要计算算一次以后就可以已知使用了。可以用下面的3x3 的矩阵表示:

        外部参数与旋转和变换向量相对应,它可以将3D点的坐标转换到坐标系系统中。 

        在3D 相关应用中,必须先校正这些畸变。为了找到这些参数,我们必须先提供一些包含明显图案模式的样本图片(比如说棋盘)。我们可以在上面找到一些特殊点(如棋盘的四个角点)。我们起到这些特殊点在图片中的位置以及它们的真实位置。有了这些信息,我们就可以使用数学方法求解畸变系数。这就是整个故事的摘要了。为了得到更好的结果我们至少需要10 个这样的图案模式。

代码 

         如上所述,我们至少需要10 图案模式来进行摄像机标定。OpenCV 自带了一些棋盘图像(/sample/cpp/left001.jpg--left14.jpg), 所以我们可以使用它们。为了便于理解我们可以认为仅有一张棋盘图像。重要的是在进行摄像机标定时我们需要输入一组3D 真实世界中的点以及与它们对应2D 图像中的点。2D 图像的点可以在图像中很容易的找到。(这些点在图像中的位置是棋盘上两个黑色方块相互接触的地方)

        那么真实世界中的3D 的点呢?这些图像来源与静态摄像机和棋盘不同的摆放位置和朝向。所以我们需要知道(X,Y,Z)的值。但是为了简单,我们可以说棋盘在XY 平面是静止的(所以Z 总是等于0)摄像机在围着棋盘移动。这种假设我们只需要知到(X,Y) 的值就可以了。现在为了求X,Y 的值,我们只需要传入这些点(0,0),(1,0),(2,0)...它们代表了点的位置。在这个例子中,我们的结果的单位就是棋盘(单个)方块的大小。但是如果我们知道单个方块的大小(假如􄖣30mm),我们输入的值就可以是(0,0),(30,0),(60,0)...结果的单位就是mm。在本例中我们不知道方块的大小(因为不是我们拍的)所以只能用前一种方法了。3D 点被称为对象点,2D 图像点被称为图像点

 设置

        为了找到棋盘的图案,我们要使用函数cv2.findChessboardCorners()。我们还需要传入图案的类型,比如 8x8 的格子或5x5 的格子等。在本例中我们使用的7x8 的格子。(常情况下棋盘都是8x8 或者7x7)。它会返回角点,如果得到图像的返回值类型(Retval)就会是True。这些角点会按顺序排列(从左到右,从上到下)。

        在找到这些角点之后我们可以使用函数cv2.cornerSubPix() 增加准确度。我们使用函数cv2.drawChessboardCorners() 绘制图案。所有的这些步骤都被包含在下面的代码中了:

import numpy as np
import cv2
import glob# termination criteria
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)# prepare object points, like (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)# Arrays to store object points and image points from all the images.
objpoints = [] # 3d point in real world space
imgpoints = [] # 2d points in image plane.images = glob.glob('*.jpg')for fname in images:img = cv2.imread(fname)gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)# Find the chess board cornersret, corners = cv2.findChessboardCorners(gray, (7,6),None)# If found, add object points, image points (after refining them)if ret == True:objpoints.append(objp)corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)imgpoints.append(corners2)# Draw and display the cornersimg = cv2.drawChessboardCorners(img, (7,6), corners2,ret)cv2.imshow('img',img)cv2.waitKey(500)
cv2.destroyAllWindows()

一副图像和被绘制在上边的图案:

标定 

         在得到了这些对象点和图像点之后,我们已经准备好来做摄像机标定了。我们要使用的函数是cv2.calibrateCamera()。它会返回摄像机矩阵,畸变系数、旋转和变换向量等。

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

畸变校正 

        现在我们找到我们想要的东西了,我们可以找到一幅图像来对它进行校正。OpenCV 提供两种方法供我们都学习一下。不过在那之前我们可以使用从函数cv2.getOptimalNewCameraMatrix() 得到的自由缩放系数对摄像机矩阵进行优化。如果缩放系数alpha = 0,返回的非畸变图像会带有最少量的不想要的像素。它甚至有可能在图像角点去除一些像素。如果alpha = 1,所有的像素都会被返回,还有一些黑图像。它还会返回一个ROI 图像,我们可以用来对结果进行裁剪。我们读取一个新的图像(left2.ipg):

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

        使用cv2.undistort() 这是最简单的方法。只需使用这个函数和上面得到的ROI 对结果进行裁剪。 

# undistort
dst = cv2.undistort(img, mtx, dist, None, newcameramtx)
# crop the image
x,y,w,h = roi
dst = dst[y:y+h, x:x+w]
cv2.imwrite('calibresult.png',dst)

        使用remapping 这应该属于“曲线救国”了。首先我们要找到从畸变图像到非畸变图像的映射方程。再使用映射方程。 

# undistort
mapx,mapy = cv2.initUndistortRectifyMap(mtx,dist,None,newcameramtx,(w,h),5)
dst = cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)
# crop the image
x,y,w,h = roi
dst = dst[y:y+h, x:x+w]
cv2.imwrite('calibresult.png',dst)

这两中方法给出的结果是相同的。结果如下所示:

        你会发现结果图像中所有的边界都变直了。
        现在我们可以使用Numpy 提供写函数(np.savez,np.savetxt 等)将摄像机矩阵和畸变系数保存以便以后使用。 

反向投影误差

        我们可以利用反向投影误差对我们找到的参数的准确性进行估计。得到的结果越接近0越好。有了内部参数、畸变参数和旋转变换矩,我们就可以使用cv2.projectPoints() 将对象点转换到图像点。然后就可以计算变换得到图像与角点检测算法的绝对差了。然后我们计算所有标定图像的误差平均值。

mean_error = 0
for i in xrange(len(objpoints)):imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)error = cv2.norm(imgpoints[i],imgpoints2, cv2.NORM_L2)/len(imgpoints2)tot_error += error
print ("total error: ", mean_error/len(objpoints))

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

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

相关文章

小程序样例1:简单待办列表

基本功能: 显示所有待办列表(点击不同的文本进行显示) 没完成的待办 已完成的待办 新建待办test 清除待办foo 代码js文件: //index.js //获取应用实例 const app getApp(); Page({data: {todo: ,todos: [{"id": 1474…

NanoPi NEO Air 配置指南

近期淘了块非常小巧的开发板 NaoPi NEO Air,这块板子仅有 40mm x 40mm 大小,除了一个 MicroUSB OTG 接口外如果想要扩展USB口就得使用左侧排针增加USB接口了。 关于 NanoPi NEO Air NanoPi NEO Air是一款基于全志H3四核A7主控的开源无线创客板&#xff…

SDRAM小项目——命令解析模块

简单介绍: 在FPGA中实现命令解析模块,命令解析模块的用来把pc端传入FPGA中的数据分解为所需要的数据和触发命令,虽然代码不多,但是却十分重要。 SDRAM的整体结构如下,可以看出,命令解析模块cmd_decode负责…

springcloud-cloud provider-payment8001微服务提供者支付Module模块

文章目录 IDEA新建project工作空间cloud-provider-payment8001微服务提供者支付Module模块建表SQL测试 IDEA新建project工作空间 微服务cloud整体聚合父工程Project 写pom文件 <?xml version"1.0" encoding"UTF-8"?><project xmlns"htt…

[足式机器人]Part2 Dr. CAN学习笔记-Advanced控制理论 Ch04-9 可观测性与分离原理

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记-Advanced控制理论 Ch04-9 可观测性与分离原理

vue知识-07

ref # 放在组件上&#xff1a; this.$refs.名字 ---组件对象.组件对象.属性 # 在vue项目中使用: // 写在组件上 <HelloWorld ref"my_hello_world"></HelloWorld>handleClick() {console.log(this.$refs)this.$refs.my_hello_world.name # 获取子组件中…

vue:使用【3.0】:条件模块

一、条件层级效果图 二、代码 <template><ContentWrap><!-- 添加条件分支:level1 --><div class"btnBox" v-if"isEdit"><el-button type"primary" click"add">添加条件分支</el-button></div…

UE5 RPG AttributeSet的设置

AttributeSet 负责定义和持有属性并且管理属性的变化。开发者可以子类化UAttributeSet。在OwnerActor的构造方法中创建的AttributeSet将会自动注册到ASC。这一步必须在C中完成。 Attributes 是由 FGameplayAttributeData定义的浮点值。 Attributes能够表达从角色的生命值到角色…

《现代C++语言核心特性解析》笔记草稿

仅供学习记录之用&#xff0c;谢绝转发 第1章 新基础类型&#xff08;C11&#xff5e;C20&#xff09; 1.1 整数类型long long 更多笔记 “在C中应该尽量少使用宏&#xff0c;用模板取而代之是明智的选择。C标准中对标准库头文件做了扩展&#xff0c;特化了long long和unsi…

【C++】vector的使用及模拟实现

目录 一、vector的介绍及使用1.1 介绍vector1.2 vector的使用1.2.1 构造1.2.2 遍历访问1.2.3 容量空间1.2.4 增删查改 二、vector的模拟实现2.1 成员变量2.2 迭代器相关函数2.3 构造-析构-赋值重载2.3.1 无参构造2.3.2 有参构造12.3.3 有参构造22.3.4 拷贝构造2.3.5 赋值重载2.…

RAG基础功能优化、以及RAG架构优化

RAG基础功能优化 对RAG的基础功能优化&#xff0c;我们要从RAG的流程入手[1]&#xff0c;可以在每个阶段做相应的场景优化。 从RAG的工作流程看&#xff0c;能优化的模块有&#xff1a;文档块切分、文本嵌入模型、提示工程优化、大模型迭代。下面针对每个模块分别做说明&#…

兴达易控EtherCAT转Profibus网关让工业自动化变得轻松快捷

EtherCAT转Profibus网关&#xff08;XD-ECPBM20&#xff09;是一种用于实现不同通信协议间互联互通的设备。它主要用于工业控制系统中&#xff0c;能够将EtherCAT总线的数据传输转换为Profibus网络可接受的格式。这样的网关设备在工业自动化领域有着广泛的应用&#xff0c;因为…

【计算机网络】第七,八,九章摘要重点

第七章网络管理 1.计算机网络面临的两大威胁&#xff1f; 恶意程序有&#xff1a;计算机病毒&#xff0c;计算机蠕虫&#xff0c;特洛伊木马&#xff0c;逻辑炸弹&#xff0c;后门入侵和流氓软件。 2.安全的计算机网络四个目标&#xff1a; 机密性&#xff0c;端点鉴别&…

一文解析 Copycat Dex与 Bitcat Dex的区别

Copycat Dex和 Bitcat Dex都带一个 Cat 并且都是衍生品协议&#xff0c;很多人都会误认为这两个是同一个项目&#xff0c;实际不然。它们是面向两个不同赛道、不同资产类型的衍生品项目。 Copycat Dex和 Bitcat Dex都是衍生品 DEX&#xff0c;它们最本质的区别主要在于&#xf…

软件测试|使用Python提取出语句中的人名

简介 在自然语言处理&#xff08;NLP&#xff09;中&#xff0c;提取文本中的人名是一项常见的任务。Python作为一种流行的编程语言&#xff0c;拥有强大的NLP库和工具&#xff0c;使我们能够轻松地进行这项任务。在本文中&#xff0c;我们将使用Python示例来演示如何提取文本…

MC使用Waterfall 跨服

前言 想弄一个跨服&#xff0c;目前这篇文章是边测试边写的&#xff0c;两个子服都是在同一个机器上运行的 如果两个子服在不同的网络&#xff0c;跨服的延迟就会比较高 两个子服 s1 和 s2 都是使用folia核心 版本1.20.1s1 端口: 25565s2 端口 : 25566 1.下载 Waterfall W…

Hello 2024补题

Wallet Exchange&#xff08;Problem - A - Codeforces&#xff09; 题目大意&#xff1a;A&#xff0c;B做游戏&#xff0c;它们的钱包里各有a,b个硬币&#xff0c;轮到它们操作时&#xff0c;它们可以扔掉自己或者对手钱包里的硬币&#xff0c;谁不能操作谁输&#xff0c;问…

Kafka的安装、管理和配置

Kafka的安装、管理和配置 1.Kafka安装 官网: https://kafka.apache.org/downloads 下载安装包,我这里下载的是https://archive.apache.org/dist/kafka/3.3.1/kafka_2.13-3.3.1.tgz Kafka是Java生态圈下的一员&#xff0c;用Scala编写&#xff0c;运行在Java虚拟机上&#xf…

【大数据】Flink 详解(九):SQL 篇 Ⅱ

《Flink 详解》系列&#xff08;已完结&#xff09;&#xff0c;共包含以下 10 10 10 篇文章&#xff1a; 【大数据】Flink 详解&#xff08;一&#xff09;&#xff1a;基础篇【大数据】Flink 详解&#xff08;二&#xff09;&#xff1a;核心篇 Ⅰ【大数据】Flink 详解&…

Flutter之配置环境创建第一个项目

随着时代发展&#xff0c;使用Flutter开发的项目越来越多&#xff0c;于是踏上了Flutter开发之路。 作为一个Android开发人员&#xff0c;也只能被卷到与时俱进&#xff0c;下面一起创建一个Flutter项目吧。 一、Android开发&#xff0c;电脑上已经具备了的条件&#xff1a; …