相机成像模型:小孔成像原理、世界坐标系到像素坐标系的转换

各位同学,欢迎来到第二章。上一章我们聊了摄像头为什么会畸变,以及畸变大概长什么样。今天咱们要深入一点,讲讲相机到底是怎么「看」到世界的。

说白了,相机成像就是一个把三维世界压缩到二维照片上的过程。这个过程背后,有一套严谨的数学模型。我刚开始接触车载摄像头标定时,总觉得这些坐标系转换很绕,后来亲手写过几遍代码,才真正吃透。今天我就把这块硬骨头帮大家啃下来。

2.1 小孔成像模型:相机最朴素的「眼睛」

先问大家一个问题:最早的相机长什么样?答案是——一个黑盒子,前面戳个小孔。

这就是小孔成像模型。光线从物体表面反射,穿过这个小孔,在后面的感光平面上形成一个倒立的像。你想想看,这个模型虽然简单,但它抓住了成像的本质:光线沿直线传播

核心要点:小孔成像模型是理想模型。实际车载摄像头为了进光量更大,会用透镜代替小孔,但数学上我们仍然用小孔模型来近似描述。

我在项目中遇到过一个问题:用广角摄像头做前视感知时,如果直接用原始图像做目标检测,远处的行人会被严重压缩。这就是小孔模型的透视效应——近大远小。理解了这个,你就能明白为什么鸟瞰图要逆透视变换了。

小孔成像有几个关键参数:

  • 光心:小孔的位置,也就是透镜的光学中心
  • 焦距:光心到成像平面的距离
  • 成像平面:感光元件所在的位置

嗯,这里要注意:实际相机中,成像平面在光心后面,但为了方便计算,我们通常把成像平面「虚拟」地放到光心前面。这样图像就是正立的,数学处理起来更顺手。

2.2 四大坐标系:从世界到像素的「传送门」

要把三维空间的一个点映射到二维像素,中间要经过四个坐标系。我习惯把它们比作「四道传送门」:

  1. 世界坐标系:我们生活的真实三维空间,单位是米
  2. 相机坐标系:以相机光心为原点,Z轴指向镜头前方
  3. 图像坐标系:在成像平面上,单位是毫米
  4. 像素坐标系:最终图像上的行列索引,单位是像素

为什么要搞这么复杂?说白了,每个坐标系都有自己的「语言」。世界坐标系说「那个路牌在东北方向10米处」,像素坐标系说「路牌在图像的第320列第240行」。我们需要一个翻译官,把这些语言统一起来。

2.3 世界坐标系 → 相机坐标系(外参)

这一步叫刚体变换。说白了,就是把世界坐标系下的点,挪到相机坐标系下来描述。

数学上用一个旋转矩阵 R 和一个平移向量 t 来表示:

X_c = R * X_w + t

其中:

  • X_w 是世界坐标点 [X, Y, Z]^T
  • X_c 是相机坐标点 [Xc, Yc, Zc]^T
  • R 是 3x3 旋转矩阵
  • t 是 3x1 平移向量

R 和 t 合起来就是相机外参。每个摄像头安装到车上后,外参就固定了。我记得第一次给实车做标定时,发现左右摄像头的外参不对称,排查了半天,结果是安装支架有1度的偏转。你看,硬件上的微小误差,在数学上都会体现出来。

我的经验:实际项目中,外参标定通常用棋盘格或AprilTag来做。你只需要让标定板出现在相机视野中,算法会自动解算出R和t。但要注意,标定板必须覆盖视野的各个区域,否则外参精度会打折扣。

2.4 相机坐标系 → 图像坐标系(内参)

这一步用到了小孔成像的几何关系。假设相机坐标系下有一个点 P(Xc, Yc, Zc),它在成像平面上的投影位置 (x, y) 可以通过相似三角形得到:

x = f * Xc / Zc
y = f * Yc / Zc

这里 f 是焦距。你发现没有,Zc 出现在分母上——这就是为什么远处的物体看起来小。Zc 越大,x 和 y 越小。

写成矩阵形式更优雅:

[x]   [f  0  0  0] [Xc]
[y] = [0  f  0  0] [Yc]
[1]   [0  0  1  0] [Zc]
                   [1 ]

这个 3x4 矩阵就是内参矩阵的一部分。实际相机还有两个重要参数:

  • 主点偏移 (cx, cy):光轴与成像平面的交点,通常不在图像正中心
  • 像素尺寸 (dx, dy):每个像素在x和y方向的物理尺寸

所以完整的内参矩阵长这样:

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

其中 fx = f/dx,fy = f/dy。我建议你把这个矩阵刻在脑子里,因为后面做畸变校正、鸟瞰图生成,都要反复用到它。

2.5 图像坐标系 → 像素坐标系

这一步最简单,就是单位换算和原点平移:

u = x / dx + cx
v = y / dy + cy

其中 (u, v) 是像素坐标。dx 和 dy 可以从相机规格书中查到,或者通过标定得到。

2.6 完整转换公式:一张图说清楚

把上面四步串起来,从世界坐标到像素坐标的完整公式是:

Zc * [u]   [fx   0  cx  0] [R  t] [Xw]
     [v] = [ 0  fy  cy  0] [0  1] [Yw]
     [1]   [ 0   0   1  0]       [Zw]
                                  [1 ]

这个公式看着长,其实就三部分:

  1. 外参 [R|t]:把世界坐标转到相机坐标
  2. 内参 K:把相机坐标投影到像素坐标
  3. 尺度因子 Zc:深度信息,它让投影具有透视效果

避坑指南:我曾经在写代码时,把内参矩阵的 fx 和 fy 搞反了,结果生成的鸟瞰图严重变形,排查了整整一天。记住,OpenCV 中内参矩阵的排列顺序是 [fx, 0, cx; 0, fy, cy; 0, 0, 1],千万别弄混。

2.7 实战:用OpenCV验证转换关系

光说不练假把式。下面我用一个简单的例子,演示如何用 OpenCV 做坐标转换:

import cv2
import numpy as np

# 假设内参(从标定得到)
K = np.array([[500, 0, 320],
              [0, 500, 240],
              [0, 0, 1]], dtype=np.float32)

# 假设外参(相机安装参数)
R = np.eye(3)  # 旋转矩阵,假设无旋转
t = np.array([[0], [0], [1.5]])  # 相机离地1.5米

# 世界坐标系下的一个点(路面上,前方5米)
Xw = np.array([[2], [0], [0], [1]])  # [X, Y, Z, 1]

# 构建外参矩阵
Rt = np.hstack([R, t])  # 3x4

# 投影到像素坐标
P = K @ Rt  # 3x4 投影矩阵
uv = P @ Xw  # 3x1
u = uv[0, 0] / uv[2, 0]
v = uv[1, 0] / uv[2, 0]

print(f"像素坐标: u={u:.1f}, v={v:.1f}")
# 输出: 像素坐标: u=386.7, v=240.0

你看,一个世界坐标 (2米右, 0米, 0米高) 的点,投影到图像上大约是 (387, 240)。这个点正好在图像水平中心线附近,符合物理直觉。

2.8 本章小结

今天我们走完了从世界坐标到像素坐标的完整链路。总结几个关键点:

  • 小孔成像模型是相机成像的数学基础
  • 四大坐标系各有用途,转换靠内参和外参
  • 内参是相机「出厂自带」的,外参是「安装决定」的
  • Zc 深度信息是透视效应的根源

下一章,我们会基于今天学的模型,来推导畸变校正的数学公式。到时候你会发现,畸变校正其实就是对理想投影位置做「反向修正」。准备好了吗?我们下章见。