【Python计算机视觉】相机标定(Camera Calibration)

目录

  • 一、相机投影
    • 1.1 针孔相机模型
    • 1.2 原理
      • 1.2.1 坐标系标定
      • 1.2.2 相机畸变现象影响
      • 1.2.3 相机旋转平移影响
      • 1.2.4 两类重要参数
  • 二、相机标定
    • 2.1 线性回归(最小二乘)
    • 2.2 非线性优化
    • 2.3 张有正标定
    • 2.4 相机标定实现
      • 2.4.1 算法描述
      • 2.4.2 代码
      • 2.4.3 结果及分析
  • 参阅

一、相机投影

1.1 针孔相机模型

1.用一个带有小孔的板遮挡在屏幕与物之间,屏幕上就会形成物的倒像,我们把这样的现象叫小孔成像。前后移动中间的板,屏幕上像的大小也会随之发生变化,这种现象说明了光沿直线传播的性质。
【Python计算机视觉】相机标定(Camera Calibration)_第1张图片

2.相机将三维世界中的坐标点(单位:米)映射到二维图像平面(单位:像素)的过程能够用一个几何模型来描述,其中最简单的称为针孔相机模型 (pinhole camera model),其框架如下图所示。
【Python计算机视觉】相机标定(Camera Calibration)_第2张图片
很显然,坐标的转换便是一个降维的过程。为了确定这种三维到二维的映射关系,我们进行如下对原理的讨论。

1.2 原理

1.2.1 坐标系标定

1.世界坐标系(world coordinate)(xw,yw,zw) ,也称为测量坐标系,是一个三维直角坐标系,以其为基准可以描述相机和待测物体的空间位置。世界坐标系的位置可以根据实际情况自由确定。
2.相机坐标系(camera coordinate)(xc,yc,zc),也是一个三维直角坐标系,原点位于镜头光心处,x、y轴分别与相面的两边平行,z轴为镜头光轴,与像平面垂直。
3.像素坐标系 uov是一个二维直角坐标系,反映了相机中像素的排列情况。原点o位于图像的左上角,u轴、v轴分别于像面的两边平行。像素坐标系中坐标轴的单位是像素(整数)。
4.图像坐标系 XOY,其坐标轴的单位通常为毫米(mm),原点是相机光轴与相面的交点(称为主点),即图像的中心点,X轴、Y轴分别与u轴、v轴平行。故两个坐标系实际是平移关系,即可以通过平移就可得到。
5.世界坐标系到相机坐标系的标定
第一步是从世界坐标系到相机坐标系的变换,这一步是三维点到三维点的变换,属于刚体变换:即物体不会发生形变,只需要进行旋转和平移。
R:表示旋转矩阵(相机外参)
T:表示偏移向量(相机外参)
【Python计算机视觉】相机标定(Camera Calibration)_第3张图片

6.相机坐标系到图像坐标系的标定
第二步是相机坐标系转为图像坐标系,这一步是三维点到二维点的变换,即从3D转换到2D.属于透视投影关系。
【Python计算机视觉】相机标定(Camera Calibration)_第4张图片
得到矩阵公式:
【Python计算机视觉】相机标定(Camera Calibration)_第5张图片
【Python计算机视觉】相机标定(Camera Calibration)_第6张图片

由于此时投影点p的单位还是mm,并不是像素pixel,需要进一步转换到像素坐标系。

7.图像坐标系到像素坐标系的标定
像素坐标系和图像坐标系都在成像平面上,只是各自的原点和度量单位不一样。图像坐标系的原点为相机光轴与成像平面的交点,通常情况下是成像平面的中点或者叫principal point。图像坐标系的单位为mm,属于物理单位,而像素坐标系的单位是pixel,我们平常描述一个像素点都是几行几列。所以这两者之间的转换如下:
其中dx和dy表示每一列和每一行分别代表多少mm,即1pixel = dx mm
【Python计算机视觉】相机标定(Camera Calibration)_第7张图片
于是由以上四个坐标系的转换得到如下式子:
【Python计算机视觉】相机标定(Camera Calibration)_第8张图片
右式结果的第一个矩阵为相机内参,第二个矩阵为相机外参,可由张有正标定法获取(戳这里查看张有正标定有关信息)

1.2.2 相机畸变现象影响

由于光学透镜固有的透视失真导致相机镜头畸变的现象我们统称为相机畸变。此处我们着重讨论图像径向畸变:
产生原因:

  • 透镜质量原因
  • 光线在远离透镜中心的地方比靠近中心的地方更加弯曲

