相机标定——OpenCV-Python Tutorials
目标
- 我们将了解导致相机失真、扭曲的内因与外因
- 我们将试着找到这些畸变参数,并消除畸变
基础
如今大量廉价的摄像机导致了很多照片畸变。两个主要的畸变是径向畸变和切向畸变。
由于径向畸变,直线会变弯。距离图片中心越远,它的影响越大。如下面这张图片,棋盘格中被红线标记的边缘。你会发现棋盘格的边缘并不与直红线重合,而是变弯了。可以到维基百科查看更多细节Distortion (optics) 。

这种畸变可以用如下公式消除:

同样的,另一种畸变-切向畸变是由于相机镜头没有与被拍摄物体平行造成的。因为有些区域会看起来更近。这可以通过下面公式消除:

总的来说,我们需要找到5个参数,即畸变系数:

除此之外,我们还需要找到一些另外的信息,如相机的内在和外部参数。内在参数是由相机内部决定的,它取决于特定的相机。它包括焦距(
),光心(
)等。这些也称作相机矩阵。它们只取决于相机本身,因此只需要计算一次。

外部参数对应于旋转和平移向量,它将三维点的坐标转换为坐标系统。
对于三维应用程序,上面这些畸变需要先被计算。要找到所有这些参数,我们需要做的是提供一些定义好的模式的示例图像(如棋盘格)。我们找到模式中特定的点(格子的交点)。这样,我们知道这些点在真实世界中的坐标,也知道它们在图片中的坐标。有了这些数据,就可以解决一些数学问题,从而得到畸变系数。这就是解决整个问题的思路。为了得到更好的结果,我们至少需要10张测试模式图片。
代码
如上所述,我们至少需要10张测试模板图片来进行相机标定。OpenCV提供了一些棋盘格图片(samples/cpp/left01.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()这个函数。我们同样需要传递我们要找什么样式,比如你这个棋盘格是8*8的,或是5*5的。在这个例子中,我们使用7*6的棋盘格。(通常棋盘格有8*8个方格,有7*7个内格点)。它返回每一一个角点,如果匹配到了模式,它将返回是True。这些角点将按一定顺序标注出来(从左到右,从上到下)
注意
这个函数并不能识别每张图片的样式。因此一个更好的选择是按照如下的思路写代码,打开摄像头检查每一帧图片是否包含样式。如果包含了需要的样式,找到角点并把他们存到一个列表中。同时提供一些间隔的时间,以便于我们调整棋盘格到不同的方向。重复这些步骤,直到足够多的图片被采集。即使在我们提供的例子中,我们也不确定14张图片中有多少合格的图片。因此我们要使用所有的图片,然后在其中找合适的一些。
注意
除了使用棋盘格,我们也可以使用圆形格子,这就需要使用cv2.findCirclesGrid() 来寻找样式。据说,使用圆形网格的时候可以减少图片的采样数。
当我们找到角点之后,我们可以使用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 corners
ret, 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 corners
img = 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.jpg)
img = cv2.imread('left12.jpg')
h, w = img.shape[:2]
newcameramtx, roi=cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),1,(w,h))
1. Using 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)
2. Using 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 etc)将相机矩阵和畸变系数存起来,以便以后使用。
重投影误差
重投影误差给出了一个很好的判别方式来检测找到的畸变参数的准确度。它尽可能的趋近于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)
相机标定——OpenCV-Python Tutorials的更多相关文章
- 张正友相机标定Opencv实现以及标定流程&&标定结果评价&&图像矫正流程解析(附标定程序和棋盘图)
使用Opencv实现张正友法相机标定之前,有几个问题事先要确认一下,那就是相机为什么需要标定,标定需要的输入和输出分别是哪些? 相机标定的目的:获取摄像机的内参和外参矩阵(同时也会得到每一幅标定图像的 ...
- 【视频开发】【计算机视觉】相机标定(Camera calibration)原理、步骤
相机标定(Camera calibration)原理.步骤 author@jason_ql(lql0716) http://blog.csdn.net/lql0716 在图像测量过程以及机器视觉应用 ...
- SLAM入门之视觉里程计(6):相机标定 张正友经典标定法详解
想要从二维图像中获取到场景的三维信息,相机的内参数是必须的,在SLAM中,相机通常是提前标定好的.张正友于1998年在论文:"A Flexible New Technique fro Cam ...
- 相机标定:PNP基于单应面解决多点透视问题
利用二维视野内的图像,求出三维图像在场景中的位姿,这是一个三维透视投影的反向求解问题.常用方法是PNP方法,需要已知三维点集的原始模型. 本文做了大量修改,如有不适,请移步原文: ...
- 相机标定 matlab opencv ROS三种方法标定步骤(3)
三 , ROS 环境下 如何进行相机标定 刚开始做到的时候遇到一些问题没有记录下来,现在回头写的时候都是没有错误的结果了,首先使用ROS标定相机, 要知道如何查看节点之间的流程图 rosrun r ...
- 相机标定 matlab opencv ROS三种方法标定步骤(1)
一 . 理解摄像机模型,网上有很多讲解的十分详细,在这里我只是记录我的整合出来的资料和我的部分理解 计算机视觉领域中常见的三个坐标系:图像坐标系,相机坐标系,世界坐标系,实际上就是要用矩阵来表 示各个 ...
- 使用OpenCV进行相机标定
1. 使用OpenCV进行标定 相机已经有很长一段历史了.但是,伴随着20世纪后期的廉价针孔照相机的问世,它们已经变成我们日常生活的一种常见的存在.不幸的是,这种廉价是由代价的:显著的变形.幸运的是, ...
- opencv 角点检测+相机标定+去畸变+重投影误差计算
https://blog.csdn.net/u010128736/article/details/52875137 https://blog.csdn.net/h532600610/article/d ...
- OpenCV相机标定和姿态更新
原帖地址: http://blog.csdn.net/aptx704610875/article/details/48914043 http://blog.csdn.net/aptx704610875 ...
- 相机标定 matlab opencv ROS三种方法标定步骤(2)
二 ubuntu下Opencv的相机标定 一般直接用Opencv的源码就可以进行相机的标定,但是可能只是会实现结果,却不懂实现的过程,我也是模模糊糊的看了<计算机视觉中的多视图几何>以及 ...
随机推荐
- day02python 整型 布尔
今日内容 int bool 详细内容 1.整型(int) Py2 32位电脑 64位电脑 超出范围后python将自动转换成long(长整型) /运算不能显示小数-> (整形除法只能保留整数位) ...
- Idea基于maven搭建多模块单体结构项目
菜鸟教程 一.创建一个公共的父工程: 1.1 创建一个新的maven项目: 1.2 先创建公共的父工程,选择maven骨架: 1.3 配置maven: 1.4 填写groupId.artifactId ...
- #学习笔记#jsp
jsp简介 JSP(Java Server Pages)是JavaWeb服务器端的动态资源,它与html页面的作用是相同的,显示数据和获取数据. jsp: 作为请求发起页面,例如显示表单.超链接. : ...
- windows openssh server 安装试用
使用Windows的可能会知道win10 的已经包好了openssh 服务,但是对于其他机器win 7 windows 2008 ,就需要其他的方法了 还好powershell 团队开发了支持wind ...
- live555 交叉编译移植到海思开发板
本文章参考了.http://blog.csdn.net/lawishere/article/details/8182952,写了hi3518的配置说明.特此感谢 https://blog.csdn.n ...
- Redis缓存相关问题总结
使用缓存是系统性能优化的第一黄金法则. 缓存的设计和使用对一个系统的性能至关重要,平时接触到项目无论多少也都会在某些层面用到缓存,比如用HashMap实现,Ehcache,memcached.redi ...
- oracle linux 6.8 安装
' 测试环境vm虚拟机 硬盘大小50G 内存2G CPU 4 选择install or upgrade an existing system 选择skip跳过内存检查 Next 选择语言,Next 选 ...
- c++ CreateProcess调用dos命令
// test.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <windows.h> #include &l ...
- VS2012 安装 NPOI (管理NuGet程序包)
问题背景 选择项目后右键==>管理NuGet程序包,搜索NPOI,返回服务器无法找到...404 解决方法: 第一步: 访问:https://www.nuget.org/api/v2/ ...
- 正点原子stm32f103mini版串口下载BOOT0引脚与与CH340G芯片引脚RTS、DTR、的关系原理
在做串口实验时,一直搞不明白一键下载是怎么回事,于是自己就去捉摸CH340G这块芯片,那么这里我将详细的讲解一下这块芯片怎么与stm32配合使用的. 1.由CH340G芯片资料可以知道这两个引脚的功能 ...