相机标定

相机模型

坐标系

  • 世界坐标系:物体在真实世界的三维坐标:(Xw,Yw,Zw)(X_w,Y_w,Z_w)

  • 相机坐标系:以相机光学中心为原点的坐标系,光轴与z轴重合:(Xc,Yc,Zc)(X_c,Y_c,Z_c)

  • 图像坐标系:相机拍摄图像的坐标系,原点为相机光轴与成像平面的交点,图像的中心点:(x,y)(x,y)

  • 像素坐标系:由于图像组成是像素,存储位置的原点在左上角:(u,v)(u,v)

  • 前三个坐标系的单位是毫米,最后一个是像素

坐标系转换

世界坐标系-相机坐标系

  • [XcYcZc1]=[Rt01][XwYwZw1]\begin{bmatrix}X_c\\Y_c\\Z_c\\1\end{bmatrix}=\begin{bmatrix}R&t\\0&1\end{bmatrix}\begin{bmatrix}X_w\\Y_w\\Z_w\\1\end{bmatrix}

相机坐标系-图像坐标系

  • {x=fZcXcy=fZcYc\begin{cases}x=\frac{f}{Z_c}X_c\\y=\frac{f}{Z_c}Y_c\end{cases}
  • [xy1]=[fZc0000fZc00001Zc0][XcYcZc1]\begin{bmatrix}x\\y\\1\end{bmatrix}=\begin{bmatrix}\frac{f}{Z_c}&0&0&0\\0&\frac{f}{Z_c}&0&0\\0&0&\frac{1}{Z_c}&0\end{bmatrix}\begin{bmatrix}X_c\\Y_c\\Z_c\\1\end{bmatrix}

图像坐标系-像素坐标系

  • {u=cx+xfxv=cy+yfy\begin{cases}u=c_x+x\cdot f_x\\v=c_y+y\cdot f_y\end{cases}
  • [uv1]=[fx0cx0fycy001][xy1]\begin{bmatrix}u\\v\\1\end{bmatrix}=\begin{bmatrix}f_x&0&c_x\\0&f_y&c_y\\0&0&1\end{bmatrix}\begin{bmatrix}x\\y\\1\end{bmatrix}

畸变模型

  • 相机的畸变一般只考虑径向畸变和切向畸变
  • {xdistorted=x(1+k1r2+k2r4+k3r6)+2p1xy+p2(r2+2x2)ydistorted=y(1+k1r2+k2r4+k3r6)+2p2xy+p1(r2+2y2)\begin{cases}x_{distorted}=x(1+k_1r^2+k_2r^4+k_3r^6)+2p_1xy+p_2(r^2+2x^2)\\y_{distorted}=y(1+k_1r^2+k_2r^4+k_3r^6)+2p_2xy+p_1(r^2+2y^2)\end{cases}
    • 其中:r2=x2+y2r^2=x^2+y^2

  • k>0,枕形畸变
  • k<0,桶形畸变

相机标定

标定结果

  • 内参:fx,fy,cx,cy,k1,k2,k3,p1,p2f_x,f_y,c_x,c_y,k_1,k_2,k_3,p_1,p_2
  • 外参:R,tR,t

张氏标定法[1]

  • 将问题转化为三维投影二维后与图像通过最小二乘优化求解

  • 目标函数:i=1nj=1mmijm^(A,Ri,ti,Mj)\sum_{i=1}^{n}\sum_{j=1}^{m}\|m_{ij}-\hat{m}(A,R_i,t_i,M_j)\|

  • 但是优化量太多,对初值很敏感,容易陷入局部最优。张正友提出了一个获取初始值的方法

  • 假定目标点在z=0的平面上,坐标变换可以简化为:

  • s[uv1]=A[r1r2r3t][XYZ1]=A[r1r2t][XY1]s\begin{bmatrix}u\\v\\1\end{bmatrix}=A\begin{bmatrix}r_1&r_2&r_3&t\end{bmatrix}\begin{bmatrix}X\\Y\\Z\\1\end{bmatrix}=A\begin{bmatrix}r_1&r_2&t\end{bmatrix}\begin{bmatrix}X\\Y\\1\end{bmatrix}

  • 这里就要求标定板要做成平面,这时问题可以简化为利用两组图像想,利用单应矩阵的求解A,R,tA,R,t

  • [h1h2h3]=λA[r1r2t]\begin{bmatrix}h_1&h_2&h_3\end{bmatrix}=\lambda A\begin{bmatrix}r_1&r_2&t\end{bmatrix}

  • 最后优化目标方程,得到标定的最优解

改进

  • 张正友方法没有限制圆环和棋盘格的标靶,圆环与棋盘格的优缺点
  • 圆形的检测精度比棋盘格更高,但是存在偏心误差:投影后的椭圆中心不是圆心

  • 圆环检测优化[2]
  • 标定靶检测边缘对检测结果的影响[3]

标定操作

  • 拍摄机位和数量的选择
    • 尽量将标定板在图形范围内都出现
    • 不同的角度旋转

标定评价

  • 重投影误差(Re-projection error):目标函数的误差

  • 影响因素:

    • 检测精度
    • 噪声
    • 分辨率
    • 优化

应用

单目:PnP问题

cv::solvePnP

双目测量

代码

  • opencv的标定函数
cv::calibrateCamera(object_points,image_points,imageSize,cam_intr_para,distCoeffs,rvecs,tvecs);

标定

import numpy as np
import cv2,traceback
chess_corner_x = 10 #
chess_corner_y = 8
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER,30,0.001) # 定义:迭代最大次数,角点变化的最小值

obj_p = np.mgrid[:chess_corner_x,:chess_corner_y].T.reshape(-1,2)
z_p = np.zeros((chess_corner_x*chess_corner_y))
obj_p = np.c_[obj_p,z_p].astype(np.float32) # 保存每个格子的位置,z轴的值为0

obj_points = [] # 真实世界坐标系
img_points = [] # 图像坐标系

#obj_p = np.zeros((chess_corner_x*chess_corner_y,3))

def detect():
cap = cv2.VideoCapture(0)
try:
while 1:
ret, frame = cap.read()
if not ret:
break
gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
# 检测棋盘角点像素坐标
ret, corners = cv2.findChessboardCorners(gray,(chess_corner_x,chess_corner_y),None)
if ret:
# 寻找亚像素坐标,窗口大小5*5
corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
obj_points.append(obj_p) # 物点
img_points.append(corners2)
frame = cv2.drawChessboardCorners(frame,(chess_corner_x,chess_corner_y),corners2,ret)
cv2.imshow("cap", frame)
if cv2.waitKey(30) == 27:
break
except:
traceback.print_exc()
finally:
cap.release()
cv2.destroyAllWindows()
detect()
ret,mrx,dist,rvecs,tveces = cv2.calibrateCamera(obj_points,img_points,(chess_corner_x,chess_corner_y),None,None)
if ret:
print("done!",mrx,dist,rvecs,tveces)
else:
print("calibration error")
tot_mean_error=0
mean_error_image = 0
for i in range(len(obj_points)):
reprojected_points,_ = cv2.projectPoints(obj_points[i],rvecs[i],tveces[i],mrx,dist)
reprojected_points=reprojected_points.reshape(-1,2)
mean_error_image=np.sum(np.sum(np.abs(img_points[i]-reprojected_points)**2,axis=-1)**(1./2))/np.alen(reprojected_points)
tot_mean_error +=mean_error_image
mean_error=tot_mean_error/len(obj_points)
print("Mean reprojection error: ",mean_error)
tot_error=0
total_points=0
for i in range(len(obj_points)):
reprojected_points,_ = cv2.projectPoints(obj_points[i],rvecs[i],tveces[i],mrx,dist)
reprojected_points=reprojected_points.reshape(-1,2)
tot_error+=np.sum(np.abs(img_points[i]-reprojected_points)**2)
total_points+=len(obj_points[i])
mean_error=np.sqrt(tot_error/total_points)
print("Mean reprojection error: ",mean_error)

  1. A Flexible New Technique for Camera Calibration ↩︎

  2. Accurate camera calibration using iterative refinement of control points ↩︎

  3. Algorithm for computation of region-based image edge profile acutance ↩︎