照相机标定

一.相机标定的原理

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

畸变矫正前的图像:

畸变矫正后的图像:

分析:

运行程序,成功的实现了自己手机参数的标定,计算出了照相机的内部参数和外部参数。通过此次实验让我进一步了解了张正友标定法的奇妙之处。 

猜你喜欢

转载自blog.csdn.net/qq_44896301/article/details/130869103