第2关:相机标定

任务描述

本关任务:理解张正友标定算法原理,并实现张正友棋盘相机标定图像。

相关知识

为了完成本关任务,你需要掌握:

  1. 理解张正友标定算法原理;
  2. 实现张正友棋盘相机标定图像。

张正友标定

”张正友标定”是指张正友教授 1998 年提出的单平面棋盘格的摄像机标定方法。提出的方法介于传统标定法和自标定法之间,但克服了传统标定法需要的高精度标定物的缺点,而仅需使用一个打印出来的棋盘格就可以。同时也相对于自标定而言,提高了精度,便于操作。因此张氏标定法被广泛应用于计算机视觉方面。

回顾一下摄像机模型中的几个坐标系:

  • 世界坐标系 :也叫参考坐标系/基准坐标系,用于描述摄像机和物体的位置;
  • 摄像机坐标系 :固定在摄像机上,原点在光心,Zc 轴沿光轴方向, Xc/Yc 轴分别平行于成像平面;
  • 以物理单位表示的图像坐标系 (x, y): 原点在摄像机光轴与图像平面的交点, x/y 轴与摄像机 Xc/Yc 轴平行,沿图像平面方向;
  • 以像素为单位表示的图像坐标系 (u, v): 原点在数字图像的左上角,u/v 轴沿图像平面向右向下为正方向。

张正友平面标定法包含了以下前提:

  • 标定物是平面靶标;
  • 将世界坐标系置于靶标平面,原点设在靶标一角,Xw/Yw 方向沿靶标平面,Zw 方向垂直于靶标平面;
  • 先不考虑畸变,标定摄像机参数,得到参数的线性初值;然后利用线性初值,进行非线性标定,得到畸变参数。

计算内参和外参的初值

根据之前介绍的摄像机模型,设三维世界坐标的点为X=[X,Y,Z,1]T,二维相机平面像素坐标为m=[u,v,1]T,所以标定用的棋盘格平面到图像平面的单应性关系为:

s_{0}m=K[R,T]X

其中 s 为尺度因子,K 为摄像机内参数,R 为旋转矩阵,T 为平移向量。

K=\begin{bmatrix} \alpha & \gamma & u_{0}\\ 0 & \beta & v_{0}\\ 0 & 0 & 1 \end{bmatrix}

注意,s 对于齐次坐标来说,不会改变齐次坐标值。张氏标定法中,令棋盘格平面为 Z=0 的平面。则可得

s\begin{bmatrix} u\\ v\\ 1 \end{bmatrix}=K\begin{bmatrix} r_{1} & r_{2} & r_{3} & t \end{bmatrix}\begin{bmatrix} X\\ Y\\ 0\\ 1 \end{bmatrix}=K\begin{bmatrix} r_{1} & r_{2} & t \end{bmatrix}\begin{bmatrix} X\\ Y\\ 1 \end{bmatrix}

我们把 K[r1,r2,t] 叫做单应性矩阵 H,即

s\begin{bmatrix} u\\ v\\ 1 \end{bmatrix}=H\begin{bmatrix} X\\ Y\\ 1 \end{bmatrix}

H=\begin{bmatrix} h_{1} & h_{2} & h_{3} \end{bmatrix}=\lambda K\begin{bmatrix} r_{1} & r_{2} & t \end{bmatrix}

H 是一个齐次矩阵,所以有 8 个未知数,至少需要 8 个方程,每对对应点能提供两个方程,所以至少需要四个对应点,就可以算出世界平面到图像平面的单应性矩阵 H。

计算内、外参数矩阵及畸变矫正

每个单应性矩阵能提供两个方程,而内参数矩阵包含 5 个参数,要求解,至少需要 3 个单应性矩阵。为了得到三个不同的单应性矩阵,我们使用至少三幅棋盘格平面的图片进行标定。通过改变相机与标定板之间的相对位置来得到三个不同的图片。

我们至少需要三幅包含棋盘格的图像,然后通过 cholesky 分解,得到相机的内参数矩阵K。

外参矩阵可以通过内参矩阵 K 及单应性矩阵 H 计算求得。张氏标定法只关注了影响最大的径向畸变,基本思想是使用内参矩阵 K、二维相机平面像素坐标和三维世界坐标推导出前两阶的畸变参数 k1 和 k2 ,使用矩阵变换计算得到畸变系数 k。更多的理论推导请参考这里

OpenCV 实现棋盘相机标定图像

使用 OpenCV 标定的流程是:角点提取 -> 相机标定。角点提取在任务 1 中已经描述。

利用获取到的图像角点,获取到棋盘标定图的内角点图像坐标之后,就可以使用calibrateCamera()函数进行标定,计算相机内参和外参系数,其calibrateCamera()函数原型如下:

  1. ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objectPoints,
  2. imagePoints, imageSize, cameraMatrix, distCoeffs[, rvecs[, tvecs[,
  3. flags[, criteria]]]])

