第2章:相机成像模型——从物理世界到像素坐标

做机器视觉的人,天天跟相机打交道。但说实话,很多人用了几年相机,对成像原理还是一知半解。我刚开始入行时也是这样——标定过了就行,管它什么模型不模型。直到有一次项目,相机换了个角度,标定结果就飘了,排查了整整两天才发现是畸变参数没设对。

从那以后,我养成了一个习惯:每次做标定前,先把相机模型在脑子里过一遍。说白了,你只有知道相机是怎么把三维世界变成二维图像的,才能理解标定到底在干什么。

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

针孔相机模型,是所有成像模型的基础。它假设光线通过一个小孔,在后面的成像平面上形成倒立的像。嗯,虽然真实相机用的是透镜,但数学上我们可以用这个模型来近似。

核心就四个坐标系之间的转换:

  • 世界坐标系(Xw, Yw, Zw)——物体在真实世界中的位置
  • 相机坐标系(Xc, Yc, Zc)——以相机光心为原点
  • 图像物理坐标系(x, y)——成像平面上的实际物理位置
  • 图像像素坐标系(u, v)——我们最终看到的像素位置

整个流程就是:世界点 → 刚体变换到相机坐标系 → 透视投影到成像平面 → 离散化为像素坐标。

关键公式(透视投影):

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

其中 f 是焦距,Zc 是物体到相机的深度。注意这里有个除法——深度越远,成像越小。这就是近大远小的数学本质。

我见过不少新手问:为什么标定板要放不同角度?其实就是为了让 Zc 有变化,这样才能解算出完整的模型参数。

2.2 内参矩阵——把物理坐标变成像素坐标

从图像物理坐标 (x, y) 到像素坐标 (u, v),需要一个转换矩阵。这就是内参矩阵 K:

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

参数含义:

参数 含义 单位
fx, fy 焦距在 x、y 方向的像素当量 像素
cx, cy 主点坐标(光轴与成像平面的交点) 像素

fx = f / dx,其中 dx 是每个像素的物理宽度。说白了,fx 告诉你:在距离相机 1 米处,一个像素对应多少毫米?

我的经验:cx、cy 理论上应该在图像正中心,但实际生产中往往有偏移。我曾经遇到一台相机,cx 偏了 15 个像素,结果手眼标定怎么都对不准。后来重新标定内参,问题就解决了。所以别迷信出厂参数,自己标一遍最靠谱。

2.3 畸变模型——真实世界不完美

针孔模型是理想情况。真实相机有透镜,透镜就有畸变。主要分两类:

径向畸变(Radial Distortion)

说白了就是图像边缘被拉伸或压缩。离中心越远,变形越明显。数学模型:

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 是点到主点的距离,k1、k2、k3 是径向畸变系数。一般 k1、k2 就够了,鱼眼镜头才需要 k3。

切向畸变(Tangential Distortion)

这个是因为透镜和成像平面不平行造成的。模型:

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 是切向畸变系数。

避坑指南:我曾经在一个项目中,标定结果看起来很好(重投影误差 0.05 像素),但实际测量就是不准。后来发现是畸变参数太多,过拟合了。对于普通工业相机,建议只标定 k1、k2、p1、p2 四个参数。k3 和更多高阶项反而会引入噪声。

2.4 HALCON 相机标定助手实操

理论讲完了,咱们上手。HALCON 的标定助手是我用过最顺手的工具之一。操作流程:

  1. 准备标定板——HALCON 支持多种标定板,最常用的是圆点阵列板。注意标定板要平整,不能有划痕。
  2. 采集图像——至少 10-15 张,覆盖不同角度、不同位置。我习惯让标定板出现在图像的四个角和中心,这样畸变参数才准确。
  3. 打开标定助手——在 HALCON 菜单栏找到 "Calibration" → "Calibration Assistant"。
  4. 加载标定板描述文件——.descr 文件,里面定义了标定板的物理尺寸和特征点布局。
  5. 读入图像——点击 "Load Images",选择刚才采集的图片。
  6. 提取特征点——助手会自动检测标定板上的圆点中心。检查一下有没有误检或漏检。
  7. 设置初始参数——输入相机型号、像元尺寸、焦距等。这些可以从相机手册里找到。
  8. 开始标定——点击 "Calibrate",等待计算完成。
  9. 检查结果——看重投影误差,一般小于 0.1 像素就算不错。如果大于 0.3 像素,说明有问题。

HALCON 标定核心代码片段:

* 创建标定数据模型
create_calib_data ('calibration_object', 'calibration_object', CalibDataID)

* 设置标定板描述文件
set_calib_data_calib_object (CalibDataID, 0, 'caltab.descr')

* 设置相机初始参数
set_calib_data_cam_param (CalibDataID, 0, 'area_scan_division', [0.012, 0, 0.008, 0, 0, 0, 0, 0])

* 添加标定图像
for i := 1 to 15 by 1
    read_image (Image, 'calib_' + i$'02d')
    find_calib_object (Image, CalibDataID, 0, 0, i, [], [])
endfor

* 执行标定
calibrate_cameras (CalibDataID, Errors)

* 获取标定结果
get_calib_data (CalibDataID, 'camera', 0, 'params', CameraParam)

2.5 本章知识结构图

下面这张图把相机成像的完整流程和标定要素串起来了。建议你保存下来,做项目时对照着看。

相机成像模型与标定知识结构图 相机成像模型与标定知识结构 世界坐标系 (Xw,Yw,Zw) 刚体变换 (R,T) 相机坐标系 (Xc,Yc,Zc) 透视投影 (x=f*Xc/Zc) 图像物理坐标 (x,y) 内参矩阵 K 像素坐标 (u,v) 畸变模型 径向畸变 (k1,k2,k3) 切向畸变 (p1,p2) 标定输出:内参矩阵 + 畸变系数 重投影误差 < 0.1 像素为合格 标定流程 ① 准备标定板 ② 采集 10-15 张图像 ③ 提取特征点 ④ 执行标定 & 验证

2.6 标定结果验证——别急着收工

标定完成后,我建议你做三件事验证:

  1. 看重投影误差——HALCON 会给出每个点的误差,平均值小于 0.1 像素算合格。如果某张图误差特别大,删掉重标。
  2. 看畸变矫正效果——用 change_radial_distortion_cam_par 生成无畸变图像,检查边缘直线是否变直了。
  3. 实际测量验证——放一个已知尺寸的物体,用标定结果测量,看误差是否在容忍范围内。

我曾经踩过的坑:有一次标定结果重投影误差只有 0.03 像素,我信心满满地去做手眼标定。结果机器人抓取总是偏 2mm。排查到最后发现,标定板本身有 0.1mm 的平面度误差。所以记住:标定板的精度决定了标定结果的上限。便宜标定板真的会害死人。

2.7 本章小结

相机成像模型,说白了就是回答一个问题:三维空间里的一个点,最终落在图像哪个像素上?

针孔模型给了我们数学框架,内参矩阵完成了物理到像素的转换,畸变模型修正了透镜带来的变形。HALCON 标定助手把这些都封装好了,但你要理解背后的原理,才能在出问题时知道从哪里下手。