目标

在本节中,我们将学习

由相机引起的失真类型,

如何找到相机的固有和非固有特性

如何根据这些特性使图像不失真

基础

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

径向变形会导致直线出现弯曲。

距图像中心越远,径向畸变越大。例如,下面显示一个图像,其中棋盘的两个边缘用红线标记。但是,您会看到棋盘的边框不是直线,并且与红线不匹配。所有预期的直线都凸出。有关更多详细信息,请访问“失真(光学)”。

径向变形可以表示成如下:

xdistorted=x(1+k1r2+k2r4+k3r6)ydistorted=y(1+k1r2+k2r4+k3r6)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+k1​r2+k2​r4+k3​r6)ydistorted​=y(1+k1​r2+k2​r4+k3​r6)

同样,由于摄像镜头未完全平行于成像平面对齐,因此会发生切向畸变。因此,图像中的某些区域看起来可能比预期的要近。切向畸变的量可以表示为:

xdistorted=x+[2p1xy+p2(r2+2x2)]ydistorted=y+[p1(r2+2y2)+2p2xy]x_{distorted} = x + [ 2p_1xy + p_2(r^2+2x^2)] \\ y_{distorted} = y + [ p_1(r^2+ 2y^2)+ 2p_2xy]xdistorted​=x+[2p1​xy+p2​(r2+2x2)]ydistorted​=y+[p1​(r2+2y2)+2p2​xy]

简而言之,我们需要找到五个参数,称为失真系数,公式如下:

Distortion  coefficients=(k1k2p1p2k3)Distortion \; coefficients=(k_1 \hspace{10pt} k_2 \hspace{10pt} p_1 \hspace{10pt} p_2 \hspace{10pt} k_3)Distortioncoefficients=(k1​k2​p1​p2​k3​)

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

camera  matrix=[fx0cx0fycy001]camera \; matrix = \left [ \begin{matrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{matrix} \right ]cameramatrix=⎣⎡​fx​00​0fy​0​cx​cy​1​⎦⎤​

外在参数对应于旋转和平移矢量,其将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 Tutorials

编译|Vincent

来源|OpenCV-Python Tutorials

python自动测试相机_OpenCV-Python 相机校准 | 四十九相关推荐

  1. python自动测试s_用 Python 开启测试自动化的未来

    使用 Python 脚本迈进先进的测试自动化技术 传统上,测试自动化工程师都是使用 shell 脚本和自主开发的工具进行自动测试.现在,测试团队可以使用 Python 软件更轻松.更稳健地完成他们的测 ...

  2. python自动测试g_使用Python进行自动化测试

    目前大家对Python都有一个共识,就是他对测试非常有用,自动化测试里Python用途也很广,但是Python到底怎么进行自动化测试呢?今天就简单的向大家介绍一下怎么使用Python进行自动化测试,本 ...

  3. python自动测试模型_Selenium+Python 自动化测试模型

    学习Selenium+Python,最终的目的是为了实现自动化测试的操作. 前面几篇文章,详细介绍了搭建环境.如何准确定位测试对象以及几个常用web 控件的脚本使用,基本可以实现关于网站的登录.注册等 ...

  4. python自动测试p-使用python+requests实现接口自动化测试

    这两天一直在找直接用python做接口自动化的方法,在网上也搜了一些博客参考,今天自己动手试了一下. 一.整体结构 上图是项目的目录结构,下面主要介绍下每个目录的作用. Common:公共方法:主要放 ...

  5. OpenCV系列之相机校准 | 四十九

    目标 在本节中,我们将学习 由相机引起的失真类型 如何找到相机的固有和非固有特性 如何根据这些特性使图像不失真 基础 一些针孔相机会给图像带来明显的失真.两种主要的变形是径向变形和切向变形. 径向变形 ...

  6. OpenCV-Python 相机校准 | 四十九

    目标 在本节中,我们将学习 由相机引起的失真类型, 如何找到相机的固有和非固有特性 如何根据这些特性使图像不失真 基础 一些针孔相机会给图像带来明显的失真.两种主要的变形是径向变形和切向变形. 径向变 ...

  7. python自动测试ping IP

    python自动测试ping IP,将不通的IP保存到txt中. # -*- coding: utf-8 -*- # @Time : 2023/3/6 9:14 # @Author : JulyLi ...

  8. Python编程基础:第四十九节 鸭子类型Duck Typing

    第四十九节 鸭子类型Duck Typing 前言 实践 前言 本节我们一起学习一个非常有趣的知识点:鸭子类型.有这么一句话:If it walks like a duck, and it quacks ...

  9. python红楼梦作者_用 Python 分析《红楼梦》,后四十回是曹雪芹所写吗?(开源)...

    原标题:用 Python 分析<红楼梦>,后四十回是曹雪芹所写吗?(开源)

最新文章

  1. Linux查看CPU相关信息
  2. 更新系统时跳过某个软件包
  3. 探究Java如何实现原子操作(atomic operation)
  4. 月圆花美 中秋快乐!
  5. java开发 网关_SpringCloud系列之网关(Gateway)应用篇
  6. SpringCloud(一)
  7. 钩子的应用: 程序运行监视
  8. vue学习笔记-10-常用特性之表单操作
  9. 在Eclipse上用JAVA连接数据库
  10. PNG转ICO在线转换
  11. 乒乓球技术要领总结(保持更新)
  12. 一键清理系统垃圾文件bat
  13. 计算机平面设计主要学什么 - 兔课网,兔课网:初学平面要怎么样构图?
  14. 旷视科技2018暑期实习-算法研究员面试记
  15. JavaScript脚本访问后台服务实例
  16. 拿下它的心-车联网(二)
  17. 对于https ssl中的pem文件和key 文件的理解
  18. JS动态添加div,然后在div中添加元素
  19. 辗转相除以及辗转相减法
  20. 用C/C++实现心形曲线的绘制

热门文章

  1. 2022-02-11 学习记录:通过CSS3的clip-path实现多边形
  2. 主引导记录MBR详解
  3. 【Basis】狄利克雷分布
  4. SEO优化基础:SEO的优势与劣势
  5. Linux IP代理筛选系统
  6. 开机后显示服务器正在启动,电脑开机后卡在Windows正在启动界面上怎么办?
  7. 2020大厂面试集合,GitHub,百度,flutter下拉加载
  8. 富有人情味的入住体验
  9. 观《穹顶之下》一些思考
  10. C-lodop打印控件 实现批量打印