畸变的本质是光学系统在不同视场由于镜头屈光度与光阑位置不同引起像高放大率不同导致的。
【Python计算机视觉】相机标定(Camera Calibration)_第9张图片
分类:
1.桶状畸变:又称“桶形失真”,视野中光轴中心区域的放大率远大于边缘区域的放大率,常出现在广角镜头和鱼眼镜头中。
2.枕形畸变:又称“鞍形形变”,视野中边缘区域的放大率远大于光轴中心区域的放大率,常用在远摄镜头中。
【Python计算机视觉】相机标定(Camera Calibration)_第10张图片
矫正:
【Python计算机视觉】相机标定(Camera Calibration)_第11张图片
调节公式为:
【Python计算机视觉】相机标定(Camera Calibration)_第12张图片

  • 公式里(x0,y0)是畸变点在成像仪上的原始位置,(x,y)是校正后新的位置。
  • 实现过程是,对输出图的点做遍历——以1080p的图像为例,从点(0,0)到点(1919,1079),一行一行的遍历——依次找到输出点(x,y)对应的原图点(x0, y0)的像素值,再将(x0, y0)的值赋给(x, y)。如果计算出来的对应的原图的点(x0,y0)不是整数,则用二次线性插值计算此点,然后赋值给(x, y)。

1.2.3 相机旋转平移影响

由于日常生活中在进行拍摄时,相机的位置是不断变化的,所以会有相机旋转与平移对拍摄的影响。所以将上面坐标转换得到的x ~ K[I|0]X矩阵进行修改,得到x ~ K[R|t]X矩阵(外部参数矩阵):
【Python计算机视觉】相机标定(Camera Calibration)_第13张图片
1.相机旋转包括三个方向:X,Y,Z,(即pitch,yaw,roll)
【Python计算机视觉】相机标定(Camera Calibration)_第14张图片
2.将三个方向上的旋转相乘,得到旋转矩阵R:
【Python计算机视觉】相机标定(Camera Calibration)_第15张图片

1.2.4 两类重要参数

【Python计算机视觉】相机标定(Camera Calibration)_第16张图片

二、相机标定

同步标定内部参数和外部参数,一般包括两种策略s:

  1. 光学标定:利用已知的几何信息(如定长棋盘格)实现参数求解。
  2. 自标定:在静态场景中利用structure from motion估算参数。

2.1 线性回归(最小二乘)

线性回归(最小二乘)单单考虑坐标的转换:
【Python计算机视觉】相机标定(Camera Calibration)_第17张图片
通过空间中已知的坐标的(特征)点(Xi,Yi,Zi),以及它们在图像中的对应坐标(ui,vi),直接估算11个待求解的内部和外部参数。
【Python计算机视觉】相机标定(Camera Calibration)_第18张图片
表示为矩阵形式:
【Python计算机视觉】相机标定(Camera Calibration)_第19张图片
即可用最小二乘法求解。
(若要回顾单硬性变换,请点击这里)

给定超定方程:Ax = 0 后,x的解即为A^TA最小特征值对应的特征向量。

标定参数线性回归的优缺点
优点:

  • 所有相机参数集中在一个矩阵中,便于求解
  • 所有矩阵可以直接描述世界坐标中的三维点,到二维图像平面中点的映射关系。

缺点:

  • 无法直接得知具体的内参数和外参数
  • 求解出的11个未知量,比待标定参数(9个)更多。带来了参数不独立/相关问题
  • 对噪声/误差敏感。

2.2 非线性优化

用概率的角度去看最小二乘问题。

  • 特征点的投影方程为:
    【Python计算机视觉】相机标定(Camera Calibration)_第20张图片
  • 给定{(ui,vi)},标定参数矩阵M的概率为:
    【Python计算机视觉】相机标定(Camera Calibration)_第21张图片
  • 给定{(ui,vi)},标定参数矩阵M的似然函数为:
    【Python计算机视觉】相机标定(Camera Calibration)_第22张图片
  • 相应求解策略:牛顿方法、高斯-牛顿方法、Levenberg-Marquardt算法等。

2.3 张有正标定

(1)计算单应性矩阵H

设三维世界坐标的点为X=[X,Y,Z,1]^T
二维相机平面像素坐标为m=[u,v,1]^T
所以标定用的棋盘格平面得到图像平面的单应性关系为:在这里插入图片描述
其中,s为尺度因子,K为摄像机内参数,R是旋转矩阵,t为平移向量,令:
【Python计算机视觉】相机标定(Camera Calibration)_第23张图片
s做为齐次坐标来说,不会改变齐次坐标值。将世界坐标系构造在棋盘格平面上,令棋盘格平面为Z=0的平面。则:【Python计算机视觉】相机标定(Camera Calibration)_第24张图片
将K[r1,r2,t]叫做单应性矩阵H,即:
【Python计算机视觉】相机标定(Camera Calibration)_第25张图片
H是一个齐次矩阵,所以有八个未知数,至少需要八个方程,每对对应点能提供两个方程,所以至少需要四个对应点,就可以算出世界平面到图像平面的单应性矩阵H。

