OpenCV-Python 相机校准和消除畸变
目标
在本节中,我们将学习
- 由相机引起的失真类型,
- 如何找到相机的固有和非固有特性
- 如何根据这些特性使图像不失真
基础
一些针孔相机会给图像带来明显的失真。两种主要的变形是径向变形和切向变形。径向变形会导致直线出现弯曲。距图像中心越远,径向畸变越大。例如,下面显示一个图像,其中棋盘的两个边缘用红线标记。但是,您会看到棋盘的边框不是直线,并且与红线不匹配。所有预期的直线都凸出。有关更多详细信息,请访问“失真(光学)”。
径向变形可以表示成如下:
同样,由于摄像镜头未完全平行于成像平面对齐,因此会发生切向畸变。因此,图像中的某些区域看起来可能比预期的要近。切向畸变的量可以表示为:
简而言之,我们需要找到五个参数,称为失真系数,公式如下:
除此之外,我们还需要其他一些信息,例如相机的内在和外在参数。内部参数特定于摄像机。它们包括诸如焦距(fx,fy)(f_x,f_y)(fx,fy)和光学中心(cx,cy)(c_x,c_y)(cx,cy)之类的信息。焦距和光学中心可用于创建相机矩阵,该相机矩阵可用于消除由于特定相机镜头而引起的畸变。相机矩阵对于特定相机而言是唯一的,因此一旦计算出,就可以在同一相机拍摄的其他图像上重复使用。它表示为3x3矩阵:
外在参数对应于旋转和平移矢量,其将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。这些角将按顺序放置(从左到右,从上到下)
另外
此功能可能无法在所有图像中找到所需的图案。因此,一个不错的选择是编写代码,使它启动相机并检查每帧所需的图案。获得图案后,找到角并将其存储在列表中。另外,在阅读下一帧之前请提供一些时间间隔,以便我们可以在不同方向上调整棋盘。继续此过程,直到获得所需数量的良好图案为止。即使在此处提供的示例中,我们也不确定给出的14张图像中有多少张是好的。
因此,我们必须阅读所有图像并仅拍摄好图像。
除了棋盘,我们还可以使用圆形网格。
在这种情况下,我们必须使用函数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点
imgpoints = [] # 图像中的2d点
images = glob.glob('*.jpg')
for fname in images:img = cv.imread(fname)gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)# 找到棋盘角落ret, corners = cv.findChessboardCorners(gray, (7,6), None)# 如果找到,添加对象点,图像点(细化之后)if ret == True:objpoints.append(objp)corners2 = cv.cornerSubPix(gray,corners, (11,11), (-1,-1), criteria)imgpoints.append(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裁剪结果即可。
# undistort
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
该方式有点困难。首先,找到从扭曲图像到未扭曲图像的映射函数。然后使用重映射功能。
# undistort
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 xrange(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)) )
欢迎关注公众号:算法工程师的学习日志
OpenCV-Python 相机校准和消除畸变相关推荐
- 利用OpenCV执行相机校准
文章目录 什么是相机校准,为什么它很重要? 为什么要使用棋盘? 使用 OpenCV 执行相机校准 打印棋盘 测量正方形长度 从不同的距离和方向拍摄棋盘的照片 找到角点 编写用于相机校准的 Python ...
- OpenCV全向相机校准Omnidirectional Camera Calibration
OpenCV全向相机校准 全向相机校准 前言 单相机校准 立体校准 图像校正 立体声重建 全向相机校准 前言 本教程将介绍全向摄像机校准模块的以下部分: 校准单个摄像机. 校准一对立体声摄像机. 校正 ...
- 使用OpenCV进行相机校准
使用OpenCV进行相机校准 使用OpenCV进行相机校准 理论 目标 源代码 解释 阅读设置 获取下一个输入(如果失败或我们有足够的输入)-校准 在当前输入中查找模式 向用户显示状态和结果,以及应用 ...
- OpenCV交互式相机校准应用
OpenCV交互式相机校准应用 交互式相机校准应用 主要应用功能 参数说明 主要参数: 进阶参数: 双圆圈图案 Data 筛选 校准过程 结果 交互式相机校准应用 根据经典的校准技术,用户必须首先收集 ...
- opencv python 相机启动慢问题 VideoCapture 感觉c++同理
着急看去下面看结果 首先先说一下总共我自己的环境加上我找朋友帮忙测试的环境 系统 win10 win11 都有 环境包含 python310 ue整套c++开发环境 前端环境 后端环境 摄像头牌子 包 ...
- python利用opencv进行相机标定获取参数,并根据畸变参数修正图像附有全部代码(流畅无痛版)
python利用opencv进行相机标定获取参数,并根据畸变参数修正图像附有全部代码 一.前言 今天的低价单孔摄像机(照相机)会给图像带来很多畸变.畸变主要有两 种:径向畸变和切想畸变.如下图所示,用 ...
- python自动测试相机_OpenCV-Python 相机校准 | 四十九
目标 在本节中,我们将学习 由相机引起的失真类型, 如何找到相机的固有和非固有特性 如何根据这些特性使图像不失真 基础 一些针孔相机会给图像带来明显的失真.两种主要的变形是径向变形和切向变形. 径向变 ...
- OpenCV系列之相机校准 | 四十九
目标 在本节中,我们将学习 由相机引起的失真类型 如何找到相机的固有和非固有特性 如何根据这些特性使图像不失真 基础 一些针孔相机会给图像带来明显的失真.两种主要的变形是径向变形和切向变形. 径向变形 ...
- 【ZED】从零开始使用ZED相机(五):Opencv+Python实现相机标定(双目)
引言 同样Opencv+Python实现双目相机的标定,单目标定详见[ZED]从零开始使用ZED相机(五):Opencv+Python实现相机标定(单目) 1 cv2.stereoCalibrate ...
最新文章
- 英伟达推出全球首个元宇宙平台,豪砸数亿是为什么?
- JAVA从数据库中拿到数据
- android aar 修改,Android aar包修改代码后重新打包
- Javascript基础系列之(六)循环语句(do while循环)
- how to make Membership header extension fields editable
- oracle thread日志查看,请教归档日志的视图v$archived_log里的thread#的问题
- 只有经验丰富的开发人员才能教您有关Java的5件事
- Linux 下的多线程下载工具
- 老百姓需要这样的智慧城市
- 【Elasticsearch】es如何停用节点
- JAVA面试要点004_JAVA编程过程中为了性能优化_应该注意到的地方
- 【廖雪峰官方网站/Java教程】反射
- JVM(四)—一道面试题搞懂JVM类加载机制
- 怎么卸载apowerrec_如何卸载win10自带的游戏中心
- 联想小新校园活动推广策划案
- 做软件测试必须了解的7个常用术语
- 如何关闭 Win 10 系统自动更新
- 网络请求及各类错误代码含义总结(包含AFN错误码大全)
- 2021年抖音0粉丝无货源直播带货最新技巧
- Eclipse 3.6 M7(太阳神版)发布