一.相机标定的原理
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和y
obj_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 += 1
cv2.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-03
2.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
畸变矫正前的图像:
畸变矫正后的图像:
分析:
运行程序,成功的实现了自己手机参数的标定,计算出了照相机的内部参数和外部参数。通过此次实验让我进一步了解了张正友标定法的奇妙之处。