(2)计算内参数矩阵
【Python计算机视觉】相机标定(Camera Calibration)_第26张图片
【Python计算机视觉】相机标定(Camera Calibration)_第27张图片
可以看出,B是一个对称阵,所以B的有效元素为6个,让这6个元素写成向量b,即:
在这里插入图片描述
【Python计算机视觉】相机标定(Camera Calibration)_第28张图片
利用约束条件可以得到:
【Python计算机视觉】相机标定(Camera Calibration)_第29张图片
通过上式,我们至少需要三幅包含棋盘格的图像,可以计算得到B,然后通过cholesky分解,得到相机的内参数矩阵K。
(3)计算外参数矩阵
由之前的推导可得:
【Python计算机视觉】相机标定(Camera Calibration)_第30张图片
(4)最大似然估计
由于可能存在高斯噪声,所以使用最大似然估计进行优化。这里使用的是多参数非线性系统优化问题的Levenberg-Marquardt算法进行迭代优化求最优解。
【Python计算机视觉】相机标定(Camera Calibration)_第31张图片
(5)径向畸变估计
数学表达式:
【Python计算机视觉】相机标定(Camera Calibration)_第32张图片
化作矩阵形式:
在这里插入图片描述
得到:
【Python计算机视觉】相机标定(Camera Calibration)_第33张图片
由此计算得到畸变系数k
综上,我们得到了相机的内参、外参和畸变系数。

2.4 相机标定实现

2.4.1 算法描述

  1. 打印一张棋盘格A4纸张(黑白间距已知),并贴在一个平板上
  2. 针对棋盘格拍摄若干张图片(一般10-20张)
  3. 在图片中检测特征点(Harris角点)
  4. 根据角点位置信息及图像中的坐标,求解Homographic矩阵
  5. 利用解析解估算方法计算出5个内部参数,以及6个外部参数
  6. 根据极大似然估计策略,设计优化目标并实现参数的refinement

2.4.2 代码

import cv2
import numpy as np
import glob

# 找棋盘格角点
# 阈值
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
#棋盘格模板规格
w = 14   #内角点个数,内角点是和其他格子连着的点
h = 9

# 世界坐标系中的棋盘格点,例如(0,0,0), (1,0,0), (2,0,0) ....,(8,5,0),去掉Z坐标,记为二维矩阵
objp = np.zeros((w*h,3), np.float32)
objp[:,:2] = np.mgrid[0:w,0:h].T.reshape(-1,2)
# 储存棋盘格角点的世界坐标和图像坐标对
objpoints = [] # 在世界坐标系中的三维点
imgpoints = [] # 在图像平面的二维点

images = glob.glob('picture/*.jpg')
for fname in images:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    # 找到棋盘格角点
    # 棋盘图像(8位灰度或彩色图像)  棋盘尺寸  存放角点的位置
    ret, corners = cv2.findChessboardCorners(gray, (w,h),None)
    # 如果找到足够点对,将其存储起来
    if ret == True:
        # 角点精确检测
        # 输入图像 角点初始坐标 搜索窗口为2*winsize+1 死区 求角点的迭代终止条件
        cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
        objpoints.append(objp)
        imgpoints.append(corners)
        # 将角点在图像上显示
        cv2.drawChessboardCorners(img, (w,h), corners, ret)
        cv2.imshow('findCorners',img)
        cv2.waitKey(1000)
cv2.destroyAllWindows()
#标定、去畸变
# 输入:世界坐标系里的位置 像素坐标 图像的像素尺寸大小 3*3矩阵,相机内参数矩阵 畸变矩阵
# 输出:标定结果 相机的内参数矩阵 畸变系数 旋转矩阵 平移向量
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
# mtx:内参数矩阵
# dist:畸变系数
# rvecs:旋转向量 (外参数)
# tvecs :平移向量 (外参数)
print (("ret:"),ret)
print (("mtx:\n"),mtx)        # 内参数矩阵
print (("dist:\n"),dist)      # 畸变系数   distortion cofficients = (k_1,k_2,p_1,p_2,k_3)
print (("rvecs:\n"),rvecs)    # 旋转向量  # 外参数
print (("tvecs:\n"),tvecs)    # 平移向量  # 外参数
# 去畸变
img2 = cv2.imread('picture/5_d.jpg')
h,w = img2.shape[:2]
# 我们已经得到了相机内参和畸变系数,在将图像去畸变之前,
# 我们还可以使用cv.getOptimalNewCameraMatrix()优化内参数和畸变系数,
# 通过设定自由自由比例因子alpha。当alpha设为0的时候,
# 将会返回一个剪裁过的将去畸变后不想要的像素去掉的内参数和畸变系数;
# 当alpha设为1的时候,将会返回一个包含额外黑色像素点的内参数和畸变系数,并返回一个ROI用于将其剪裁掉
newcameramtx, roi=cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),0,(w,h)) # 自由比例参数

