计算机视觉技术:使用Python和OpenCV实现相机标定与畸变校正详解
概述
相机标定是一种旨在通过确定相机的内参(焦距、光学中心、畸变系数)和外参(相机的位置和方向),提高图像在现实世界中的几何精度的过程。该过程可以纠正相机拍摄的图像中的畸变,使相机能够准确感知现实世界中的距离、角度和物体。一个很好的例子是纠正鱼眼相机拍摄的图像。
一、什么是相机标定
相机通过将其投影到二维平面上来捕捉现实世界。然而,由于光学元件和镜头的结构特性,这些图像可能会出现误差。最常见的误差是畸变和透视误差。相机标定通过计算相机的内参和外参来纠正这些误差,从而实现更准确的测量和几何计算。
关键参数
-
内参
- 焦距:根据镜头的焦距确定图像的大小。
- 光学中心(主点):相机镜头的中心点。
- 畸变系数:用于纠正镜头畸变,如桶形畸变和枕形畸变。(你可以在图 1 中清楚地看到这一点。)
-
外参
- 相机位置:相机相对于世界的位置(x、y、z 坐标)。
- 相机方向:相机相对于世界的视角(旋转角度)。
注意:术语“畸变”指的是镜头引起的误差,如变形或弯曲。
二、如何进行相机标定
通常使用已知的几何图案(如棋盘格)进行相机标定。该图案的已知尺寸和位置用作参考,以检测相机图像中的畸变。 标定过程包括以下步骤:
- 图像采集:要进行相机标定,你需要一组至少 15 张从不同角度拍摄的棋盘格图案的图像。该图案的角点有助于检测图像中的畸变。
- 角点检测:在每张图像中检测棋盘格图案的角点。正确检测这些角点对于准确标定至关重要。
- 内参和外参的计算:根据相机图像中的角点与它们的真实世界坐标之间的差异,优化并计算相机的内参和外参。通常使用 AI 算法或数学优化技术进行此计算。
- 畸变纠正:使用计算出的参数纠正图像中的畸变,去除非线性镜头畸变。
- 验证:为了测试标定的准确性,用相机拍摄一张新图像,并应用标定参数来纠正图像。然后观察纠正的准确性。
三、使用 OpenCV 在 Python 中进行相机标定
OpenCV 是 Python 中用于相机标定最常用的库之一。OpenCV 提供了相机标定和畸变纠正所需的函数。以下是简单的标定示例:
import cv2
import numpy as np
import glob
# 棋盘格的尺寸(内部角点的数量)
grid_size = (9, 6)
# 每个正方形的实际尺寸(2 厘米)
square_size = 2 # 厘米
# 棋盘格的 3D 世界坐标
obj_points = np.zeros((grid_size[0] * grid_size[1], 3), np.float32)
obj_points[:, :2] = np.mgrid[0:grid_size[0], 0:grid_size[1]].T.reshape(-1, 2) * square_size
# 用于存储标定所需点的列表
object_points = [] # 3D 世界坐标
image_points = [] # 2D 图像坐标
# 存放标定图像的文件夹
images = glob.glob('calibration_images/*.jpg')
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 查找棋盘格的角点
ret, corners = cv2.findChessboardCorners(gray, grid_size, None)
if ret:
object_points.append(obj_points)
image_points.append(corners)
# 可视化角点
cv2.drawChessboardCorners(img, grid_size, corners, ret)
cv2.imshow('Chessboard Corners', img)
cv2.waitKey(500)
cv2.destroyAllWindows()
# 执行标定
ret, camera_matrix, dist_coeffs, rvecs, tvecs = cv2.calibrateCamera(
object_points, image_points, gray.shape[::-1], None, None
)
# 保存相机矩阵和畸变系数
np.savez('calibration_data.npz', camera_matrix=camera_matrix, dist_coeffs=dist_coeffs)
# 打印标定结果
print("Camera Matrix:\n", camera_matrix)
print("Distortion Coefficients:\n", dist_coeffs)
四、代码解释
import cv2
import numpy as np
import glob
grid_size = (9, 6)
square_size = 2 # cm
obj_points = np.zeros((grid_size[0] * grid_size[1], 3), np.float32)
obj_points[:, :2] = np.mgrid[0:grid_size[0], 0:grid_size[1]].T.reshape(-1, 2) * square_size
注意:mgrid
是 NumPy 的一个函数,用于创建多维网格结构,其语法为 mgrid[start:end:step]
。
例如:
# 二维网格(网格)
x, y = np.mgrid[0:3, 0:3]
print("X:\n", x)
print("Y:\n", y)
输出:
X:
[[0 0 0]
[1 1 1]
[2 2 2]]
Y:
[[0 1 2]
[0 1 2]
[0 1 2]]
object_points = [] # 3D 世界坐标
image_points = [] # 2D 图像坐标
images = glob.glob('calibration_images/*.jpg')
calibration_images
文件夹中的所有 .jpg
文件,并将它们存储在一个列表中。这些是用于标定的棋盘格图案图像。for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
fname
指定的图像。ret, corners = cv2.findChessboardCorners(gray, grid_size, None)
if ret:
object_points.append(obj_points)
image_points.append(corners)
True
,即成功找到棋盘格的角点:
cv2.drawChessboardCorners(img, grid_size, corners, ret)
cv2.imshow('Chessboard Corners', img)
cv2.waitKey(500)
cv2.destroyAllWindows()
ret, camera_matrix, dist_coeffs, rvecs, tvecs = cv2.calibrateCamera(
object_points, image_points, gray.shape[::-1], None, None
)
注意:提供 **camera_matrix=None**
和 **dist_coeffs=None**
的原因是你希望计算这些参数并获得标定的结果。 在你的代码中,你试图从特定图像中推导出相机的内参和镜头畸变系数(camera_matrix 和 dist_coeffs)。
cv2.CALIB_USE_INTRINSIC_GUESS
:启用内参(相机矩阵和畸变系数)的初始猜测。cv2.CALIB_FIX_PRINCIPAL_POINT
:固定主点。cv2.CALIB_FIX_ASPECT_RATIO
:固定纵横比。cv2.TERM_CRITERIA_MAX_ITER
和 cv2.TERM_CRITERIA_EPS
等选项一起使用。np.savez('calibration_data.npz', camera_matrix=camera_matrix, dist_coeffs=dist_coeffs)
calibration_data.npz
的文件中。此文件允许你稍后重用标定参数。print("Camera Matrix:\n", camera_matrix)
print("Distortion Coefficients:\n", dist_coeffs)
此代码使用 OpenCV 进行相机标定,并保存结果。相机标定对于纠正图像中的畸变以及进行准确测量至关重要。
五、标定结果中你将获得的值
在这一过程结束时,camera_matrix
和 dist_coeffs
将由函数计算并返回:
camera_matrix
该矩阵包含相机的内参(焦距、主点等)。它用于了解相机镜头的特性以及透视变换。它是一个 3×3 矩阵,可能如下所示:
在这里,f_x 和 f_y 是相机沿水平和垂直方向的焦距(以像素为单位),c_x 和 c_y 是图像平面上主点的像素坐标(通常是图像的中心)。
什么是主点? 主点表示图像的光学中心,并定义了相机镜头与图像平面之间的关系。如果相机的光轴偏离中心,这种偏移可以通过 c_x 和 c_y 坐标检测到。
示例:
假设你的相机分辨率为 1920×1080 像素,标定后你获得以下 camera_matrix:
dist_coeffs
该向量包含镜头的畸变系数。这些系数用于纠正镜头的几何畸变(例如桶形畸变或枕形畸变)。
通常,该向量包含以下系数:
这些系数:
-
k_1、k_2、k_3:径向畸变系数。这些系数用于纠正桶形或枕形畸变。如果图像中的畸变随着距离中心的增加而变得更加明显,这就是径向畸变,k_1、k_2 和 k_3 用于纠正这种畸变。k_1 纠正靠近图像中心的畸变。k_2 纠正向图像边缘的较大畸变。k_3 对远离中心的点(尤其是边缘)进行微调,特别是对于边缘处的畸变。
桶形畸变:一种向边缘膨胀的畸变。
枕形畸变:一种向边缘收缩的畸变。
-
p_1、p_2:切向畸变系数。当镜头与传感器未完美对齐时,会发生这种畸变。如果镜头未完美居中或存在轴向偏移,图像倾向于向边缘偏移。p_1 和 p_2 用于纠正这种倾斜。p_1 纠正沿 x 轴(水平平面)的偏移或畸变。p_2 纠正沿 y 轴(垂直平面)的偏移或畸变。
什么是径向畸变和切向畸变?
径向畸变:这些畸变导致图像中的直线随着距离中心的增加而弯曲。k_1、k_2 和 k_3 用于纠正这些曲线。
切向畸变:当镜头与传感器不完全垂直时发生,导致图像向边缘偏移。p_1 和 p_2 用于纠正这些偏移。
-
k_4、k_5、k_6(可选):高阶径向畸变系数。这些参数用于纠正更复杂的畸变,例如来自超广角镜头的畸变。它们通常不用于标准标定,但在需要更精确的校正时可以应用。
示例:
dist_coeffs = [0.1, -0.25, 0.001, 0.002, 0.03]
K 和 P 的值越大,校正效果越强。随着它们的减小,校正效果减弱。
作者:知来者逆