CV

Live Camera Calibration

"相机标定"

Posted by Stephen on April 2, 2022

前言

本库提供相机校准脚本,原理是通过使用摄像头在线抓取多个校准图案,然后求出相机内参。

环境

系统环境

Distributor ID:	Ubuntu
Description:	Ubuntu 18.04.4 LTS
Release:	18.04
Codename:	bionic
Linux version :       5.3.0-46-generic ( buildd@lcy01-amd64-013 ) 
Gcc version:         7.5.0  ( Ubuntu 7.5.0-3ubuntu1~18.04 )

软件信息

version : 	
     None

正文

理论原理

什么是相机内参?

相机内参既是相机模型参数,每个相机的内参都不一致,可以从出厂设置时候获取和自行标定计算出来。

通常认为相机的内参在出厂之后是固定的,不会在使用的过程中发生变化。

内参数 = 映射模型+畸变模型

映射模型:相机将三维世界中的坐标点(单位米)映射到二维图像平面(单位像素)的过程,针孔模型是其中之一

畸变模型:由于相机镜头上的透镜存在,使得光线投影到平面的过程。

这里只讨论针孔相机模型,不涉及外参

映射模型

Image text

引用**像素坐标系 **$o-u-v$,像素坐标系与成像平面之间,相差了一个缩放和一个原点平移。

根据三角形相似和坐标系转换(三维和像素)

得到:

$Z\left(\begin{array}{l} u
v
1 \end{array}\right)=\left(\begin{array}{ccc} f_{x} & 0 & c_{x}
0 & f_{y} & c_{y}
0 & 0 & 1 \end{array}\right)\left(\begin{array}{c} X
Y
Z \end{array}\right) \stackrel{\text { def }}{=} \boldsymbol{K} \boldsymbol{P}$

其中,$f_{x}, f_{y} $和$ c_{x}, c_{y}$的单位为像素。$f_x = \alpha f, f_y= \beta f$,$f$为焦距

P点和$P^{\prime}$的关系:设像素坐标在$u$轴缩放了$\alpha$倍,在$v$轴上缩放了$\beta$倍,同时原点平移了$\left[c_{x}, c_{y}\right]^{\mathrm{T}}$

$ K =\left(\begin{array}{ccc} f_{x} & 0 & c_{x}
0 & f_{y} & c_{y}
0 & 0 & 1 \end{array}\right)$,从该公式看,只与焦距有关。

矩阵K就是相机的内参数,通常认为相机的内参在出厂之后是固定的,不会在使用的过程中发生变化。

畸变模型

畸变分成两种:

  1. 径向畸变:由于透镜形状引起的,径(光线往中心走)

    桶形和枕形两种畸变中,穿过图像中心和光轴有交点的直线还能保持形状不变。

    1. 桶形畸变,图像放大率随着与光轴之间的距离增加而减少。
    2. 枕形畸变,图像放大率随着与光轴之间的距离减少而增加。
  2. 切向畸变:由于组装过程中不能是透镜和成像面严格平行引发的。

    两种畸变都有解决方案:去畸变处理(Undistort)畸变校正处理。

总结:

相机矩阵:包括焦距(fx,fy),光学中心(Cx,Cy),完全取决于相机本身,是相机的固有属性,只需要计算一次,可用矩阵表示如下:[fx, 0, Cx; 0, fy, Cy; 0,0,1];

畸变系数:畸变数学模型的5个参数 D = (k1,k2, P1, P2, k3);

相机内参:相机矩阵和畸变系数统称为相机内参,在不考虑畸变的时候,相机矩阵也会被称为相机内参;

单目相机的成像过程:

  1. 世界坐标系下有一个固定的点P,世界坐标为$P_W$
  2. 由于相机在运动,相机的运动由$R,t$描述,P的相机坐标为$\tilde{\boldsymbol{P}}_{\mathrm{c}} = RP_W + t$。
  3. 这时的$\tilde{\boldsymbol{P}}{\mathrm{c}}$的分量为$X,Y,Z$,把它们投影到归一化平面$Z=1$,得到P的归一化坐标:$\boldsymbol{P}{\mathrm{c}}=[X / Z, Y / Z, 1]^{\mathrm{T}}$,注意,Z可能小于1,说明该点位于归一化平面后面,它可能不会在相机平面上成像,实践中要检查一次。
  4. 有畸变时,根据畸变参数计算$P_c$发生畸变后的坐标。
  5. P的归一化坐标经过内参后,对应到它的像素坐标:$\boldsymbol{P}{u v}=\boldsymbol{K} \boldsymbol{P}{\mathrm{c}}$

综上所述,一共有四种坐标: 世界坐标(三维世界),相机坐标(相机位置),归一化坐标(成像平面),像素坐标(图片)

最后用一幅图来总结从世界坐标系到像素坐标系(不考虑畸变)的转换关系:

Image text

代码理解