dst = cv2.undistort(img2, mtx, dist, None, newcameramtx)
# 根据前面ROI区域裁剪图片
x,y,w,h = roi
dst = dst[y:y+h, x:x+w]
cv2.imwrite('calibresult.jpg',dst)

# 反投影误差
# 通过反投影误差,我们可以来评估结果的好坏。越接近0,说明结果越理想。
# 通过之前计算的内参数矩阵、畸变系数、旋转矩阵和平移向量,使用cv2.projectPoints()计算三维点到二维图像的投影,
# 然后计算反投影得到的点与图像上检测到的点的误差,最后计算一个对于所有标定图像的平均误差,这个值就是反投影误差。
total_error = 0
for i in range(len(objpoints)):
    imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
    error = cv2.norm(imgpoints[i],imgpoints2, cv2.NORM_L2)/len(imgpoints2)
    total_error += error
print (("total error: "), total_error/len(objpoints))


2.4.3 结果及分析

(1)输入数据集:
输入数据集是一组来自严格平面的棋盘格,用自己的手机拍摄了14张,故计算手机相机的内外参数:【Python计算机视觉】相机标定(Camera Calibration)_第34张图片
(2)输出角点检测结果:
【Python计算机视觉】相机标定(Camera Calibration)_第35张图片
OpenCV中自带了提取棋盘格中内角点的函数:findChessboardCorners()。这个函数的功能是确定输入图像中是否有棋盘格图案,并检测棋盘格的内角点。如果所有的内角点都找到了,那么函数返回一个非0值;如果没有找到所有的内角点,就会返回0。
因为我们使用的是有14×9的内格子,所以我们在设置w和h时分别填写14和9。函数返回每一一个角点,如果匹配到了模式,它将返回是True。这些角点将按一定顺序标注出来(从左到右,从上到下)。

(3)相机参数:

  • 内参数矩阵mtx:
    【Python计算机视觉】相机标定(Camera Calibration)_第36张图片

  • 畸变系数dist:

    在这里插入图片描述

  • 旋转向量rvecs:

    rvecs:  [array([[-0.15864377],
           [ 0.06980558],
           [ 0.16090932]]), array([[-0.26484807],
           [-0.17818488],
           [ 0.03831059]]), array([[-0.24593887],
           [ 0.33735992],
           [-0.00986063]]), array([[-0.00906031],
           [ 0.01517562],
           [ 0.06098918]]), array([[-0.28737887],
           [ 0.03681998],
           [ 0.06031694]]), array([[0.05507092],
           [0.02459088],
           [0.01313248]]), array([[-0.29605163],
           [ 0.00182411],
           [ 0.06790322]]), array([[-0.09594883],
           [ 0.03165031],
           [ 0.12594066]]), array([[-0.09307569],
           [ 0.0338839 ],
           [-0.04746944]]), array([[-0.07165137],
           [ 0.39360228],
           [ 0.00174199]]), array([[-0.1892262 ],
           [-0.31510907],
           [-0.10536917]]), array([[-0.59337799],
           [ 0.23014183],
           [ 0.07634885]]), array([[ 0.12684867],
           [-0.00313582],
           [ 0.04928179]]), array([[-0.22086014],
           [ 0.14914275],
           [ 0.04814694]])] ```
    
    
    
  • 平移向量tvecs:

    tvecs:  [array([[-4.59692988],
           [-4.56254851],
           [17.32213156]]), array([[-4.11514485],
           [-4.60415106],
           [15.43947814]]), array([[-7.56009876],
           [-3.55997914],
           [17.8127442 ]]), array([[-6.03322459],
           [-3.22313036],
           [16.17444347]]), array([[-4.73783498],
           [-4.04532552],
           [17.60559336]]), array([[-5.970035  ],
           [-3.47727784],
           [14.78357089]]), array([[-5.75140128],
           [-3.89551428],
           [17.74619271]]), array([[-4.40524727],
           [-3.66910702],
           [19.77897714]]), array([[-5.21074683],
           [-3.19815475],
           [16.82278397]]), array([[-6.55567674],
           [-3.42179644],
           [18.35279753]]), array([[-4.43566524],
           [-3.60398259],
           [15.54220295]]), array([[-5.6594958 ],
           [-3.52781208],
           [19.56802292]]), array([[-5.33115856],
           [-2.88742627],
           [15.62377258]]), array([[-4.96680286],
           [-4.09675094],
           [17.95433939]])] ```
    

(4)注意:

  • 图像内角点需要确定清楚之后修改相应的参数值;
  • 图片以10-20张适宜,数量太少则容易导致标定参数不准确。

参阅

畸变图像校正
坐标系转换

你可能感兴趣的:(实验,计算机视觉,python,计算机视觉,opencv,图像处理,机器学习)