11焦距与畸变

演示 1 · fx 是刻度 演示 2 · r = f·tan(θ) 演示 3 · 怎么拟合
这三句话要讲清
  1. fx 是像素焦距,它表示刻度(每弧度多少像素),无法表示弯曲
  2. r = f·tan(θ),角度和像素不成正比;而且 θ → 90° 时 r → ∞(这是现实含义)
  3. 畸变参数在「拟合」,拟合的是「真实与理想的差」;而且只在标定覆盖的范围内有效
先记住这一句

针孔投影是一个「除法」:把 3D 点的位置除以它的深度,得到一个方向;再乘 fx,得到像素。 就这一步,没有别的。

u = fx · (X / Z) + cx  v = fy · (Y / Z) + cy


一、fx 是像素焦距:它表示刻度,无法表示弯曲

先说 fx 是怎么来的

fx 不是凭空出现的数字,它是两个物理量相除得到的。

fx 的定义

fx = 物理焦距 ÷ 像素尺寸

物理焦距用毫米;像素尺寸(一个像素有多大)也先换成毫米;相除之后,单位就变成了像素。

拖下面两个滑块,看 fx 怎么跟着变,它们算出来的 fx,就是演示 1 里那个 fx。

然后是那条走廊

下面是一条由直线构成的走廊(44 条 3D 直线),正对着相机。 拖动两个滑块,看它们各自改变什么。

演示 1 · 两个旋钮 拖 fx → 只缩放 · 拖 k1 → 直线才弯 (四个读数的解释在下面)
相机看到的画面(1920 × 1080)
走廊的 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(θ) 的地方。

演示 2 · 光线落在像平面的哪里 鼠标拖动可旋转 · 滚轮缩放
相机横截面:白点是光心,上方横着的是像平面
同样一批角度(每 10°),在像平面上的落点
针孔 r = f · tan θ 等距 r = f · θ
这两张图怎么看

左边:白点是光心,光线从它射出;上方横着的那张是像平面。 每条光线打到像平面上的位置,就是那个橙点(旁边标了角度)。
像平面上还有两条轨迹线:把角度从 0° 连续加大时,落点扫过的路径。 琥珀色是针孔(很快跑到很远),绿色是等距(一直待在近处)。

右边:上下两行点是同一批角度(0°、10°、20°…),灰线把它们一一对应起来。横轴就是像平面上的位置。

上行(针孔):点越来越疏,每转过 10°,像点往外跑的距离越来越大。
这就是「画面边缘比中心敏感」的来源。

下行(等距):点间距几乎一样,角度均匀地映射到半径上("等距"这名字就是这么来的)。
但代价是:所有点都被压在左边,因为它是把 70° 硬塞进一段有限的像平面。

三、畸变参数在「拟合」什么

蓝色那条是一颗真实广角镜头的偏差曲线,它比理想的针孔把边缘"收"得更紧,这就是桶形畸变。 琥珀色那条是我们用多项式拟合出来的结果。 畸变参数要干的,就是这件事:拟合这条差曲线。

演示 3 · 拟合「真实与理想的差」 加一项 → 范围内更准,范围外更险
横轴 = 角度 θ · 纵轴 = 相对偏差(真实半径比理想半径小多少)
真实偏差 拟合曲线 标定采样点(带噪声) 标定覆盖范围
这个演示要你看到的

① 畸变参数不是一个"物理量",它是一组"拟合系数"。 它拟合的对象是「真实与理想的差」,不是图像,不是镜头,是那条差曲线。

② 为什么需要好几个参数?因为那条差曲线不是一个简单形状,只能用多项式一项一项去逼近。

③ 为什么不是越多越好?把「项数」从 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θ,没有多余参数
fov
Devernay–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 有「天花板」,看模型建在哪个变量上

上面这张表最反直觉的一点是:一个模型能覆盖多广,主要不取决于它有「几个」参数, 而取决于它把多项式建在哪个变量上。

建在这个变量最大能到多少于是 FOV 天花板
tan θθ → 90° 时 趋向无穷永远到不了 180°
θ最大 π(= 180°),有界正好 180°
球面映射 / 反函数有界且平滑超过 180°

所以「换个模型」的本质,不是「多加几个参数」,而是换一个在更大的 θ 范围里不发散的映射。 表里最漂亮的反例就是 ucm:只多 一个 参数 ξ,就把天花板从 180° 抬到了 185° 以上。

0°60°120°180°220°
① tan θ 基  →  天花板 < 180°
针孔 (linear)
≈100°
plumb_bob
≈120°
rational_polynomial
≈165°
thin_prism
≈170°
② θ 基  →  天花板 180°,再往外要加项
equidistant
180°
fov (Devernay)
≈180°
kannala_brandt / KB3
≤180°
fisheye62
>180°
fisheye624
>180°
③ 球面 / 统一模型  →  跨过 180°
ucm / omni
≈185°
eucm
>180°
double_sphere
>180°
OCamCalib
>200°
④ 非参数 / 全景
稠密 LUT
任意
三个必须说清的前提

① 这里的 FOV 是「全视场角」(对角),不是半角。半角 = FOV ÷ 2。 180° 的全视场对应 90° 半角,正好是 tan 发散的地方。这就是那条黄色虚线的位置。

② 上面的「实用 FOV」是量级参考,不是硬规格。真正能标到多少还取决于镜头本身、靶标覆盖、标定质量。 「≈120°」的意思是再大就开始不划算了,不是「数学上不可能」。

③ 参数个数不决定覆盖面。ucm 只多 1 个参数就跨过了 180°; 而 rational_polynomial 有 8 个,仍然过不去。 决定因素是「建在哪个变量上」,不是「有几项」。


三句话收尾

① fx 是像素焦距,它表示刻度、只决定看得多宽;弯曲它表示不了,那是 k1 的事。

② r = f·tan(θ) 是一条特定的曲线,角度不均匀地映射到像素;而且它在 90° 处发散。

③ 畸变参数是拟合系数,拟合「真实与理想的差」,只在标定范围内有效。

④ 能覆盖多广,看模型建在哪个变量上,不是看有几个参数。

← 上一页 ·  下一页 →
京ICP备19013239号