参考:OpenCV相机标定全过程

  1. findChessboardCorners() 棋盘格角点检测

    cv.findChessboardCorners(image, patternSize[, corners[, flags]]	) -> retval,corners
       
    参数:
    	image	输入的棋盘格图像(可以是8位单通道或三通道图像); 
    	patternSize	棋盘格内部的角点的行列数(注意:不是棋盘格的行列数,如棋盘格的行列数分别为4、8,而内部角点的行列数分别是3、7,因此这里应该指定为cv::Size(3, 7)); 
    	corners	检测到的棋盘格角点,类型为std::vectorcv::Point2f。 
    	flags	用于指定在检测棋盘格角点的过程中所应用的一种或多种过滤方法,可以使用下面的一种或多种,如果都是用则使用OR: 
    		cv::CALIB_CB_ADAPTIVE_THRESH:使用自适应阈值将图像转化成二值图像 
    		cv::CALIB_CB_NORMALIZE_IMAGE:归一化图像灰度系数(用直方图均衡化或者自适应阈值) 
    		cv::CALIB_CB_FILTER_QUADS:在轮廓提取阶段,使用附加条件排除错误的假设 
    		cv::CALIB_CV_FAST_CHECK:快速检测 
       
    

  2. cornerSubPix() 亚像素检测

    cv.cornerSubPix(image, corners, winSize, zeroZone, criteria	) -> corners
    参数:
    	image	源图像
    	corners	提供角点的初始坐标,返回更加精确的点
    	winSize	搜索窗口的一般尺寸,如果winSize=Size(5,5),则search windows为1111
    	zeroZone	死区的一般尺寸,用来避免自相关矩阵的奇点,(-1,-1)表示没有死区
    	criteria	控制迭代次数和精度 
    
  3. drawChessboardCorners() 棋盘格角点绘制

    cv.drawChessboardCorners(image, patternSize, corners, patternWasFound) -> 	image
    参数:
    	image	8-bit,三通道图像
    	patternSize	每一行每一列的角
    	corners	已经检测到的角
    	patternWasFound	findChessboardCorners的返回值 
    
  4. calibrateCamera() 求解摄像头的内在参数和外在参数

    #include <opencv2/calib3d.hpp>
       
    python:
    cv.calibrateCamera(	objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs[, rvecs[, tvecs[, flags[, criteria]]]] -> retval, cameraMatrix, distCoeffs, rvecs, tvecs
    参数:
    	objectPoints	世界坐标,用vector,输入x,y坐标,z坐标为0
    	imagePoints	图像坐标,vector
    	imageSize,图像的大小用于初始化标定摄像机的image的size
    	cameraMatrix,内参数矩阵
    	distCoeffs,畸变矩阵
    	rvecs,位移向量
    	tvecs,旋转向量
    	flags,可以组合: 
    		CV_CALIB_USE_INTRINSIC_GUESS:使用该参数时,将包含有效的fx,fy,cx,cy的估计值的内参矩阵cameraMatrix,作为初始值输入,然后函数对其做进一步优化。如果不使用这个参数,用图像的中心点初始化光轴点坐标(cx, cy),使用最小二乘估算出fx,fy(这种求法好像和张正友的论文不一样,不知道为何要这样处理)。注意,如果已知内部参数(内参矩阵和畸变系数),就不需要使用这个函数来估计外参,可以使用solvepnp()函数计算外参数矩阵。
       
    		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_USE_INTRINSIC_GUESS参数,就从提供的畸变系数矩阵中得到。否则,设置为0。
       
    		CV_CALIB_RATIONAL_MODEL(理想模型):启用畸变k4,k5,k6三个畸变参数。使标定函数使用有理模型,返回8个系数。如果没有设置,则只计算其它5个畸变参数。
       
    		CALIB_THIN_PRISM_MODEL (薄棱镜畸变模型):启用畸变系数S1、S2、S3和S4。使标定函数使用薄棱柱模型并返回12个系数。如果不设置标志,则函数计算并返回只有5个失真系数。
       
    		CALIB_FIX_S1_S2_S3_S4 :优化过程中不改变薄棱镜畸变系数S1、S2、S3、S4。如果cv_calib_use_intrinsic_guess设置,使用提供的畸变系数矩阵中的值。否则,设置为0。
       
    		CALIB_TILTED_MODEL (倾斜模型):启用畸变系数tauX and tauY。标定函数使用倾斜传感器模型并返回14个系数。如果不设置标志,则函数计算并返回只有5个失真系数。
       
    		CALIB_FIX_TAUX_TAUY :在优化过程中,倾斜传感器模型的系数不被改变。如果cv_calib_use_intrinsic_guess设置,从提供的畸变系数矩阵中得到。否则,设置为0。 
    

实操

步骤:

  1. 打印 pattern.png 并将其粘贴到实心板上
  2. 固定相机镜头变焦,校准值随镜头变焦变化而变化
  3. 录制视频,图案在相机前移动
    • 图案大部分时间应该是完全可见的
    • 尝试移动图案以覆盖相机视图的所有部分,注意角落
    • 视频长度应为 1 或 2 分钟
  4. 运行calibration.py从视频中提取棋盘图案角并执行相机校准
$ mkdir out
$ calibrate.py example_input/chessboard.avi calibration.yaml --debug-dir out

有时候无法检测到棋盘格角点,导致无法计算摄像头的内参数

使用camera-calibration.py 得到某摄像头的内参:

rms, camera_matrix, dist_coefs, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, (w, h), None, None)
    return {'RMS': rms, 'cam_matrix': camera_matrix,
            'distortion coef': dist_coefs}
RMS: 0.7904033508165567
cam_matrix: [[339.04918574367036, 0.0, 324.4578306708091], [0.0, 354.55587891010885, 249.2879173320891], [0.0, 0.0, 1.0]]
distortion coef: [[-0.2637199595877457, 0.04944373758433949, 0.0021764838280600063, -0.0007236132861065995, 0.0029699959816359145]]

由相机矩阵和畸变系数得

cameraMatrix = [ [fx, 0, Cx]; [0, fy,Cy]; [0,0,1]]

DistCoeffs = [K1, K2, P1, P2, K3]

得到

# Camera calibration and distortion parameters (OpenCV) , 一般切成6个小数点
Camera.fx: 339.049185
Camera.fy: 354.555878
Camera.cx: 324.457830
Camera.cy: 249.287917

Camera.k1: -0.263719
Camera.k2: 0.049443
Camera.p1: 0.002176
Camera.p2: -0.000723
Camera.k3: 0.002969

参考:

video2calibration