cv.calibrateCamera
相机造成的失真类型
如何找到相机的内在和外在特性
如何基于这些特性来消除图像失真
基础
一些针孔相机会对图像造成严重失真。两种主要的畸变是径向畸变和切向畸变。
径向变形会使直线看起来是弯曲的。点离图像中心越远,径向失真就越大。例如,下面显示了一个图像,其中棋盘的两个边缘用红线标记。但是,你可以看到棋盘的边界不是一条直线,与红线不匹配。所有预期的直线都凸起了。
径向畸变可以表示如下:
类似地,由于图像拍摄透镜没有完全平行于成像平面对准,所以发生切向失真。因此,图像中的某些区域看起来可能比预期的更近。切向畸变的量可以表示如下:
简而言之,我们需要找到五个参数,即失真系数,由下式给出:
除此之外,我们还需要一些其他信息,比如相机的内在和外在参数。固有参数是特定于相机的。它们包括像焦距(fx,fy)和光学中心(cx,cy)这样的信息。焦距和光学中心可用于创建相机矩阵,该矩阵可用于消除特定相机镜头造成的失真。相机矩阵对于特定的相机是唯一的,因此一旦计算出来,它就可以在同一相机拍摄的其他图像上重复使用。它表示为3x3矩阵。
外部参数对应于将3D点的坐标转换为坐标系的旋转和平移矢量。
对于立体应用,需要首先校正这些失真。为了找到这些参数,我们必须提供一些定义良好的模式(例如棋盘)的样本图像。我们找到了一些我们已经知道相对位置的特定点(例如棋盘上的直角)。我们知道这些点在现实世界空间中的坐标,也知道图像中的坐标。所以我们可以求解失真系数。为了获得更好的结果,我们至少需要10个测试模式。
如上所述,我们需要至少10个测试模式来进行相机校准。OpenCV附带了一些棋盘的图像(请参阅samples/data/left01.jpg–left14.jpg),因此我们将使用这些图像。考虑一个棋盘的图像。相机校准所需的重要输入数据是3D真实世界点的集合以及图像中这些点的对应2D坐标。2D图像点是可以的,我们可以很容易地从图像中找到。(这些图像点是棋盘中两个黑色方块相互接触的位置)
那么来自真实世界空间的3D点呢?这些图像是从静态相机中拍摄的,棋盘被放置在不同的位置和方向。所以我们需要知道(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图像点称为图像点。
Setup
因此,要在棋盘中找到图案,我们可以使用函数cv.findChessboardCorners()。我们还需要传递我们正在寻找的图案类型,如8x8网格、5x5网格等。在本例中,我们使用7x6网格。(通常棋盘有8x8个方格和7x7个内角)。它返回角点和retval,如果获得图案,该值将为True。这些角将按顺序(从左到右,从上到下)放置
Note
此功能可能无法在所有图像中找到所需的图案。因此,一个好的选择是编写代码,这样,它启动相机并检查每一帧所需的模式。获得图案后,找到角落并将其存储在列表中。此外,在阅读下一帧之前提供一些间隔,以便我们可以将棋盘调整到不同的方向。继续此过程,直到获得所需数量的良好图案。即使在这里提供的例子中,我们也不确定给出的14个图像中有多少是好的。因此,我们必须阅读所有的图像,只拍摄好的图像。
我们可以使用圆形网格来代替棋盘。在这种情况下,我们必须使用函数cv.findCirclesGrid()来查找模式。较少的图像足以使用圆形网格执行相机校准。
一旦我们找到了角,我们可以使用cv.canerSubPix()来提高它们的准确性。我们也可以使用cv.drawChessbordCorners()来绘制图案。所有这些步骤都包含在下面的代码中:
点击查看代码
import numpy as np
import cv2 as cv
import glob
# termination criteria
criteria = (cv.TERM_CRITERIA_EPS + cv.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 = 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(corners2)
# Draw and display the corners
cv.drawChessboardCorners(img, (7,6), corners2, ret)
cv.imshow('img', img)
cv.waitKey(500)
cv.destroyAllWindows()
一张画有图案的图片如下所示:
校准
现在我们有了目标点和图像点,我们就可以进行校准了。我们可以使用函数cv.calirateCamera(),它返回相机矩阵、失真系数、旋转和平移矢量等。
ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
Undistortion
现在,我们可以拍摄一张图片并不侵权。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. Using cv.undistort()
这是最简单的方法。只需调用函数并使用上面获得的ROI来裁剪结果。
点击查看代码
# undistort
dst = cv.undistort(img, mtx, dist, None, newcameramtx)
# crop the image
x, y, w, h = roi
dst = dst[y:y+h, x:x+w]
cv.imwrite('calibresult.png', dst)
**2.Using remapping**
这条路有点难。首先,找到一个从失真图像到未失真图像的映射函数。然后使用重映射函数
点击查看代码
# undistort
mapx, mapy = cv.initUndistortRectifyMap(mtx, dist, None, newcameramtx, (w,h), 5)
dst = cv.remap(img, mapx, mapy, cv.INTER_LINEAR)
# crop the image
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 += error
print( "total error: {}".format(mean_error/len(objpoints)) )
**总结来说畸变矫正的过程是根据现有二维像素坐标反推三维坐标,然后再投影到二维坐标,再矫正。**
cv.calibrateCamera的更多相关文章
- 我的复杂的OpenCV编译之路(OpenCV3.1.0 + VS2010 + Win7)
教程:www.cnblogs.com/jliangqiu2016/p/5597501.html 这里主要记载我编译遇到的错误及解决方法. OpenCV3.1软件下载:https://sourcefor ...
- 相机标定问题-Matlab & Py-Opencv
一.相机标定基本理论 1.相机成像系统介绍 图中总共有4个坐标系: 图像坐标系:Op 坐标表示方法(u,v) Unit:Dots(个) 成像坐标系:Oi ...
- 基于OpenCV做“三维重建”(2)--封装标定过程
既然已经能够找到了标定点,那么下边的工作就是使用标定结果了.[这本书在这里的内容组织让人莫名其妙]但是通过阅读代码能够很方便地串起来. /*------------------------------ ...
- 【双目备课】《学习OpenCV第18章》相机模型与标定整编
一.相机模型 针孔模型.在这个简单模型中,想象光线是从场景或一个很远的物体发射过来的,但只有一条光线从该场景中的任意特定点进入针孔. 我们将这个图像进行抽象,就能够得到这样的结果: 其中,f为像到针孔 ...
- 车道线识别/Opencv/传统方法
车道检测(Advanced Lane Finding Project) 实现步骤: 使用提供的一组棋盘格图片计算相机校正矩阵(camera calibration matrix)和失真系数(disto ...
- 采用QHD分辨率使用kinect2_calibration,完成QHD图像校正
//.................................................................................//采用QHD分辨率使用kinec ...
- Camera Calibration 相机标定:Opencv应用方法
本系列文章由 @YhL_Leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/49427383 Opencv中Camer ...
- OpenCV-Python 相机校准 | 四十九
目标 在本节中,我们将学习 由相机引起的失真类型, 如何找到相机的固有和非固有特性 如何根据这些特性使图像不失真 基础 一些针孔相机会给图像带来明显的失真.两种主要的变形是径向变形和切向变形. 径向变 ...
- [OpenCV实战]50 用OpenCV制作低成本立体相机
本文主要讲述利用OpenCV制作低成本立体相机以及如何使用OpenCV创建3D视频,准确来说是模仿双目立体相机,我们通常说立体相机一般是指双目立体相机,就是带两个摄像头的那种(目就是指眼睛,双目就是两 ...
- [OpenCV实战]38 基于OpenCV的相机标定
文章目录 1 什么是相机标定? 2 图像形成几何学 2.1 设定 2.1.1 世界坐标系 2.1.2 相机坐标系 2.1.3 图像坐标系 2.2 图像形成方法总结 3 基于OpenCV的相机标定原理 ...
随机推荐
- 【Azure Redis 缓存】Azure Reids是否可以开启慢日志(slowlog)和执行config指令
问题描述 使用Azure Redis,是否可以开启慢日志来查看最近时间中执行比较耗时的指令呢? 同时,如何执行Redis的Config只能来修改配置呢? 根本原因 一:Azure Reids通过Red ...
- 【Azure API 管理】APIM添加Log-to-eventhub的策略后,一些相关APIM与Event Hub的问题
问题描述 1) APIM 到Event Hub 写入日志是否有数量限制,比如每秒最大写入数量: 2) 是否可以在同一个APIM配置多个Event Hub,如果可以该APIM写入日志的峰值是 ...
- 钉钉机器人自动关联 GitHub 发送 approval prs
摘要:用技术来解决 PM 枯燥的 approval pr 工作,本文将阐述如何自动化获取 GitHub Organization 下各个 repo 待 merge 的 pull requests 并通 ...
- 可视化学习:使用WebGL实现网格背景
前言 作为前端开发人员,我们最关注的就是应用的交互体验,而元素背景是最基础的交互体验之一.一般而言,能够使用代码实现的界面,我们都会尽可能减少图片的使用,这主要是有几方面的原因,第一,是图片会消耗更多 ...
- Windows10 windows installer卸载或安装不了软件怎么办?
先说我的方法: 1.把安装出现问题的软件或者想要卸载的软件的安装目录下的所有文件都删除. 2.用清理软件清理一下垃圾,包括注册表,这里我自己使用的是火绒->安全工具- ...
- 摆脱鼠标操作 vscode-vim-use-readme.md
vscode-vim 学习笔记 梳理下自己定义的快捷键 Normal模式返回 ESC capsLock 双击shift ctrl+[ jj ctrl+c (这个键比较特殊 用习惯y的话,考虑这个) 一 ...
- 【easy52pojie】一款方便看吾爱论坛帖子的爬虫程序
众所周知吾爱论坛一页最多显示十来条回帖,且间隔很大,每页的信息密度太低了.在帖子很庞大的情况下,一页一页翻页,着实有点痛苦. 故简单敲敲代码,使用request xpath技术做了一个论坛帖子回复查看 ...
- Android 开发Day10
这是main里面的所有代码,按版本修改过 AndroidManifest.xml <?xml version="1.0" encoding="utf-8" ...
- C++ memcpy、memmove
函数原型: void *memcpy(void *dest, const void* src, size_t count ); void *memmove(void *dest, const void ...
- Android优化总结
目录介绍 1.OOM和崩溃优化 1.1 OOM优化 1.2 ANR优化 1.3 Crash优化 2.内存泄漏优化 2.0 动画资源未释放 2.1 错误使用单利 2.2 错误使用静态变量 2.3 han ...