一.相机标定的原理
1.1 相机如何成像:
相机成像系统中,共包含四个坐标系:世界坐标系、相机坐标系、图像坐标系、像素坐标系。
1.1.1 世界坐标系:
世界坐标系(world coordinate),也称为测量坐标系,是一个三维直角坐标系,以其为基准可以描述相机和待测物体的空间位置。世界坐标系的位置可以根据实际情况自由确定。
1.1.2 相机坐标系:
相机坐标系(camera coordinate),也是一个三维直角坐标系,原点位于镜头光心处,x、y轴分别与相面的两边平行,z轴为镜头光轴,与像平面垂直
1.1.3 像素坐标系、图像坐标系:
像素坐标系(pixel coordinate)
如上图,像素坐标系是一个二维直角坐标系,反映了相机CCD/CMOS芯片中像素的排列情况。原点位于图像的左上角,轴、轴分别于像面的两边平行。像素坐标系中坐标轴的单位是像素(整数)。
像素坐标系不利于坐标变换,因此需要建立图像坐标系,其坐标轴的单位通常为毫米(mm),原点是相机光轴与相面的交点(称为主点),即图像的中心点,轴、轴分别与轴、轴平行。故两个坐标系实际是平移关系,即可以通过平移就可得到。
1.2 相机标定的目的:
求出相机的内、外参数,以及畸变参数。
标定相机后通常是想做两件事:一个是由于每个镜头的畸变程度各不相同,通过相机标定可以校正这种镜头畸变矫正畸变,生成矫正后的图像;另一个是根据获得的图像重构三维场景。
1.2 相机标定的总体原理:
摄像机标定(Camera calibraTIon)简单来说就是从世界坐标系换到图像坐标系的过程,也就是求最终的投影矩阵的过程。
二.相机标定步骤
1)、准备一个张正友标定法的棋盘格,棋盘格大小已知,用相机对其进行不同角度的拍摄,得到一组图像;
2)、对图像中的特征点如标定板角点进行检测,得到标定板角点的像素坐标值,根据已知的棋盘格大小和世界坐标系原点,计算得到标定板角点的物理坐标值;
3)、求解内参矩阵与外参矩阵。
根据物理坐标值和像素坐标值的关系,求出 H 矩阵,进而构造 V 矩阵,求解 B 矩阵,利用B矩阵求解相机内参矩阵 A ,最后求解每张图片对应的相机外参矩阵 :
4)、求解畸变参数。
构造 D 矩阵,计算径向畸变参数;
5)、利用L-M(Levenberg-Marquardt)算法对上述参数进行优化
三、实现
3.1 数据准备
对标定板在不同角度拍照,得到10张图片。得到如下类似的图片。
3.2 代码(Python+OpenCV)
import cv2
import numpy as np
import glob# 设置寻找亚像素角点的参数,采用的停止准则是最大循环次数30和最大误差容限0.001
criteria = (cv2.TERM_CRITERIA_MAX_ITER | cv2.TERM_CRITERIA_EPS, 30, 0.001)# 获取标定板角点的位置
objp = np.zeros((4 * 4, 3), np.float32)
objp[:, :2] = np.mgrid[0:4, 0:4].T.reshape(-1, 2) # 将世界坐标系建在标定板上,所有点的Z坐标全部为0,所以只需要赋值x和yobj_points = [] # 存储3D点
img_points = [] # 存储2D点images = glob.glob(r"D:\software\pycharm\PycharmProjects\computer-version\biaoding\images\*.jpg")
i = 0
for fname in images:img = cv2.imread(fname)gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)size = gray.shape[::-1]ret, corners = cv2.findChessboardCorners(gray, (4, 4), None)# print(corners)if ret:obj_points.append(objp)corners2 = cv2.cornerSubPix(gray, corners, (5, 5), (-1, -1), criteria) # 在原角点的基础上寻找亚像素角点# print(corners2)if [corners2]:img_points.append(corners2)else:img_points.append(corners)cv2.drawChessboardCorners(img, (4, 4), corners, ret) # 记住,OpenCV的绘制函数一般无返回值i += 1cv2.imwrite('conimg' + str(i) + '.jpg', img)cv2.waitKey(1500)print(len(img_points))
cv2.destroyAllWindows()# 标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, size, None, None)print("内参数矩阵:\n", mtx) # 内参数矩阵
print("畸变系数:\n", dist) # 畸变系数 distortion cofficients = (k_1,k_2,p_1,p_2,k_3)
print("旋转向量:\n", rvecs) # 旋转向量 # 外参数
print("平移向量:\n", tvecs) # 平移向量 # 外参数print("-----------------------------------------------------")img = cv2.imread(images[2])
h, w = img.shape[:2]
# 获取新的相机矩阵和ROI
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h))
# 进行畸变校正
dst = cv2.undistort(img, mtx, dist, None, newcameramtx)
# 裁剪校正后的图片,去掉黑边
x, y, w, h = roi
dst = dst[y:y + h, x:x + w]
# 保存校正后的图片
cv2.imwrite('undistorted.jpg', dst)
print("校正后的图片已保存到文件 'undistorted.jpg'")
结果如下:
D:\software\anaconda3\envs\homework\python.exe D:\software\pycharm\PycharmProjects\computer-version\biaoding\biaoding.py
10
内参数矩阵:[[2.96560599e+04 0.00000000e+00 1.14649373e+03][0.00000000e+00 2.27193976e+04 2.23086118e+03][0.00000000e+00 0.00000000e+00 1.00000000e+00]]
畸变系数:[[-2.89172774e+00 -7.08266723e+02 -8.71069820e-02 9.23282091e-032.69077473e+04]]
旋转向量:(array([[ 0.00393121],[-0.55572922],[ 0.00146949]]), array([[-0.24713914],[-0.62161368],[ 1.12164248]]), array([[0.03655035],[0.72426273],[0.58207192]]), array([[ 0.03267646],[-0.81493845],[ 0.33111165]]), array([[ 0.10136185],[-0.8188412 ],[ 0.0126507 ]]), array([[ 0.07815116],[-0.45667052],[ 0.04574239]]), array([[-0.06538785],[-0.45146705],[ 0.29559633]]), array([[0.19950015],[0.59439631],[1.05909245]]), array([[-0.61847323],[-0.65382223],[ 1.44780297]]), array([[-0.56075885],[-0.65910126],[ 1.21835979]]))
平移向量:(array([[-1.88349377],[-2.34826569],[79.07381781]]), array([[-3.71964945e-03],[-2.09696872e+00],[ 9.57830776e+01]]), array([[ 2.82236969],[-10.16609008],[105.50710577]]), array([[ 2.4498244 ],[ -9.91592803],[113.69245822]]), array([[ 1.94032193],[ -9.41660327],[118.34213194]]), array([[ 3.29323168],[-10.37799624],[150.0005035 ]]), array([[ 1.90004234],[ -9.89181093],[136.81120801]]), array([[ 3.81109485],[ -9.80118203],[131.55206487]]), array([[ 0.12140321],[ -3.0523607 ],[114.3591686 ]]), array([[ 0.17173674],[-2.75540486],[80.26443015]]))
-----------------------------------------------------
校正后的图片已保存到文件 'undistorted.jpg'Process finished with exit code 0
畸变矫正前的图像:
畸变矫正后的图像:
分析:
运行程序,成功的实现了自己手机参数的标定,计算出了照相机的内部参数和外部参数。通过此次实验让我进一步了解了张正友标定法的奇妙之处。