不变量排查法
什么是不变量
不变量(invariant):在任何一套正确的坐标约定下都必须成立的量。排查坐标约定 bug 的难处在于:你不知道哪一端错了——训练端、导出端、查看器端,各有各的约定。凡是依赖某一端约定的观察(比如”PSNR 高不高”)都不能当证据,因为它们可能在同一套错误约定下内部自洽。只有不依赖约定的量才能当裁判。
四条常用几何不变量
- 地平线位置。相机无横滚(roll)时,世界中的水平面在画面上的投影位置由俯仰角唯一决定,与轴向、原点约定无关。约定错(翻转、俯仰符号错),它立刻跑位。
- 铅垂线方向。世界中竖直的边(墙棱、门框竖边)在无横滚相机的画面里必须严格竖直。旋转写错(比如绕错轴、手性错),竖边先歪——这是比地平线更灵敏的旋转探针。
- 平行线汇聚。世界中一组平行线在透视投影下交于同一消失点(vanishing point),这是射影几何的定理,不因约定而变。轴交换类错误会把墙线”对折”,平行结构直接断掉。
- 计数守恒。高斯数、顶点数在任何刚体/相似变换前后一个不多一个不少。变换代码写错行列、读错字段偏移,往往先在计数上露馅——这是唯一一条不需要渲染就能查的不变量。
为什么这些量”不依赖约定”
前三条来自透视投影的结构性质:投影矩阵 K[R∣t] 无论内部参数 K、外参 R,t 的符号约定怎么变,只要是正确的同一物理相机,世界中的水平面、铅垂方向、平行关系在画面上的定性表现就不变。换句话说,错误约定破坏的恰恰是这些结构性质——所以它们天然适合做”约定对不对”的探针。
第四条来自变换的代数性质:刚体变换是双射,不可能改变点数。
使用时的坑
- 不变量判”错”很硬,判”对”不充分。四条全过不代表约定全对——比如绕竖直轴转 180°,地平线不动、铅垂线不歪(见案例 C)。不变量是用来证伪的,不是用来证明的。
- 先确认前提再套用。地平线判据要求相机无横滚、俯仰角已知;拿一张歪着脖子拍的照片去套”铅垂线必须竖直”,会冤枉好数据。
参考
判据设计:28° 手算
从不变量到判据
“地平线必须在画面上半部”是定性不变量,还不够用——翻转前后它都在半部里,只是一上一下。判据(criterion)要把不变量变成一行可算的数:给定相机参数,地平线应该落在第几行像素。
推导
针孔相机模型下,像素坐标 (u,v)(v 从画面顶部向下数)对应相机系方向:
(fxu−cx, fyv−cy, −1)
其中 fx,fy 是以像素为单位的焦距,(cx,cy) 是主点。设相机低头俯角 θ=28°、无横滚。世界是水平的,地平线对应”视线在相机系中相对光轴上抬 θ 角”的方向,即竖直分量满足:
fyvh−cy=−tanθ
解出地平线行号:
vh=cy−fytanθ
负号来自 v 轴朝下:往上看,行号减小。
代入数字
本数据集:1920×1080,OPENCV 模型无畸变,fy=935.3074,cy=540。
vh=540−935.3×tan28°=540−935.3×0.5317≈540−497≈43 px
低头 28°,地平线几乎贴着画面顶端——符合直觉:低得越多,地平线越高。
若图像被竖直翻转,行序颠倒,同一内容出现在:
vh′=1080−43=1037 px
43 对 1037,差了近 1000 行,肉眼一眼定案,不需要任何代码。
设计判据的要点
- 选区分度大的机位。俯角 28° 把地平线推到贴近边缘的极端位置,翻转前后差 H−2vh≈994 行。若相机平视(θ=0),地平线就在 cy=540,翻转前后都是 540,判据失效。判据要故意选让正确答案”偏心”的场景。
- 数字要能手算复核。tan28°≈0.5317 两位有效数字足够,误差几个像素不影响 43 vs 1037 的结论。判据的威力在于快,不在于精。
- 前提写在判据旁边。此判据成立需要:无横滚、cy=H/2(主点居中)、地平线在无穷远(走廊场景成立)。换了前提要重算。
参考
PSNR 为什么当不了判据
PSNR 在算什么
峰值信噪比 PSNR(peak signal-to-noise ratio)定义为:
PSNR=10log10MSEMAX2,MSE=N1∑i=1N(Ii−I^i)2
它只度量两张图逐像素的均方误差:渲染图 I^ 和参考图 I 在相同像素位置上差多少。它不含任何几何、任何结构信息——把两张图同步做任意重排,只要对应关系不变,PSNR 不变。
实测的翻转盲区
拿训练收敛的模型,分别对原图和垂直翻转后的图算 PSNR:
19.8 dBvs20.1 dB
差 0.3 dB,属于噪声级波动。上下颠倒这样毁灭性的约定错误,PSNR 完全分不出来——翻转后的图仍然是一张统计特征几乎一样的走廊照片,模型对两套数据的拟合优度相当。
根因:内部自洽 ≠ 外部正确
自渲染自评是一个闭环:用系统的约定渲染,再用同一套约定评估。系统和它自己永远一致——约定错得再离谱,只要输入数据和评估链路错得”配套”,指标照样漂亮。这类指标叫内部自洽(self-consistent)指标,它能回答”拟合得好不好”,回答不了”和世界对不对”。
类比:一块慢了 10 分钟的钟,和它自己对时永远精准。
判据必须有外部锚点
“约定对不对”是关于世界的命题,只能用不经过系统内部管线的信息来裁决:已知的相机俯角、世界的水平与铅垂、平行线结构。几何就是这些外部锚点中最硬的一类——它绕过渲染器、绕过 loss,直接质问数据本身。
容易踩的坑
- PSNR 不是没用,是用途不同。调参、选 checkpoint、比较同管线内的模型,PSNR 完全合格;它被禁用的场合只有一个:裁决跨系统的约定对错。
- loss 同罪。训练 loss 也是内部自洽指标,13.9 dB 的翻转模型 loss 照样稳步下降——“曲线漂亮”从来不是没 bug 的证据。
参考
案例A:Unity 采集端的轴向
c2w 的列就是相机三根轴
相机到世界矩阵 c2w(camera-to-world)的旋转部分 R 的三列,分别是相机系 x,y,z 轴在世界系中的方向。本项目(Unity 采集端,定案约定):
R=∣r∣∣−u∣∣b∣
- 第 1 列 r:right,相机右方
- 第 2 列 −u:注意是负 up,即天生朝下(down)
- 第 3 列 b:backward,相机后方(视线反方向)
很多引擎和图形学教材给的是 [r,u,f](OpenGL 式:up、forward)。一列之差,渲染翻天。
射线公式:两种等价写法
由 c2w 生成像素射线方向,因 v 原点约定有两个等价形式:
# v 从画面顶部数(标准图像/PNG 约定):
d ∝ r·(u−cx)/fx + down·((v−cy)/fy) + backward·(−1)
# v 从画面底部数(GL/ReadPixels 约定):
d ∝ r·(u−cx)/fx + down·(−(v−cy)/fy) + backward·(−1)
两式数学上描述同一台相机,差别只在 v 从哪头数。最后一项 −1:视线沿 backward 轴的负方向(即 forward)看出去。
符号错的两种指纹
把 [r,−u,b] 当成 [r,u,f] 读,两处负号各产生一种典型症状:
- up 差一个负号 → 画面上下颠倒。竖直方向的采样方向反了,等价于把图像竖直翻转。
- forward/backward 差一个负号 → 深度反排。射线朝相机背后发出,看到的内容深度顺序整个颠倒,近处变远处。
症状指纹:地平线跑位 + 铅垂线歪斜(两问定生死:低头机位地平线应在 v≈43 一侧;走廊竖边必须严格竖直)。
容易踩的坑
- “c2w 第 2 列是 up”是最常见的想当然。本约定里第 2 列是 down。读 transforms.json 之前先确认生成端用哪套列序,不要按教材默认值硬套。
- 轴向错和翻转错症状相似,病根不同。翻转(案例 B)是图像存储行序问题,所有帧一致地颠倒;轴向错是 c2w 矩阵列约定问题,会连带影响射线生成。判据能区分:纯翻转时 c2w 不变,铅垂线仍竖直;轴向错时铅垂线会歪。
参考
案例B:竖直翻转全案
病根:两个像素原点
同一块屏幕像素,两套读法:
- Unity
ReadPixels(OpenGL 约定):原点在左下,第 0 行是画面最底行。
- PNG 编码器 / 标准图像约定:原点在左上,第 0 行是最顶行。
采集端按左下原点写出,下游按左上原点读入——同一段像素,行序整个颠倒。1020 张训练图每一张都被竖直翻转,且翻转得非常一致,所以不崩、不报错,只是悄悄错。
为什么训练照跑却只有 13.9 dB
关键:翻转只发生在图像上,相机位姿没翻。每张图描绘的世界被竖直镜像了一次,而 c2w 还按原样指向——对优化器来说,这等价于每个相机都在看一个与自己位姿不自洽的世界。没有任何一个 3D 场景能同时满足所有 1020 个”图-位姿”对,高斯被互相矛盾的梯度来回拉扯:
- 训练不发散(矛盾是系统性的,不是噪声)
- 但收敛奇慢,且天花板极低:实测停在 13.9 dB
这就是”loss 曲线漂亮 ≠ 没问题”的实物案例——曲线下降只说明优化器在拟合它见到的数据,不说明数据自洽。
定罪与修复
地平线判据一锤定音:低头 28° 机位,渲染画面里地平线沉在 v≈1037(应为 v≈43,推导见判据设计一节)。行序颠倒实锤。
修复 = 写文件时把行序翻回来(或读入时翻),一行的事。修复后同管线训练到 30.2 dB,16 dB 的差距全部来自这一个原点约定。
为什么这条坑隐蔽
- 每张图单独看都正常。走廊场景上下大致对称(地/顶、左墙/右墙),翻转后的图人眼不细看分不出,PSNR 也分不出(19.8 vs 20.1 的盲区实测)。
- 错误一致性。如果只有部分帧翻转,loss 会剧烈震荡,反而好查;全员一致翻转才有”收敛但低分”的迷惑性。
排查启示
图像在系统边界(采集→文件→训练框架)上过一次手,就要问一次:这端的像素原点在哪? OpenGL/DirectX/Metal、各图像库、各框架张量布局的原点约定并不统一,逐端确认比事后查 16 dB 便宜得多。
参考
案例C:SuperSplat 绕 Z 转 180°
案情:交换链上的隐形旋转
SuperSplat 读入 ply 时默认绕 Z 轴转 180°。绕 Z 转半圈的旋转矩阵:
Rz(180°)=−1000−10001,(x,y,z)↦(−x,−y,z)
效果:画面左右、前后全反,模型像”旋转了对影”。
症状指纹:为什么它能躲过不变量
这是本集最阴险的一类错:
- 地平线不动——Z 轴(竖直方向)不变,水平面还是水平面
- 铅垂线不歪——竖直边还是竖直边
- 平行线结构不断——旋转是刚体变换,平行关系保持
前三条几何不变量全过,只有朝向反了:场景里该在左边的东西在右边。识别它要靠有方向性的内容(文字、已知布局),或和源数据直接比朝向。不变量证伪的盲区,正好由这个案例标出来。
修法:预补偿而不是硬扛
Rz(180°) 是对合(involution)——它是自己的逆:转两次半圈等于没转。所以不必改 SuperSplat,导出前先把坐标预转一次:
p_ss = (−x, −y, z) # 导出前预补偿
它进场自动再转一次:
(−x,−y,z)↦(−(−x),−(−y),z)=(x,y,z)
负负得正,恰好原位。两次半圈 = 原位,代数上就是 Rz(180°)2=I。
通用思路
交换工具(查看器、转换器、编辑器)的”默认动作”——自动旋转、自动缩放、自动换轴——都是可查证的确定行为。查到之后在导出端施加它的逆变换抵消,通常比改造工具或在加载端打补丁干净。这也是调试跨系统问题的分工原则:错在哪端查清,修在哪端便宜。
容易踩的坑
- 只补偿位置不够。绕 Z 转 180° 同样作用于每个高斯的朝向(旋转四元数)和 SH 方向性颜色。严格交换时要把旋转和 SH 一起共轭变换,否则椭球朝向和视角相关颜色会错——这正是”SH 随不随旋转”检查点存在的原因。
- 别凭症状猜轴。左右前后全反也可以是绕 Y 转 180° 加镜像的组合;确认是”绕 Z 半圈”要靠官方行为说明或最小样例实测,不是看画面感觉。
参考
七检查点总卡:逐条机理
清单与顺序
| # | 检查点 | 一行判据 |
|---|
| ① | 坐标手性 / 轴向 | 铅垂线必须竖直;c2w 三列 = right, −up, backward |
| ② | 图像原点 / 翻转 | 低头 28° → 地平线 v≈43;沉底 1037 = 翻转 |
| ③ | 像素中心 +0.5 | 半像素错位:细结构重影、边缘发虚 |
| ④ | 四元数归一化 | ∥q∥=1;模长偏离 → 形状畸变 |
| ⑤ | up 轴约定 | 顶/底原点对号入座,v 方向别串 |
| ⑥ | 单位 / 尺度 | 米 vs 厘米;场景尺度与相机距离同量纲 |
| ⑦ | SH 是否随旋转 | 换系后颜色漂移 = SH 没转 |
顺序不是随意的:前两条用肉眼不变量就能判,成本最低、命中率最高,先查;后面逐项深入数据内部。
各条机理速览
① 手性与轴向。c2w 列约定错,射线生成整体错位(见案例 A)。手性(chirality,左手系/右手系)错更隐蔽:坐标逐轴看着都对,但叉乘方向反了,旋转全变成镜像。
② 原点与翻转。ReadPixels 左下原点 vs PNG 左上原点,行序颠倒(见案例 B)。43 vs 1037 判据一行定案。
③ 像素中心 +0.5。像素 (i,j) 的几何中心在 (i+0.5, j+0.5),不在整数格点。生成射线或光栅化时漏掉这半个像素,整个采样网格平移 0.5 px——对低频内容无伤,对细结构就是系统性重影、边缘发虚。这类错不会让 PSNR 崩掉,只会让所有图像略糊,极易被当成”模型能力不够”。
⑥ 单位与尺度。一端按米、一端按厘米,场景尺度差 100 倍:相机平移量、高斯尺度、近平面/远平面全部失配。判据是看量纲关系——相机到场景的典型距离和场景尺寸必须同量纲;渲染出来”一切极小/极大、裁切诡异”先查这条。注意有些训练框架会自动重定向(含缩放)数据,导出模型时要逆变换回原坐标系,否则尺度和朝向一起被换。
④⑤⑦ 的机理(本集只给判据):
- ④ 四元数归一化:3DGS 协方差 Σ=R(q)diag(s2)R(q)⊤ 假设 R 正交,即 ∥q∥=1。模长偏离 1 会乘进每个轴长(s 实际被缩放到 ∥q∥2s 量级),椭球形状整体畸变。判据:扫描 ply 里 rot 字段模长分布。
- ⑤ up 轴约定:不同系统默认 Y-up / Z-up,导入导出时 v 方向容易串。判据:对照源数据的竖直方向渲染一条铅垂线。
- ⑦ SH 随不随旋转:颜色存在球谐 SH(spherical harmonics)系数里,是方向相关量。坐标系整体旋转时,SH 系数必须乘对应的 SH 旋转矩阵同步变换,否则视角相关颜色整体漂移(“换系必漂色”)。判据:换系前后绕场景环视,比较颜色是否随视角正确变化。
使用守则
按序过一遍,每项只认自己的一行几何判据;全程禁用 PSNR/loss 当”约定对不对”的证据——它们管拟合好坏,不管世界对错(19.8 vs 20.1 的翻转盲区在案)。