3. 相机外参标定(世界坐标系到相机坐标系)
任务目标
已知标定板上若干特征点的世界坐标及其在图像中的像素坐标,求解相机外参(旋转矩阵 R 和平移向量 t),将世界坐标系中的点变换到相机坐标系。
核心原理
相机成像模型为:
s * [u, v, 1]^T = K * [R | t] * [X, Y, Z, 1]^T
其中 K 为内参矩阵(本案例假设已知),(X,Y,Z) 为世界坐标,(u,v) 为像素坐标。通过至少 6 组 3D-2D 对应点,使用 PnP(Perspective-n-Point)算法即可求解外参。本案例使用 OpenCV 的 solvePnP 实现。
环境依赖
- Python 3.7+
- OpenCV(
pip install opencv-python) - NumPy(
pip install numpy)
关键完整代码
import cv2
import numpy as np
# ========== 1. 相机内参(示例值,实际需通过标定获得) ==========
K = np.array([[800, 0, 320],
[0, 800, 240],
[0, 0, 1]], dtype=np.float64)
dist_coeffs = np.zeros((4, 1)) # 假设无畸变
# ========== 2. 标定板特征点:世界坐标(单位:mm) ==========
# 例:9x6 棋盘格,格边长 25mm,Z=0 平面
pattern_size = (9, 6)
square_size = 25.0
world_points = []
for j in range(pattern_size[1]):
for i in range(pattern_size[0]):
world_points.append([i * square_size, j * square_size, 0])
world_points = np.array(world_points, dtype=np.float64)
# ========== 3. 对应像素坐标(示例数据,实际由图像检测获得) ==========
# 此处为模拟数据,仅演示流程;实际使用时替换为角点检测结果
image_points = np.array([
[320, 240], [345, 241], [370, 242], [395, 243], [420, 244],
[445, 245], [470, 246], [495, 247], [520, 248],
[321, 265], [346, 266], [371, 267], [396, 268], [421, 269],
[446, 270], [471, 271], [496, 272], [521, 273],
# ... 实际需提供全部 54 组对应点
], dtype=np.float64)
# 若使用模拟数据,需保证点数匹配;此处仅取前 18 点演示
world_points = world_points[:len(image_points)]
# ========== 4. 使用 PnP 求解外参 ==========
success, rvec, tvec = cv2.solvePnP(
world_points, image_points, K, dist_coeffs,
flags=cv2.SOLVEPNP_ITERATIVE
)
if success:
# 旋转向量转旋转矩阵
R, _ = cv2.Rodrigues(rvec)
print("旋转矩阵 R (世界→相机):")
print(R)
print("\n平移向量 t (世界→相机):")
print(tvec.reshape(3, 1))
# ========== 5. 验证:重投影误差 ==========
proj_points, _ = cv2.projectPoints(
world_points, rvec, tvec, K, dist_coeffs
)
proj_points = proj_points.reshape(-1, 2)
errors = np.linalg.norm(image_points - proj_points, axis=1)
mean_error = np.mean(errors)
print(f"\n重投影均方根误差: {mean_error:.4f} 像素")
print("判定标准:误差 < 0.5 像素,标定结果可靠")
else:
print("PnP 求解失败")
运行与验证
- 数据准备:将
image_points替换为实际图像中检测到的棋盘格角点像素坐标(使用cv2.findChessboardCorners获取),并确保与world_points一一对应。 - 执行代码:直接运行上述 Python 脚本。
- 通过条件:程序输出 R 和 t,且重投影均方根误差小于 0.5 像素。误差越小,外参标定越精确。
常见问题
- 点数不足:solvePnP 至少需要 4 组非共面点或 6 组共面点,建议使用完整棋盘格(54 个角点)以获得稳定结果。
- 内参不准:内参 K 的误差会直接影响外参精度,应先用棋盘格完成内参标定。
- 畸变未处理:若镜头畸变明显,需提供畸变系数,否则重投影误差会偏大。