4、相机模型:针孔相机模型、内参矩阵、畸变模型

聊到3D视觉,绕不开的第一个核心就是——相机是如何看到世界的。

说白了,相机就是把三维空间里的点,投影到二维图像上。这个过程怎么建模?就是我们今天要聊的针孔相机模型。我刚开始做SLAM的时候,觉得这东西太简单了,不就是个小孔成像嘛。结果真上手标定相机、做特征点重投影的时候,才发现里面坑不少。

嗯,咱们一步步来。

4.1 针孔相机模型——最朴素的成像原理

先想象一个暗箱,前面戳一个小孔。外面的光线穿过小孔,在后面的感光平面上形成一个倒像。这就是针孔相机的基本原理。

核心思想:三维世界中的点 P(X, Y, Z),通过光心(小孔)投影到成像平面上的点 p(x, y)。这个映射关系是线性的,可以用相似三角形来描述。

数学上怎么写?假设相机光心在原点,成像平面在 Z = f 处(f 是焦距),那么空间点 P = [X, Y, Z]^T 投影到成像平面上的坐标为:

x = f * (X / Z)
y = f * (Y / Z)

注意,这里成的像是倒立的。实际相机传感器会帮我们翻转过来,所以公式里通常直接写成正像。

我个人习惯把这一步叫做「归一化到相机坐标系」——先把三维点除以 Z,得到归一化平面上的坐标 [X/Z, Y/Z, 1]^T,再乘以焦距 f。这样逻辑更清晰。

4.2 内参矩阵——从物理坐标到像素坐标

上面算出来的 (x, y) 还是物理单位(毫米、米),但图像是由像素组成的。怎么把物理坐标映射到像素坐标?这就需要内参矩阵 K。

内参矩阵 K 长这样:

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

其中:

  • fx, fy:分别是 x 和 y 方向上的焦距(像素单位)。通常 fx ≈ fy,但也不。
  • cx, cy:主点坐标,也就是光轴与成像平面的交点。理想情况下在图像中心,但实际会有偏移。

完整的投影公式就是:

p_pixel = K * P_normalized

即:
u = fx * (X/Z) + cx
v = fy * (Y/Z) + cy

我的经验:内参矩阵是相机标定的核心输出。我在项目里见过有人直接用厂家给的标称值,结果重投影误差大得离谱。记住:每台相机都要单独标定,尤其是手机摄像头、工业相机这种批量生产的,个体差异比你想象的大。

内参矩阵的物理意义很直观:它描述了从相机坐标系到图像坐标系的仿射变换。fx 和 fy 决定了图像的缩放,cx 和 cy 决定了图像的平移。

4.3 畸变模型——现实世界没有完美的镜头

理想很丰满,现实很骨感。针孔模型假设镜头是完美的,但实际镜头都有畸变。我最早做视觉里程计时,没做畸变校正,结果特征点匹配一塌糊涂。后来老老实实加了畸变模型,效果立竿见影。

畸变主要分两类:径向畸变和切向畸变。

4.3.1 径向畸变

径向畸变是镜头形状引起的,光线经过透镜边缘时弯曲程度和中心不一样。表现为:

  • 桶形畸变:图像向外凸出,广角镜头常见。
  • 枕形畸变:图像向内凹陷,长焦镜头常见。

数学模型用泰勒级数展开来近似:

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

其中 r^2 = x^2 + y^2,是归一化平面上的点到光心的距离。k1, k2, k3 是径向畸变系数。

避坑指南:我曾经在标定一个鱼眼镜头时,只用了 k1 和 k2,结果图像边缘还是歪的。后来加上 k3 才搞定。对于普通镜头,k1, k2 就够了;但鱼眼、广角镜头,一定要用 k3,甚至更高阶项。

4.3.2 切向畸变

切向畸变是镜头和成像平面不平行引起的,说白了就是装配误差。镜头没装正,稍微歪了一点。

数学模型:

x_distorted = x + [2*p1*x*y + p2*(r^2 + 2*x^2)]
y_distorted = y + [p1*(r^2 + 2*y^2) + 2*p2*x*y]

p1, p2 是切向畸变系数。

完整的畸变模型就是径向 + 切向的组合。OpenCV 里标定相机时,通常输出 5 个畸变参数:[k1, k2, p1, p2, k3]。

4.4 完整流程:从三维点到像素

把上面所有步骤串起来,一个三维点怎么变成图像上的像素?

  1. 世界坐标系 → 相机坐标系:通过外参矩阵(旋转 R + 平移 t)变换。
  2. 相机坐标系 → 归一化平面:除以 Z,得到 [X/Z, Y/Z, 1]^T。
  3. 施加畸变:用径向和切向畸变模型修正归一化坐标。
  4. 归一化平面 → 像素坐标:乘以内参矩阵 K,得到 (u, v)。

这个过程在SLAM里叫投影,反过来从像素到三维点叫反投影(但会丢失深度信息)。

关键点:畸变校正一定要在归一化平面上做,而不是在像素坐标上做。顺序搞反了,校正结果就是错的。我见过不少新手踩这个坑。

4.5 知识结构图

下面这张图把相机模型的核心流程串起来了,方便你理解整体脉络:

相机模型核心流程 世界坐标系 P_w = [X_w, Y_w, Z_w] 外参 R,t 相机坐标系 P_c = [X, Y, Z] 除以 Z 归一化平面 [X/Z, Y/Z, 1] 畸变校正 径向 + 切向 像素坐标 [u, v] 内参矩阵 K fx, fy, cx, cy 物理→像素映射 畸变参数 k1, k2, k3, p1, p2

4.6 常用标定工具与参数对比

实际项目中,我们不会手算这些参数。常用的标定工具有:

工具 特点 适用场景
OpenCV cv::calibrateCamera 经典棋盘格标定,成熟稳定 普通镜头、工业相机
MATLAB Camera Calibrator 交互式操作,可视化好 快速验证、教学演示
Kalibr 支持多相机、IMU联合标定 VIO、SLAM系统标定
ROS camera_calibration 基于棋盘格,实时标定 机器人平台、嵌入式设备

我的建议:标定棋盘格时,多拍不同角度(倾斜、旋转、远近),至少20张。我见过有人只拍10张正面图,标出来的畸变参数完全不对。另外,棋盘格要平整,贴在硬板上,别用软纸——弯曲的棋盘格会引入额外误差。

4.7 小结

相机模型是整个3D视觉的基石。你想想看,后面所有的三角测量、PnP、BA优化,都建立在「知道像素点对应哪个三维方向」这个基础上。如果相机模型搞错了,后面的算法再漂亮也是白搭。

记住三个核心:

  • 针孔模型:三维到二维的线性投影关系。
  • 内参矩阵:物理坐标到像素坐标的桥梁。
  • 畸变模型:修正镜头带来的非线性失真。

嗯,这一章就到这。把这些概念吃透了,后面讲外参、讲投影矩阵的时候,你会觉得顺滑很多。