fx 是像素焦距,它表示刻度(每弧度多少像素),无法表示弯曲针孔投影是一个「除法」:把 3D 点的位置除以它的深度,得到一个方向;再乘 fx,得到像素。 就这一步,没有别的。
u = fx · (X / Z) + cx v = fy · (Y / Z) + cy
fx 是像素焦距:它表示刻度,无法表示弯曲fx 是怎么来的
fx 不是凭空出现的数字,它是两个物理量相除得到的。
fx = 物理焦距 ÷ 像素尺寸
物理焦距用毫米;像素尺寸(一个像素有多大)也先换成毫米;相除之后,单位就变成了像素。
拖下面两个滑块,看 fx 怎么跟着变,它们算出来的 fx,就是演示 1 里那个 fx。
下面是一条由直线构成的走廊(44 条 3D 直线),正对着相机。 拖动两个滑块,看它们各自改变什么。
| 读数 | 含义 |
|---|---|
| 1) fx 像素焦距 | 焦距,但单位是像素(不是毫米)。等于「物理焦距 ÷ 像素尺寸」。 它的实际含义是:每转过 1 弧度,像点移动多少个像素,这就是「刻度」。 |
| 2) 水平视角 | 这颗镜头左右方向能看到的张角,单位是度。= 2 · atan( (画面宽 ÷ 2) ÷ fx )。注意:光有 fx 算不出视角,还得知道画面有多宽,同一颗镜头,只读出画面中间一块,视角就变小。 |
| 3) k1 畸变系数 | 它就是 半径 × (1 + k1 · 半径²) 里的那个 k1
在针孔那一步「除法」之后,额外加的一步非线性修正。k1 = 0 → 纯针孔,不弯;k1 < 0 → 边缘被往里吸,直线往外鼓(桶形);k1 > 0 → 边缘被往外推,直线往里凹(枕形)。 |
| 4) 投影后最大弯曲 | 把 44 条 3D 直线投到画面上之后,量「它还是不是直的」,单位是像素。 做法:每条线取两端和中点,投影后量中点偏离两端连线多少像素;44 条取最大值。 纯针孔下它精确等于 0(数学上直线必映成直线);一旦有畸变,它会跳到几百像素。 |
把 fx 从 250 拖到 6000,画面只是缩放,没有一条线弯。 因为针孔投影是一个「除法」,而除法把直线还是映成直线。
真正让线弯的是 k1,它加了一步:半径 × (1 + k1 · 半径²)。
这一步是非线性的,所以直线就弯了。
结论:fx 是像素焦距,它表示的是「刻度」,表示不了「弯曲」;
「弯曲」是 k1 的事。这是两个独立的旋钮。
r = f · tan(θ) 的现实含义
把相机剖开看:光心在下面那个白点,像平面是上面横着的那张。
一条与光轴夹角 θ 的光线,打在像平面上离中心 r = f · tan(θ) 的地方。
左边:白点是光心,光线从它射出;上方横着的那张是像平面。
每条光线打到像平面上的位置,就是那个橙点(旁边标了角度)。
像平面上还有两条轨迹线:把角度从 0° 连续加大时,落点扫过的路径。
琥珀色是针孔(很快跑到很远),绿色是等距(一直待在近处)。
右边:上下两行点是同一批角度(0°、10°、20°…),灰线把它们一一对应起来。横轴就是像平面上的位置。
上行(针孔):点越来越疏,每转过 10°,像点往外跑的距离越来越大。
这就是「画面边缘比中心敏感」的来源。
下行(等距):点间距几乎一样,角度均匀地映射到半径上("等距"这名字就是这么来的)。
但代价是:所有点都被压在左边,因为它是把 70° 硬塞进一段有限的像平面。
蓝色那条是一颗真实广角镜头的偏差曲线,它比理想的针孔把边缘"收"得更紧,这就是桶形畸变。 琥珀色那条是我们用多项式拟合出来的结果。 畸变参数要干的,就是这件事:拟合这条差曲线。
① 畸变参数不是一个"物理量",它是一组"拟合系数"。 它拟合的对象是「真实与理想的差」,不是图像,不是镜头,是那条差曲线。
② 为什么需要好几个参数?因为那条差曲线不是一个简单形状,只能用多项式一项一项去逼近。
③ 为什么不是越多越好?把「项数」从 1 拖到 3,盯着两个数字看:
范围内误差会一路变小(拟合能力更强了),范围外误差却一路变大。
因为高阶项在标定区间里几乎没有约束,出了区间就有很长的"杠杆"。
所以「标定范围」和「参数个数」是绑在一起的: 多项式的有效期,只在它被拟合的那段区间内。
这一节回答一个很实际的选型问题:手上这颗镜头,该用哪个模型?
能不能覆盖大角度,主要不取决于「有几个参数」,而取决于「多项式建在哪个变量上」。
tan θ 基 → 天花板 < 180°;θ 基 → 天花板 180°;
球面映射 → 超过 180°。
下面按「多项式(或映射)建在哪个变量上」分组 这是决定 FOV 天花板的那一件事,比「有几个参数」重要得多。 最后一列标出你会在哪儿遇到它。
| 模型 | 参数 | 建在 | 实用 FOV | 常见实现 / 说明 |
|---|---|---|---|---|
① tan θ 基:针孔 + 修正 → 天花板 < 180° | ||||
plumb_bob= radtan / Brown-Conrady |
k1 k2 p1 p2 k3 |
tan θ | ≈ 120° | ROS camera_calibration 默认、OpenCV CALIB_STANDARD、Kalibr pinhole-radtan、Colmap OPENCV、VINS / OpenVINS radtan |
rational_polynomial |
k1…k6 p1 p2 |
tan θ 有理式 |
≈ 165° | ROS rational_polynomial、OpenCV CALIB_RATIONAL。分母可能出奇点 |
thin_prism |
k1…k6 p1 p2 s1…s4 |
tan θ | ≈ 170° | OpenCV CALIB_THIN_PRISM、Colmap THIN_PRISM_FISHEYE。多 4 个薄棱镜项,治装配偏心 |
tilted |
基础模型参数 + tauX tauY |
tan θ | 同基础模型 | OpenCV 4.5+ CALIB_TILTED。显式建模传感器不垂直于光轴 |
② θ 基:鱼眼多项式 → 天花板 180°,再往外要加项 | ||||
equidistant= Aria「Spherical」 |
fx fy cx cy |
θ | 180° | Kalibr pinhole-equi、OpenVINS / VINS equidistant、Colmap SIMPLE_RADIAL_FISHEYE 的基。只有一个解析式 r = fθ,没有多余参数 |
fovDevernay–Faugeras |
fx fy cx cy + ω |
θ 解析 |
≈ 180° | Kalibr pinhole-fov、Colmap FOV。单个 ω 描述整个畸变场,极简 |
kannala_brandt= KB4 / OpenCV fisheye |
k1 k2 k3 k4 |
θ | ≤ 180° | OpenCV cv::fisheye、ROS fisheye、Kalibr kb4、Basalt KB4、Colmap OPENCV_FISHEYE。Foxglove 官方:支持到 180° |
| KB3 KannalaBrandtK3 |
k0 k1 k2 k3(9 阶) |
θ | ≤ 180° | Project Aria 的眼动相机用它。形式:r(θ) = θ + k0θ³ + k1θ⁵ + k2θ⁷ + k3θ⁹ |
fisheye62 |
k0 k1 k2 k3 p0 p1+ crit_theta(可选) |
θ + 切向 | > 180° | 就是 Project Aria 的 FisheyeRadTanThinPrism。Foxglove 明确写:超过 180° 就用它 |
fisheye624 |
6 径向 + 2 切向 + 4 薄棱镜 | θ + 更多项 | > 180° | Aria 的完整版;nerfstudio 直接叫它 FISHEYE624,Blender Cycles 也加了它 |
| ③ 球面 / 统一模型(Unified):把像面当成球的一部分 → > 180° | ||||
ucm= omni (Mei) |
ξ(+ 可选 k1 k2 p1 p2) |
球面 | ≈ 185° | Kalibr omni-radtan。只多 1 个参数就跨过 180°,性价比最高的一个 |
eucm |
α β |
球面 | > 180° | Kalibr eucm、Basalt EUCM。闭式反投影,工程上很好用 |
double_sphere= DS / DS+ |
ξ α |
双球面 | > 180° | Kalibr ds、Basalt DS。无三角函数,速度快 |
| OCamCalib | r(θ) 反函数多项式 + 仿射 c d e |
θ 的反函数 | > 200° | Scaramuzza。超大 FOV 的经典选择,但需要特制靶标 |
| ④ 非参数 / 全景 | ||||
| 稠密 LUT | 无参数 | — | 任意 | OpenCV initUndistortRectifyMap + remap。不需要模型,但存储大、不可解释、不能外推 |
| ERP / 等距柱状 | — | 球面 | 360° | 全景相机。已经不是「相机模型」而是「球面展开」 |
上面这张表最反直觉的一点是:一个模型能覆盖多广,主要不取决于它有「几个」参数, 而取决于它把多项式建在哪个变量上。
| 建在 | 这个变量最大能到多少 | 于是 FOV 天花板 |
|---|---|---|
tan θ | θ → 90° 时 趋向无穷 | 永远到不了 180° |
θ | 最大 π(= 180°),有界 | 正好 180° |
| 球面映射 / 反函数 | 有界且平滑 | 超过 180° |
所以「换个模型」的本质,不是「多加几个参数」,而是换一个在更大的 θ 范围里不发散的映射。
表里最漂亮的反例就是 ucm:只多 一个 参数 ξ,就把天花板从 180° 抬到了 185° 以上。
① 这里的 FOV 是「全视场角」(对角),不是半角。半角 = FOV ÷ 2。
180° 的全视场对应 90° 半角,正好是 tan 发散的地方。这就是那条黄色虚线的位置。
② 上面的「实用 FOV」是量级参考,不是硬规格。真正能标到多少还取决于镜头本身、靶标覆盖、标定质量。 「≈120°」的意思是再大就开始不划算了,不是「数学上不可能」。
③ 参数个数不决定覆盖面。ucm 只多 1 个参数就跨过了 180°;
而 rational_polynomial 有 8 个,仍然过不去。
决定因素是「建在哪个变量上」,不是「有几项」。
① fx 是像素焦距,它表示刻度、只决定看得多宽;弯曲它表示不了,那是 k1 的事。
② r = f·tan(θ) 是一条特定的曲线,角度不均匀地映射到像素;而且它在 90° 处发散。
③ 畸变参数是拟合系数,拟合「真实与理想的差」,只在标定范围内有效。
④ 能覆盖多广,看模型建在哪个变量上,不是看有几个参数。