视觉基础:针孔相机模型、畸变模型、相机标定原理

做视觉SLAM,第一个绕不开的就是相机模型。说白了,相机是怎么把三维世界变成一张二维图片的?这个问题搞不清楚,后面什么特征匹配、三角化、BA优化,全是空中楼阁。

我个人习惯,讲视觉基础一定从针孔模型开始。它是最简单、最直观的模型,也是理解所有复杂相机模型的基础。

针孔相机模型

想象一个密闭的黑盒子,前面戳一个小孔,光线穿过小孔,在后面的感光平面上成像。这就是针孔相机的基本原理。

数学上怎么描述?我们建立四个坐标系:世界坐标系、相机坐标系、图像坐标系、像素坐标系。嗯,这里要注意,初学者最容易搞混的就是图像坐标系和像素坐标系。

  • 世界坐标系:描述物体在真实世界中的位置,单位是米。
  • 相机坐标系:以相机光心为原点,Z轴指向相机前方,单位是米。
  • 图像坐标系:在成像平面上,以光轴与平面的交点为原点,单位是毫米。
  • 像素坐标系:在图像上,以左上角为原点,单位是像素。

从世界坐标到像素坐标,经历了三次变换:刚体变换(外参)、透视投影、仿射变换(内参)。

核心公式:

s * p = K * [R | t] * P

其中:

  • P 是世界坐标点 (X, Y, Z, 1)
  • R, t 是相机外参(旋转和平移)
  • K 是相机内参矩阵
  • p 是像素坐标 (u, v, 1)
  • s 是尺度因子

内参矩阵K长这样:

K = [fx  0  cx]
    [ 0  fy  cy]
    [ 0   0   1]

fx, fy 是焦距在像素单位下的表示,cx, cy 是光心在像素坐标系中的偏移。我在项目中遇到过,有些廉价摄像头的光心偏移特别大,如果不标定直接用,重建出来的三维点云会严重变形。

畸变模型

现实中的镜头不是完美的针孔。为了收集更多光线,镜头是凸透镜,这就带来了畸变。说白了,就是直线变弯了。

畸变主要分两类:

  • 径向畸变:光线在镜头边缘弯曲更厉害。表现为「桶形畸变」或「枕形畸变」。
  • 切向畸变:镜头和成像平面不平行。表现为「梯形畸变」。

数学上怎么矫正?我们用多项式模型来描述畸变:

x_distorted = x * (1 + k1*r^2 + k2*r^4 + k3*r^6) + 2*p1*x*y + p2*(r^2 + 2*x^2)
y_distorted = y * (1 + k1*r^2 + k2*r^4 + k3*r^6) + p1*(r^2 + 2*y^2) + 2*p2*x*y

其中 k1, k2, k3 是径向畸变参数,p1, p2 是切向畸变参数。r 是点到光心的距离。

避坑指南:

我曾经在标定一个广角镜头时,只用了k1, k2两个参数,结果图像边缘的畸变矫正效果很差。后来加了k3,效果才明显改善。对于普通镜头,k1, k2就够了;但广角或鱼眼镜头,一定要用三个径向畸变参数。

相机标定原理

相机标定,就是求解内参和畸变参数的过程。你想想看,我们怎么知道fx, fy, cx, cy是多少?怎么知道镜头畸变有多大?

最经典的方法是张正友标定法。它的核心思想是:拍摄不同角度的棋盘格,利用棋盘格上角点的已知几何关系,来求解相机参数。

具体步骤:

  1. 打印一张棋盘格,贴在平面上。
  2. 用相机从不同角度拍摄10-20张照片。
  3. 检测每张图片中的角点坐标。
  4. 利用角点的世界坐标和像素坐标,建立方程组。
  5. 用最小二乘法求解内参和畸变参数。
  6. 用非线性优化(如Levenberg-Marquardt)进行精化。

注意事项:

  • 棋盘格要平整,不能有褶皱。
  • 拍摄角度要覆盖各个方向,包括倾斜、旋转。
  • 棋盘格在图像中的占比要适中,太小或太大都会影响精度。
  • 光照要均匀,避免反光。

我个人习惯,标定完成后会做一件事:用标定结果去矫正一张图片,然后肉眼检查矫正后的直线是不是真的直了。如果还有弯曲,说明标定不够好,需要重新拍。

下面这张图展示了整个视觉基础的知识体系:

视觉基础:相机模型与标定知识体系 相机模型与标定 针孔相机模型 畸变模型 相机标定原理 坐标系定义 内参矩阵K 外参R,t 透视投影 径向畸变 切向畸变 畸变参数k1,k2,k3 畸变参数p1,p2 张正友标定法 棋盘格检测 最小二乘求解 非线性优化 最终目标:获取精确的内参和畸变参数

标定完成后,你会得到一组参数。我建议你把这些参数保存成YAML文件,方便后续调用。格式大概是这样:

%YAML:1.0
---
camera_matrix:
  rows: 3
  cols: 3
  data: [535.4, 0, 320.1, 0, 536.2, 240.5, 0, 0, 1]
distortion_coefficients:
  rows: 1
  cols: 5
  data: [-0.283, 0.075, 0.001, -0.002, 0.0]

有了这些参数,你就可以对图像做去畸变处理,然后进行后续的视觉SLAM操作了。记住,标定是视觉SLAM的第一步,也是最重要的一步。标定不准,后面全是白费功夫。

实用建议:

如果你用的是ROS,可以直接用camera_calibration包来标定,非常方便。它会实时显示标定进度和重投影误差。我一般要求重投影误差小于0.5像素才算合格。

专注资料整理