原文地址http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_calib3d/py_calibration/py_calibration.html#calibration


目标

  1. 我们将了解导致相机失真、扭曲的内因与外因
  2. 我们将试着找到这些畸变参数,并消除畸变

基础

如今大量廉价的摄像机导致了很多照片畸变。两个主要的畸变是径向畸变和切向畸变。

由于径向畸变,直线会变弯。距离图片中心越远,它的影响越大。如下面这张图片,棋盘格中被红线标记的边缘。你会发现棋盘格的边缘并不与直红线重合,而是变弯了。可以到维基百科查看更多细节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的更多相关文章

  1. 张正友相机标定Opencv实现以及标定流程&&标定结果评价&&图像矫正流程解析(附标定程序和棋盘图)

    使用Opencv实现张正友法相机标定之前,有几个问题事先要确认一下,那就是相机为什么需要标定,标定需要的输入和输出分别是哪些? 相机标定的目的:获取摄像机的内参和外参矩阵(同时也会得到每一幅标定图像的 ...

  2. 【视频开发】【计算机视觉】相机标定(Camera calibration)原理、步骤

    相机标定(Camera calibration)原理.步骤 author@jason_ql(lql0716)  http://blog.csdn.net/lql0716 在图像测量过程以及机器视觉应用 ...

  3. SLAM入门之视觉里程计(6):相机标定 张正友经典标定法详解

    想要从二维图像中获取到场景的三维信息,相机的内参数是必须的,在SLAM中,相机通常是提前标定好的.张正友于1998年在论文:"A Flexible New Technique fro Cam ...

  4. 相机标定:PNP基于单应面解决多点透视问题

              利用二维视野内的图像,求出三维图像在场景中的位姿,这是一个三维透视投影的反向求解问题.常用方法是PNP方法,需要已知三维点集的原始模型. 本文做了大量修改,如有不适,请移步原文:  ...

  5. 相机标定 matlab opencv ROS三种方法标定步骤(3)

    三 ,  ROS 环境下 如何进行相机标定 刚开始做到的时候遇到一些问题没有记录下来,现在回头写的时候都是没有错误的结果了,首先使用ROS标定相机, 要知道如何查看节点之间的流程图  rosrun r ...

  6. 相机标定 matlab opencv ROS三种方法标定步骤(1)

    一 . 理解摄像机模型,网上有很多讲解的十分详细,在这里我只是记录我的整合出来的资料和我的部分理解 计算机视觉领域中常见的三个坐标系:图像坐标系,相机坐标系,世界坐标系,实际上就是要用矩阵来表 示各个 ...

  7. 使用OpenCV进行相机标定

    1. 使用OpenCV进行标定 相机已经有很长一段历史了.但是,伴随着20世纪后期的廉价针孔照相机的问世,它们已经变成我们日常生活的一种常见的存在.不幸的是,这种廉价是由代价的:显著的变形.幸运的是, ...

  8. opencv 角点检测+相机标定+去畸变+重投影误差计算

    https://blog.csdn.net/u010128736/article/details/52875137 https://blog.csdn.net/h532600610/article/d ...

  9. OpenCV相机标定和姿态更新

    原帖地址: http://blog.csdn.net/aptx704610875/article/details/48914043 http://blog.csdn.net/aptx704610875 ...

  10. 相机标定 matlab opencv ROS三种方法标定步骤(2)

    二  ubuntu下Opencv的相机标定 一般直接用Opencv的源码就可以进行相机的标定,但是可能只是会实现结果,却不懂实现的过程,我也是模模糊糊的看了<计算机视觉中的多视图几何>以及 ...

随机推荐

  1. Android版本28使用http请求报错not permitted by network security policy

    Android版本28使用http请求报错not permitted by network security policy android模拟器调试登录的时候报错 CLEARTEXT communic ...

  2. LiquiBase 学习

    preconditions mysql database is installed maven has been setted up properly add depedenceies apply p ...

  3. 关于if...else语句的小注意

    if...else是一个使用非常频繁的条件语句,在条件满足时执行if下的代码,条件不满足时执行else下的代码.但在使用过程中会由于粗心犯一些错误. 比如我想要把性别的默认值设置为“男”,应该进行如下 ...

  4. Makefile中的ifeq 多条件使用

    Makefile中的ifeq 多条件使用 网上关于makefile中ifeq的介绍已经很多了,为什么我还要在写这篇文章,因为他们只说了if else两种条件的情况,并没有讲多于两种条件情况的使用. 多 ...

  5. Linux系统安装管理

    将lfs linux liveCD的内容copy安装到硬盘 先将98.ima(dos启动软盘镜像文件)用ultraISO写入到u盘(usbhdd+), 不必勾选“创建启动分区”. 将liveCD和内核 ...

  6. 程序猿大牛 Jeff Atwood 的两本中文书

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/happydeer/article/details/17094271 watermark/2/text ...

  7. debian系统下apache2开启ssi功能

    SSI (Server Side Include)的 html 文件扩展名 (.shtml), 通常称为"服务器端嵌入"或者叫"服务器端包含"说白了就是类似其他 ...

  8. div遮盖,弹出层

    <html>     <head>     <title>LIGHTBOX EXAMPLE</title>     <meta charset=& ...

  9. FPGA 主时钟约束---primary clocks

    FPGA 主时钟约束---primary clocks 个人的理解,FPGA做时钟约束的主要目的是给布局布线过程一个指导意义. 注:周期的参数值为ns waveform 里面的第一个参数为波形第一个上 ...

  10. 彻底删除msde2008(请先在控制面板中卸载).bat

    彻底删除msde2008(请先在控制面板中卸载).bat @echo offset /P dv=请确认强制删除MSDE2008(请先在控制面板中卸载),Y=删除,N=退出:if not defined ...