OpenCV-Python教程:46.摄像头标定

基础

今天的针孔摄像头对图像做了很多扭曲,两个主要的扭曲是径向畸变和切向畸变。

由于径向畸变,直线会显示成曲线,当直线离图像中心越远时越明显。比如下面显示的这张图,棋盘的两个用红色标出来的边缘,你可以看到棋盘不是直线,也不和红线匹配。所有的直线都凸了。

扭曲可以用下面的来解决:

类似的,另一个切向畸变是因为成像的光线不是完全平行的到达镜像平面。所以有些区域比期望的要看上去离的近。可以用下面的方式解决:

简单说,我们需要找到5个参数,叫做畸变参数:

除此之外,我们需要找到更多的信息,比如摄像头的内部和外部参数,内部参数是摄像头特定的参数。包括焦距(fx, fy)。光学中心(cx, cy)。也叫摄像机矩阵。它只依赖摄像头本身。一旦算出来就可以保存下来为以后使用,它应该是一个3x3的矩阵:

外部参数对应了旋转和平移向量来反应一个3维的点到2维的系统里。

对于立体的应用,这些扭曲需要首先被矫正。要找到所有的这些参数,我们得做的是提供一些有良好定义模式的样例图像(比如棋盘)。我们找到特定的点(棋盘的四个角),我们知道他们的真实世界的坐标,我们知道他们在图像里的坐标。通过这些数据,后台就能解决一些数学问题以得到畸变参数。

编码

上面提到的,我们需要10个测试模式来做摄像机矫正。重要的输入数据是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),...,我们可以用mm来表示结果。

3D的点被叫做物体点,而2D的图像点被叫做图像点。

设置

要找到棋盘的模式,我们用函数cv2.findChessboardCorners()。我们也需要传我们要找的模式的类型,比如8x8网格,5x5网格等,在这个例子里,我们使用7x6网格(一般来说棋盘都是8x8的方块7x7的内角),它返回角点。这些角点会按照从左到右,从上到下的顺序放好。

这个函数可能没法在所有图像里找到需要的模式,所以一个号的选择是写代码,启动摄像机,然后检查每帧,找需要的模式,当取得了模式,找到角点,并存在列表里。同时提供一些间隔,然后在读下面的帧的时候我们可以调整我们的棋盘的方向。不断进行这个过程知道需要的好的模式都获取到了。即使在这个例子里,我们也不知道多少是好的,所以我们读入所有的图像取里面好的。

除了棋盘,我们可以使用一些环形滤线。但是之后使用函数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, 所有像素都返回。

img=cv2.imread('left12.jpg')
h,w=img.shape[:2]
newcameramtx,roi=cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),1,(w,h))

1. 使用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.使用重测图

这是曲线救国,首先找到从扭曲图像到非扭曲图像的映射函数。然后使用重测函数。

# 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等),为以后使用

重投影差

重投影差给了找到的参数是否准确的一个好的估计。这个应该越接近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)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容