1. 相机成像原理:从世界到像素的坐标之旅
做相机标定,首先得搞清楚一个问题:相机到底是怎么把三维世界变成一张二维照片的?
说白了,这就是个坐标变换的过程。我刚开始接触这个领域时,也被一堆坐标系绕得头晕。后来跟着项目做了几轮标定,才慢慢理清了这条线。今天咱们就把这条线彻底捋顺。
1.1 小孔成像模型——最朴素的相机
先回到最原始的相机形态。你想想看,一个暗箱,前面戳个小孔,光线透过小孔在后面的感光面上成像。这就是小孔成像模型。
核心特点:
- 所有光线都穿过同一个点——光心(小孔位置)
- 成像是倒立的,左右也颠倒
- 没有镜头畸变,理论上最纯净的成像
但现实中没有完美的针孔相机。小孔太小,进光量不够,曝光时间太长;小孔太大,图像又模糊。所以现代相机都用镜头来替代小孔,这就引出了针孔相机模型。
1.2 针孔相机模型——数学化的理想相机
针孔相机模型是对小孔成像的数学抽象。它假设相机是完美的,没有畸变,光轴垂直于成像平面。虽然现实相机做不到这么理想,但它是所有标定算法的基础。
我记得第一次用OpenCV做标定时,输出的内参矩阵就是基于这个模型算出来的。当时没理解透,结果标定出来的参数用在畸变校正上,图像边缘全是扭曲的。后来才意识到,模型假设和实际相机之间的差距,就是我们需要标定去弥补的。
我的经验:针孔模型是标定的"基准坐标系"。所有畸变参数、外参,都是在这个模型基础上做修正。理解了这个,后面学张正友标定法就轻松多了。
1.3 四个坐标系——坐标变换的链条
从三维世界到二维像素,中间要经过四次坐标变换。我习惯把这四个坐标系串成一条链:
| 坐标系 | 维度 | 单位 | 作用 |
|---|---|---|---|
| 世界坐标系 | 3D (Xw, Yw, Zw) | 物理单位(米/mm) | 描述物体在真实世界中的位置 |
| 相机坐标系 | 3D (Xc, Yc, Zc) | 物理单位(米/mm) | 以相机光心为原点,Z轴指向镜头前方 |
| 图像坐标系 | 2D (x, y) | 物理单位(米/mm) | 成像平面上的物理位置 |
| 像素坐标系 | 2D (u, v) | 像素(pixel) | 图像数组中的行列索引 |
这条链的变换顺序是:世界 → 相机 → 图像 → 像素。每一步都有对应的数学公式,咱们一个一个看。
1.3.1 世界坐标系 → 相机坐标系(外参变换)
世界坐标系是人为定义的。你可以把原点放在棋盘格的角点上,也可以放在房间的角落。相机坐标系则以光心为原点,Z轴沿着光轴方向。
从世界到相机,需要做一次刚体变换:旋转 + 平移。
// 数学形式
[Xc, Yc, Zc, 1]^T = [R | t] * [Xw, Yw, Zw, 1]^T
// 其中:
// R:3x3 旋转矩阵
// t:3x1 平移向量
// [R | t]:3x4 外参矩阵
这里的R和t,就是相机的外参。每个相机位置、角度不同,外参就不同。我在做多相机拼接项目时,需要标定每个相机相对于世界坐标系的外参,这一步如果标不准,后面拼接出来的全景图全是错位的。
1.3.2 相机坐标系 → 图像坐标系(投影变换)
这一步是三维到二维的投影,用的是相似三角形原理。
x = f * Xc / Zc
y = f * Yc / Zc
// f:焦距(物理单位)
// (x, y):图像坐标系下的坐标
注意这里有个除法——除以Zc。这就是为什么远处的物体看起来小,近处的看起来大。透视效应就体现在这里。
避坑指南:我曾经在写标定脚本时,把这一步的Zc当成了常数,结果算出来的投影点全偏了。记住,Zc是每个三维点对应的深度值,不是固定的。
1.3.3 图像坐标系 → 像素坐标系(离散化)
图像坐标系是连续的物理坐标,单位是毫米。像素坐标系是离散的整数坐标,单位是像素。这一步就是把物理位置映射到感光元件的像素格点上。
u = x / dx + u0
v = y / dy + v0
// dx, dy:每个像素的物理尺寸(mm/pixel)
// (u0, v0):主点坐标(光轴与成像平面的交点)
把上面三步合并,就得到了完整的投影公式:
s * [u, v, 1]^T = K * [R | t] * [Xw, Yw, Zw, 1]^T
// K:内参矩阵(3x3)
// [fx, 0, u0]
// [ 0, fy, v0]
// [ 0, 0, 1]
// fx = f/dx, fy = f/dy:以像素为单位的焦距
// s:尺度因子(等于Zc)
1.4 内参矩阵——相机的"身份证"
内参矩阵K包含了相机的固有属性:焦距、主点、像素尺寸。每个相机出厂时都不一样,而且会随着温度、震动等因素变化。这就是为什么我们需要标定。
| 参数 | 含义 | 典型值(举例) |
|---|---|---|
| fx | x方向焦距(像素) | 1200 ~ 2000 |
| fy | y方向焦距(像素) | 1200 ~ 2000 |
| u0 | 主点x坐标 | 图像宽度的一半 |
| v0 | 主点y坐标 | 图像高度的一半 |
我习惯把内参矩阵比作相机的"身份证"。每台相机都有唯一的内参,标定的核心任务之一就是把这个身份证号算出来。
1.5 知识结构总览
下面这张图把整个成像模型和坐标系变换串起来了。建议你保存下来,后面学标定算法时随时对照。
1.6 为什么需要标定?
搞清楚了成像模型,标定的意义就很清楚了:
- 内参未知:fx, fy, u0, v0 出厂时可能有,但精度不够,而且会变化
- 畸变存在:镜头不是完美的,径向畸变和切向畸变需要校正
- 外参需要:相机相对于世界坐标系的位置和姿态,需要从标定中获取
说白了,标定就是用已知的三维点(棋盘格角点)和对应的二维像素点,反推出相机的内参、外参和畸变参数。这是一个典型的"由果推因"的过程。
我的建议:刚开始学标定,不要急着写代码。先把这四个坐标系的关系画在纸上,用手算一遍投影过程。我当年就是这么干的,虽然慢,但后面写脚本时思路特别清晰。