它的输入参数为:

  1. objectPoints: 为世界坐标系中的三维点。需要依据棋盘上单个黑白矩阵的大小,计算出(初始化)每一个内角点的世界坐标;
  2. imagePoints: 为每一个内角点对应的图像坐标点;
  3. imageSize: 为图像的像素尺寸大小,在计算相机的内参和畸变矩阵时需要使用到该参数;
  4. cameraMatrix: 为相机的内参矩阵;
  5. distCoeffs: 为畸变矩阵;
  6. rvecs: 为旋转向量;
  7. tvecs: 为位移向量;
  8. flags: 为标定时所采用的算法。有如下几个参数:
    • CV_CALIB_USE_INTRINSIC_GUESS:使用该参数时,在cameraMatrix矩阵中应该有fx,fy,u0,v0的估计值。否则的话,将初始化(u0,v0)图像的中心点,使用最小二乘估算出fx,fy。 
    • CV_CALIB_FIX_PRINCIPAL_POINT:在进行优化时会固定光轴点。当CV_CALIB_USE_INTRINSIC_GUESS参数被设置,光轴点将保持在中心或者某个输入的值。
    • CV_CALIB_FIX_ASPECT_RATIO:固定fx/fy的比值,只将fy作为可变量,进行优化计算。当CV_CALIB_USE_INTRINSIC_GUESS没有被设置,fx 和 fy 将会被忽略。只有fx/fy的比值在计算中会被用到。
    • CV_CALIB_ZERO_TANGENT_DIST:设定切向畸变参数(p1,p2)为零。
    • CV_CALIB_FIX_K1,…,CV_CALIB_FIX_K6:对应的径向畸变在优化中保持不变。
    • CV_CALIB_RATIONAL_MODEL:计算k4,k5,k6三个畸变参数。如果没有设置,则只计算其它 5 个畸变参数。
  9. criteria: 是最优迭代终止条件设定。

输出参数有 5 个,分别为:

  • ret:校正成功与否,布尔类型变量;
  • mtx:相机内参;
  • dist:畸变系数;
  • revcs:旋转矩阵;
  • tvecs:平移矩阵。

在本实训中,该函数常用参数为前三个,其它的参数指定为None。在使用该函数进行标定运算之前,需要对棋盘上每一个内角点的空间坐标系的位置坐标进行初始化,标定的结果是生成相机的内参矩阵cameraMatrix、相机的 5 个畸变系数distCoeffs,另外每张图像都会生成属于自己的平移向量和旋转向量。

到此我们已经完成了标定的过程,得到了摄像头的各个参数,后面就可以用这些得到的参数来做摄像头的矫正了。利用求得的相机的内参和外参数据,可以对图像进行畸变的矫正,在 OpenCV 中使用undistort()函数实现,其函数原型如下:

  1. dst = undistort(src, cameraMatrix, distCoeffs[, dst[, newCameraMatrix]])

输出为目标图像dst,输入参数为:

  • src:代表畸变的原始图像;
  • cameraMatrix:为之前求得的相机的内参矩阵;
  • distCoeffs:为之前求得的相机畸变矩阵;
  • dst:矫正后的输出图像,跟输入图像具有相同的类型和大小;
  • newCameraMatrix:可以指定新的相机参数,默认跟cameraMatrix保持一致。

到此,就完成了摄像头的标定和图像的矫正的整个流程。

编程要求


图 1 给定的原始图像

根据提示,在右侧编辑器补充task2()函数的 Begin-End 区间代码,实现对给定图片(如图 1 所示)实现图像的畸变矫正。具体要求如下:

  1. 使用cv2.calibrateCamera()函数寻找角点,求出图片的相机内参mtx、畸变系数dist、旋转矩阵revcs和平移矩阵tvecs
  2. cv2.undistort()纠正图像畸变,newCameraMatrix参数设置为程序中优化后的相机内参矩阵,即newcameramtx。最后结果保存在dst变量。

测试说明

平台会对你补全的代码进行测试,如果实际输出与预期输出匹配则通关。

测试输入:无 ; 预期输出: distortion correction success

参考代码:

import cv2
import numpy as np

def calb():
    #criteria:角点精准化迭代过程的终止条件
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)

    objp = np.zeros((7*7,3), np.float32)
    '''
    设定世界坐标下点的坐标值,因为用的是棋盘可以直接按网格取;
    假定棋盘正好在x-y平面上,这样z值直接取0,简化初始化步骤。
    mgrid把列向量[0:cbraw]复制了cbcol列,把行向量[0:cbcol]复制了cbraw行。
    转置reshape后,每行都是4×6网格中的某个点的坐标。
    '''
    objp[:,:2] = np.mgrid[0:7,0:7].T.reshape(-1,2)

    objpoints = [] # 3d point in real world space
    imgpoints = [] # 2d points in image plane.

    fname = "/data/workspace/myshixun/task2/5test1.jpg"
    #识别出角点,记录世界物体坐标和图像坐标
    img = cv2.imread(fname) #source image
    cv2.imwrite('/data/workspace/myshixun/task2/out/5test1.png', img)

    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #转灰度
    #寻找角点,存入corners,ret是找到角点的flag
    ret, corners = cv2.findChessboardCorners(gray,(7,7),None)


    if ret == True:
         objpoints.append(objp)
         #执行亚像素级角点检测
         corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
         imgpoints.append(corners2)
         #在棋盘上绘制角点
         img = cv2.drawChessboardCorners(img,(7,7),corners2,ret)

    '''
    传入所有图片各自角点的三维、二维坐标,相机标定。
    每张图片都有自己的旋转和平移矩阵,但是相机内参和畸变系数只有一组。
    mtx,相机内参;dist,畸变系数;revcs,旋转矩阵;tvecs,平移矩阵。
    '''
    ########## Begin ##########
    ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1],None,None)

    ########## End ##########
    h,w = img.shape[:2]
    '''
    优化相机内参(camera matrix),这一步可选。
    参数1表示保留所有像素点,同时可能引入黑色像素,
    设为0表示尽可能裁剪不想要的像素,这是个scale,0-1都可以取。
    '''
    newcameramtx, roi=cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),1,(w,h))
    #纠正畸变
    ########## Begin ##########
    dst = cv2.undistort(img, mtx, dist, None, newcameramtx)
    ########## End ##########
    #这步只是输出纠正畸变以后的图片
    x,y,w,h = roi
    dst = dst[y:y+h, x:x+w]
    cv2.imwrite('/data/workspace/myshixun/task2/out/calibresult.png',dst)

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

畜牧当道

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值