2. 针孔相机模型:成像原理、内参矩阵与畸变模型

说到相机标定,咱们得先搞清楚一件事:相机到底是怎么看到世界的?

你可能会觉得这问题太基础了。但说实话,我在做SLAM的初期,就因为对成像模型理解不够深,吃过不少亏。有一次标定出来的内参,怎么看怎么不对劲,折腾了两天才发现——我用的畸变模型跟相机实际的不匹配。

所以这一章,咱们把针孔相机模型彻底聊透。从原理到数学,再到实际工程中的坑,一步到位。

2.1 针孔成像:最简单的相机

针孔相机模型,说白了就是一个小黑盒,前面戳个针孔,后面放个感光平面。光线从物体上反射过来,穿过针孔,在后面的平面上形成一个倒立的像。

这个模型虽然简单,但它抓住了成像的本质:3D世界到2D图像的投影。

实际相机当然不是真的用针孔,而是用透镜。但透镜会引入畸变,这个我们后面再聊。先把这个理想模型吃透。

核心思想:针孔模型假设所有光线都通过一个点(光心),然后投影到成像平面上。这是一个线性模型,数学上非常干净。

2.2 从世界到像素:四个坐标系的变换

一个3D点怎么变成图像上的像素?中间要经过四次坐标变换。我习惯把这四个步骤记成一张流程图:

针孔相机成像流程:四个坐标系变换 世界坐标系 (Xw, Yw, Zw) 相机坐标系 (Xc, Yc, Zc) 归一化平面 (x, y, 1) 像素坐标系 (u, v) R, t 透视投影 内参矩阵 关键理解: • 外参(R, t)把世界点转到相机坐标系 • 透视投影(除以Z)把3D点压到归一化平面 • 内参矩阵把归一化坐标映射到像素坐标

2.3 内参矩阵:相机的"基因"

内参矩阵K,是相机最核心的参数。它描述了从归一化平面到像素坐标的映射关系。

数学形式长这样:

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

四个参数各司其职:

参数 含义 工程影响
fx x方向焦距(像素单位) 决定图像水平方向的缩放
fy y方向焦距(像素单位) 决定图像垂直方向的缩放
cx 主点x坐标(像素) 光轴与成像平面的交点
cy 主点y坐标(像素) 通常接近图像中心,但不完全

我的经验:fx和fy理论上应该相等(如果像素是正方形)。但实际中,很多相机的fx和fy会有微小差异。我遇到过一台工业相机,fx和fy差了0.3%,虽然不大,但在高精度测量场景下,这个差异必须标定出来。

2.4 畸变模型:现实总是不完美的

针孔模型是理想情况。但实际相机用了透镜,透镜就会引入畸变。说白了,就是图像变形了。

畸变主要分两类:

2.4.1 径向畸变

这是最常见的畸变。光线经过透镜边缘时,弯曲程度跟中心不一样,导致图像边缘的直线变弯。

  • 桶形畸变:图像向外膨胀,像桶一样。广角镜头常见。
  • 枕形畸变:图像向内收缩,像枕头。长焦镜头常见。

数学上用泰勒级数来建模:

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^2 = x^2 + y^2,k1、k2、k3 是径向畸变系数。

避坑指南:我曾经在标定一个鱼眼镜头时,只用了k1和k2两个参数,结果标定出来的图像边缘还是弯的。后来加上k3才搞定。对于视场角超过120°的镜头,建议至少用三个径向畸变参数。

2.4.2 切向畸变

这个畸变是因为透镜和成像平面不平行造成的。说白了,就是镜头装歪了。

数学形式:

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

2.5 完整的畸变校正流程

在实际工程中,我们拿到一张图像,第一步往往就是做畸变校正。流程如下:

  1. 获取原始像素坐标 (u, v)
  2. 反算到归一化平面:x = (u - cx)/fx, y = (v - cy)/fy
  3. 计算畸变量:用上面提到的径向和切向公式
  4. 得到校正后的归一化坐标
  5. 重新投影到像素坐标

代码实现大概长这样:

def undistort_point(u, v, K, dist_coeffs):
    fx, fy, cx, cy = K[0,0], K[1,1], K[0,2], K[1,2]
    k1, k2, p1, p2, k3 = dist_coeffs
    
    # 归一化坐标
    x = (u - cx) / fx
    y = (v - cy) / fy
    
    # 计算畸变
    r2 = x*x + y*y
    r4 = r2*r2
    r6 = r4*r2
    
    # 径向畸变
    k_radial = 1 + k1*r2 + k2*r4 + k3*r6
    x_rad = x * k_radial
    y_rad = y * k_radial
    
    # 切向畸变
    x_corr = x_rad + 2*p1*x*y + p2*(r2 + 2*x*x)
    y_corr = y_rad + p1*(r2 + 2*y*y) + 2*p2*x*y
    
    # 投影回像素
    u_corr = x_corr * fx + cx
    v_corr = y_corr * fy + cy
    
    return u_corr, v_corr

重要提醒:畸变校正通常是对整张图像做的,不是对单个点。OpenCV的cv2.undistort()函数就是干这个的。但如果你在做SLAM,只需要对特征点做校正,那就用上面的逐点方法,效率高得多。

2.6 工程中的常见问题

做标定和畸变校正时,有几个坑我踩过,分享给你:

问题 现象 解决方案
标定板图像太少 内参不稳定,每次标定结果差异大 至少拍15-20张不同角度的标定板图像
标定板占画面比例太小 畸变参数不准 让标定板覆盖画面1/3以上
忽略切向畸变 图像有梯形变形 工业相机一定要标定p1、p2
畸变模型选错 校正后边缘仍有弯曲 鱼眼镜头用专门模型,别用普通针孔

2.7 小结

针孔相机模型是整个多视角几何的基石。内参矩阵描述了相机的"基因",畸变模型修正了现实的不完美。理解透这两块,后面的立体匹配、三角化、BA优化才能走得稳。

嗯,这一章就到这里。记住:标定不是一次性的活,相机温度变化、镜头松动都会导致内参漂移。定期重新标定,是个好习惯。