4. 相机成像模型:针孔相机模型、内参矩阵、畸变模型(径向/切向)

说到障碍物检测,相机就是我们的「眼睛」。但相机怎么把三维世界变成二维图像的呢?这背后就是成像模型在起作用。我刚开始做感知时,总觉得这东西偏理论,直到有一次标定出了问题,检测框全飘了……嗯,从那以后我再也不敢小看它了。

说白了,相机成像模型就是回答一个问题:空间中的一个点,到底落在图像上的哪个像素位置? 今天咱们就把它彻底讲透。

核心要点:相机成像 = 针孔模型(理想情况) + 畸变修正(实际情况)

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

你想想看,最早的相机就是一个小黑屋,墙上开个针孔。外面的光线穿过小孔,在对面的墙上形成倒像。这就是针孔模型的原型。

数学上怎么描述?其实就一个相似三角形的关系。假设相机坐标系下有个点 P(X, Y, Z),它投影到成像平面上的位置 p(x, y) 满足:

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

这里的 f 是焦距。注意,这个模型是理想化的——它假设光线是直线传播,没有畸变。但实际镜头不是针孔,是透镜组,所以必然有偏差。

我的经验: 实际项目中,我们很少直接用针孔模型做计算,但它是一切的基础。理解了这个,后面的内参、畸变就都好办了。

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

刚才的公式给出的是物理坐标(单位是毫米),但图像是像素坐标(单位是像素)。怎么转换?这就需要内参矩阵了。

内参矩阵 K 长这样:

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

其中:

  • fx, fy:分别是 x 和 y 方向的焦距(像素单位)
  • cx, cy:主点坐标,通常是图像中心

完整的投影公式就是:

pixel_x = fx * (X / Z) + cx
pixel_y = fy * (Y / Z) + cy

写成矩阵形式更简洁:

[u]   [fx, 0, cx] [X]
[v] = [0, fy, cy] [Y]
[1]   [0,  0,  1] [Z]

这里 u, v 就是像素坐标。说白了,内参矩阵就是把「物理世界里的角度」映射成「图像上的像素位置」。

注意: 内参是相机出厂就固定的,但会随温度、震动等轻微变化。所以量产车下线时,每台车都要单独标定内参。

4.3 畸变模型——现实总是不完美的

理想很丰满,现实很骨感。实际镜头拍出来的图像,边缘的直线会变弯。这就是畸变。主要分两种:

4.3.1 径向畸变

说白了就是「鱼眼效果」。离图像中心越远,变形越严重。常见的有:

  • 桶形畸变:直线向外凸,像木桶
  • 枕形畸变:直线向内凹,像枕头

数学上用泰勒级数修正:

x_corrected = x * (1 + k1*r² + k2*r⁴ + k3*r⁶)
y_corrected = y * (1 + k1*r² + k2*r⁴ + k3*r⁶)

其中 r 是点到中心的距离,k1, k2, k3 是畸变系数。

我曾经踩过的坑: 有一次用广角镜头做障碍物检测,没做畸变校正就直接跑模型。结果边缘的车都变形了,检测框完全对不上。后来老老实实加了畸变校正,精度直接提升 15%。

4.3.2 切向畸变

这个是因为镜头和成像平面不平行造成的。说白了就是「镜头装歪了」。修正公式:

x_corrected = x + [2*p1*x*y + p2*(r² + 2*x²)]
y_corrected = y + [p1*(r² + 2*y²) + 2*p2*x*y]

p1, p2 是切向畸变系数。实际项目中,切向畸变通常比径向畸变小一个数量级,但也不能忽略。

4.4 完整的畸变校正流程

在实际工程中,我们一般这样做:

  1. 获取原始图像:从相机拿到带畸变的图
  2. 计算畸变映射:用内参和畸变系数,算出每个像素的校正偏移
  3. 重映射:把原始像素搬到正确的位置
  4. 插值:因为坐标是浮点数,需要用双线性插值补全

代码实现(OpenCV 示例):

import cv2
import numpy as np

# 假设已经标定好了内参和畸变系数
K = np.array([[fx, 0, cx],
              [0, fy, cy],
              [0, 0, 1]])
dist = np.array([k1, k2, p1, p2, k3])

# 畸变校正
img_undistorted = cv2.undistort(img, K, dist)

# 或者用 initUndistortRectifyMap 做预计算(效率更高)
map1, map2 = cv2.initUndistortRectifyMap(K, dist, None, K, (w, h), cv2.CV_32FC1)
img_undistorted = cv2.remap(img, map1, map2, cv2.INTER_LINEAR)

我个人的习惯: 在自动驾驶中,畸变校正最好在图像预处理阶段就做掉。后续的目标检测、车道线识别都基于校正后的图像,这样外参标定也更准。

4.5 本章知识体系

下面这张图帮你理清整个相机成像模型的脉络:

相机成像模型知识体系 相机成像模型 针孔相机模型 内参矩阵 K 畸变模型 相似三角形 焦距 f fx, fy cx, cy 径向畸变 切向畸变 最终目标:从 3D 世界点 → 2D 像素坐标

4.6 小结

相机成像模型这块,说白了就三件事:

  • 针孔模型:告诉你理想情况下怎么投影
  • 内参矩阵:把物理坐标转成像素坐标
  • 畸变模型:修正镜头带来的变形

实际做障碍物检测时,畸变校正这一步千万别省。我见过太多团队,模型精度上不去,最后发现是图像都没校正。嗯,细节决定成败。

可可精品资料库