相机标定与校正原理及代码(Python、C++)实现

相机标定与校正

    • 一、相机标定理论背景
      • 1.1 相机模型
      • 1.2 畸变模型
    • 二、详细标定流程
      • 2.1 数据采集
      • 2.2 角点提取
      • 2.3 构造对应关系
      • 2.4 标定求解
      • 2.5 图像校正
      • 2.6 标定精度分析
    • 三、Python 代码详细示例
    • 四、C++ 代码详细示例
    • 五、常见问题与注意事项
    • 六、总结

一、相机标定理论背景

1.1 相机模型

  • 针孔模型:
    相机可以用针孔模型描述,即假设所有光线都通过一个单一的光心,然后在成像平面上成像。该模型定义了相机的内参和外参。
  • 内参:
    • 焦距(fx, fy):描述相机镜头的放大能力。
    • 主点(cx, cy):通常为图像中心,表示成像平面上光心的位置。
    • 畸变系数: 常见有径向畸变(k1, k2, k3)和切向畸变(p1, p2)。
  • 外参:
    表示相机坐标系与世界坐标系之间的旋转(旋转向量)和平移(平移向量)。

1.2 畸变模型

  • 径向畸变:
    常见原因是镜头形状非理想,图像边缘出现“桶形”或“枕形”畸变。
  • 切向畸变:
    主要由镜头与图像传感器不完全平行引起。

二、详细标定流程

2.1 数据采集

  • 标定板选择:
    常用棋盘格、对称圆点或不对称圆点等。棋盘格较为常见,因为角点提取简单且精度较高。
  • 图像采集:
    • 数量要求: 至少 10~15 张不同角度、不同距离的图像。
    • 分布要求: 确保棋盘格在图像中位置、旋转角度均有较大变化,覆盖整个视场。
    • 光照均匀: 避免过曝或阴影影响角点检测。

2.2 角点提取

  • 初步检测:
    使用 OpenCV 函数 findChessboardCorners(棋盘格)进行初步角点检测。
  • 亚像素精确化:
    调用 cornerSubPix 进一步优化角点位置,迭代算法使误差达到亚像素级别。
  • 结果验证:
    可视化检测结果,检查是否检测到全部角点并且顺序正确(通常从左上角开始依行排列)。

2.3 构造对应关系

  • 世界坐标系:
    假设棋盘格平面为 Z=0,构造一个规则的网格坐标。例如,对于一个 9×6 的棋盘,生成 (0,0,0), (1,0,0), …, (8,5,0) 的坐标,再乘以实际棋盘格大小(如 25mm)。
  • 图像坐标系:
    每幅图像中检测到的角点坐标。确保顺序与世界坐标点一致。

2.4 标定求解

  • 调用标定函数:
    使用 calibrateCamera 传入世界坐标集合、图像坐标集合、图像尺寸等参数。该函数通过最小化重投影误差求解相机内参和外参。
  • 输出参数:
    • camera_matrix: 内参矩阵
    • dist_coeffs: 畸变系数
    • rvecs、tvecs: 各图像对应的旋转向量和平移向量
    • 重投影误差: 可作为标定精度评价指标,通常越低越好。

2.5 图像校正

  • 获取最优新内参矩阵:
    使用 getOptimalNewCameraMatrix 可以根据畸变系数和 ROI(感兴趣区域)得到优化的内参矩阵,减少黑边。
  • 图像矫正:
    调用 undistort 对图像进行去畸变处理。
  • 结果裁剪:
    根据 ROI 裁剪图像,使校正后的图像更加紧凑。

2.6 标定精度分析

  • 重投影误差:
    平均误差通常在 0.1~1 像素之间较为理想。
  • 可视化验证:
    对比原始图像和校正后的图像,检查直线是否变直,观察图像边缘是否有明显黑边或拉伸失真。

三、Python 代码详细示例

以下代码在之前示例基础上增加了更多注释和步骤解释,便于理解每一行代码的作用。

import cv2
import numpy as np
import glob

# 设置棋盘内角点数(棋盘行数和列数,即角点个数)
chessboard_size = (9, 6)
# 设置棋盘每个格子的实际尺寸(单位:毫米或其他)
square_size = 25

# 准备棋盘格世界坐标点(假设棋盘在 z=0 平面)
# 例如: (0,0,0), (1,0,0), ... , (8,5,0)
objp = np.zeros((chessboard_size[0] * chessboard_size[1], 3), np.float32)
# 利用 meshgrid 生成二维网格,并转换为 N×2 的数组,最后赋值到前三列中
objp[:, :2] = np.mgrid[0:chessboard_size[0], 0:chessboard_size[1]].T.reshape(-1, 2)
objp = objp * square_size  # 缩放到实际尺寸

# 存储所有图像对应的 3D 世界坐标和 2D 图像坐标
objpoints = []  # 每幅图像的棋盘格点的世界坐标
imgpoints = []  # 每幅图像中检测到的角点

# 获取所有标定图像路径,调整路径为实际保存位置
images = glob.glob('calib_images/*.jpg')

for fname in images:
    img = cv2.imread(fname)
    if

你可能感兴趣的:(自动驾驶,python,c++,自动驾驶)