1. 相机内参标定基础

任务目标

使用OpenCV对棋盘格标定板图像进行相机内参标定,输出相机矩阵(焦距与主点)和畸变系数,并通过重投影误差评估标定质量。

环境依赖

  • Python 3.8+
  • OpenCV(opencv-python)
  • NumPy

实现代码

import cv2
import numpy as np
import glob

# 棋盘格内角点尺寸(内角点数 = 格子数 - 1)
CHECKERBOARD = (9, 6)
SQUARE_SIZE = 25.0  # 棋盘格每个格子的实际边长(单位:mm)

# 准备对象点(世界坐标系中棋盘格角点的三维坐标,z=0)
objp = np.zeros((CHECKERBOARD[0] * CHECKERBOARD[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:CHECKERBOARD[0], 0:CHECKERBOARD[1]].T.reshape(-1, 2)
objp *= SQUARE_SIZE

objpoints = []  # 所有图像对应的对象点
imgpoints = []  # 所有图像对应的像素角点

# 读取标定图像(请将图像文件放在当前目录的 calib_images/ 文件夹下)
images = glob.glob('calib_images/*.jpg')

for fname in images:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    ret, corners = cv2.findChessboardCorners(gray, CHECKERBOARD, None)
    if ret:
        objpoints.append(objp)
        # 亚像素角点精化
        criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
        corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
        imgpoints.append(corners2)
        # 可视化角点(可选)
        cv2.drawChessboardCorners(img, CHECKERBOARD, corners2, ret)
        cv2.imshow('Corners', img)
        cv2.waitKey(100)
    else:
        print(f'[跳过] 未检测到完整棋盘格: {fname}')

cv2.destroyAllWindows()

if len(objpoints) < 5:
    raise RuntimeError('有效标定图像不足5张,请补充图像后重试')

# 执行相机标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
    objpoints, imgpoints, gray.shape[::-1], None, None
)

# 输出标定结果
print('=== 相机内参标定结果 ===')
print('相机矩阵 (Camera Matrix):')
print(mtx)
print('\n畸变系数 (Distortion Coefficients):')
print(dist.ravel())
print(f'\n重投影误差 (RMS): {ret:.4f} 像素')

# 计算每张图像的平均重投影误差(可选验证)
total_error = 0
for i in range(len(objpoints)):
    imgpoints_proj, _ = cv2.projectPoints(
        objpoints[i], rvecs[i], tvecs[i], mtx, dist
    )
    error = cv2.norm(imgpoints[i], imgpoints_proj, cv2.NORM_L2) / len(imgpoints_proj)
    total_error += error
print(f'平均每张图像重投影误差: {total_error / len(objpoints):.4f} 像素')

运行方式

  1. 准备标定板:打印一张9×6内角点的棋盘格(建议A4纸打印并贴在硬板上),测量每个格子的实际边长并填入SQUARE_SIZE。
  2. 采集图像:从不同角度、距离、倾斜姿态拍摄棋盘格,确保棋盘格完整出现在画面中,共采集15~20张。将图像存入calib_images/文件夹。
  3. 运行脚本:python calibrate.py(将上述代码保存为calibrate.py)。

验证方法与通过标准

  • 角点检测:运行过程中每张图像会显示检测到的棋盘格角点,若某张图像未检测到完整棋盘格,程序会跳过并提示。
  • 标定成功标志:程序正常输出相机矩阵、畸变系数和RMS误差,且不报错。
  • 质量判定:RMS重投影误差小于0.5像素为优秀,小于1.0像素为合格。若RMS大于1.0像素,应检查角点检测是否准确、剔除模糊图像、增加图像数量或改变拍摄角度后重新标定。
  • 结果合理性:焦距(mtx[0,0]和mtx[1,1])应与图像分辨率在同一量级(例如对1920×1080图像,焦距通常在1000~3000像素范围);主点(mtx[0,2], mtx[1,2])应接近图像中心。

常见问题

  • 检测不到角点:确保棋盘格图像清晰、光照均匀、无强烈反光;避免棋盘格过小或过大占画面比例不当。
  • 标定结果不稳定:图像数量不足或拍摄角度过于单一(如全部为正面拍摄)会导致结果不稳定,应保证图像覆盖画面各个区域并包含明显倾斜角度。
  • 重投影误差偏大:检查SQUARE_SIZE是否与实际打印尺寸一致(该值不影响RMS计算,但影响焦距的物理尺度);剔除角点检测明显偏移的图像。