2. 相机成像模型:从世界到像素的“翻译官”

做双目视觉,第一个绕不开的坎儿就是相机成像模型。说白了,就是搞明白“真实世界里的一个点,是怎么变成照片上那个像素的”。

我个人习惯把相机成像比作一个“翻译”过程。世界坐标系是“中文”,像素坐标是“英文”,中间需要经过好几层翻译。今天我们就一层层拆开看。

核心一句话:相机成像模型 = 针孔模型(理想) + 畸变模型(现实) + 内外参(翻译规则)

相机成像模型知识体系 针孔相机模型 理想成像几何 小孔成像原理 透镜畸变模型 径向畸变 + 切向畸变 现实世界的修正 相机内参 fx, fy, cx, cy 相机内部属性 相机外参 旋转R + 平移t 相机在世界中的位姿 坐标转换 世界→相机→图像→像素 四步走 世界坐标系 → 相机坐标系 → 图像坐标系 → 像素坐标系 外参决定 | 内参决定 | 内参决定

2.1 针孔相机模型:最朴素的理想情况

针孔相机模型,是所有成像模型的“老祖宗”。它假设光线是直线传播的,穿过一个小孔,在后面的成像平面上形成倒立的像。

我记得刚入行时,师傅让我先把这个模型画一百遍。当时觉得枯燥,后来才发现——所有复杂的标定算法,底层逻辑都绕不开这个模型。

数学上,它用一个简单的相似三角形关系来描述:

// 针孔模型核心公式
// 假设相机光心在原点,光轴沿Z方向
// 三维点 P(X, Y, Z) 投影到成像平面上的点 p(x, y)

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

// 其中 f 是焦距(光心到成像平面的距离)

我的经验:实际项目中,我们很少直接用这个公式。但它是一切推导的起点。你想想看,如果连理想情况都搞不明白,怎么去处理现实中的畸变?

2.2 透镜畸变模型:现实总是不完美的

针孔模型很完美,但现实中的相机用的是透镜,不是针孔。透镜一上场,问题就来了——畸变。

畸变主要分两种:

  • 径向畸变:光线经过透镜边缘时弯曲得更厉害。表现为“桶形畸变”(照片中间鼓起来)或“枕形畸变”(照片边缘拉长)。
  • 切向畸变:透镜和成像平面不平行。表现为图像像被“拧”了一下。

我曾经在一个项目中,用廉价摄像头做双目测距。标定完一看,误差大得离谱。排查了三天,最后发现是畸变参数没给对。嗯,从那以后我再也不敢轻视畸变模型了。

畸变修正的数学表达:

// 径向畸变修正(常用3阶或5阶模型)
x_corrected = x * (1 + k1*r^2 + k2*r^4 + k3*r^6)
y_corrected = y * (1 + k1*r^2 + k2*r^4 + k3*r^6)

// 切向畸变修正
x_corrected = x + [2*p1*x*y + p2*(r^2 + 2*x^2)]
y_corrected = y + [p1*(r^2 + 2*y^2) + 2*p2*x*y]

// 其中 r^2 = x^2 + y^2
// k1, k2, k3 是径向畸变系数
// p1, p2 是切向畸变系数

避坑指南:我曾经见过有人用5阶径向畸变模型去标定手机摄像头,结果过拟合了。一般来说,3阶模型(k1, k2, k3)就够用。除非你用鱼眼镜头,否则别轻易加高阶项。

2.3 相机内参:相机的“身份证”

相机内参,描述的是相机内部的光学属性。说白了,就是告诉你“相机自己长什么样”。

内参矩阵通常写成这样:

// 相机内参矩阵 K
K = [fx   0   cx]
    [ 0   fy  cy]
    [ 0    0    1]

// fx, fy: 焦距(以像素为单位)
// cx, cy: 主点坐标(光轴与成像平面的交点)

这里有个细节要注意:fx 和 fy 通常不相等。为什么?因为像素不是正方形的。我遇到过不少新手,上来就假设 fx = fy,结果标定出来的结果根本不能用。

内参参数 物理含义 常见范围
fx 水平方向焦距(像素) 500 ~ 3000
fy 垂直方向焦距(像素) 500 ~ 3000
cx 主点x坐标 图像宽度的一半附近
cy 主点y坐标 图像高度的一半附近

我的习惯:拿到一个新相机,第一件事就是标定内参。别偷懒,这一步省了,后面所有精度都是空中楼阁。

2.4 相机外参:相机在世界中的“位置”

外参描述的是相机坐标系和世界坐标系之间的关系。说白了,就是“相机放在哪里,朝哪个方向看”。

外参由两部分组成:

  • 旋转矩阵 R:3x3 的正交矩阵,描述相机的朝向
  • 平移向量 t:3x1 的向量,描述相机的位置
// 外参变换公式
// 将世界坐标系中的点 P_w 转换到相机坐标系 P_c

P_c = R * P_w + t

// 或者用齐次坐标写成
[P_c] = [R  t] * [P_w]
[ 1 ]   [0  1]   [ 1 ]

你想想看,双目视觉的核心就是两个相机的外参关系——我们叫它“双目外参”。左相机和右相机之间的 R 和 t,决定了我们能不能算出深度来。

避坑指南:我曾经在室外做双目标定,风太大,三脚架晃了一下。结果外参标出来,左右相机的旋转差了0.5度。别小看这0.5度,在10米外,深度误差能到半米。所以,标定时的机械稳定性,比你想的重要得多。

2.5 世界坐标系到像素坐标的完整转换

好了,现在我们把所有东西串起来。一个世界坐标系中的点,是怎么变成像素坐标的?

一共四步:

  1. 世界 → 相机:用外参(R, t)把点从世界坐标系转到相机坐标系
  2. 相机 → 归一化平面:用针孔模型,把三维点投影到归一化平面(Z=1)
  3. 畸变修正:对归一化平面上的点做畸变修正
  4. 归一化平面 → 像素:用内参(fx, fy, cx, cy)把点映射到像素坐标
// 完整转换流程(伪代码)
function worldToPixel(P_world, K, R, t, distCoeffs):
    // 1. 世界坐标系 → 相机坐标系
    P_cam = R * P_world + t
    
    // 2. 投影到归一化平面
    x = P_cam.x / P_cam.z
    y = P_cam.y / P_cam.z
    
    // 3. 畸变修正
    r2 = x*x + y*y
    x_dist = x * (1 + k1*r2 + k2*r2*r2) + 2*p1*x*y + p2*(r2 + 2*x*x)
    y_dist = y * (1 + k1*r2 + k2*r2*r2) + p1*(r2 + 2*y*y) + 2*p2*x*y
    
    // 4. 归一化平面 → 像素坐标
    u = fx * x_dist + cx
    v = fy * y_dist + cy
    
    return (u, v)

核心要点:这四步是相机成像的“标准流程”。无论你用 OpenCV、MATLAB 还是自己手写代码,底层逻辑都是这个。搞懂了它,你就掌握了相机成像的“翻译官”技能。

我个人觉得,理解这个转换过程,比背公式重要得多。公式忘了可以查,但流程逻辑一旦刻在脑子里,遇到任何成像问题你都能快速定位——是外参不对?内参没标好?还是畸变模型选错了?

嗯,这一章的内容就到这里。记住:成像模型是双目视觉的“地基”,地基不稳,楼盖得再高也得塌。


专注资料整理