9 月中旬,我们把 Seestar 的数据迁到无头 WBPP 流水线上跑。NGC 1491 是那批里帧数最多的一个目标:440 帧、10 秒一张,按常规流程叠一次就行。

结果叠出来的 master 不对。

现象:master 不像用了 440 帧

成片的问题不是“有点噪”,而是和输入规模对不上:星点稀疏、背景脏,看起来像只用了不到一半的数据。但 WBPP 的退出码是 0,日志最后也没有报错。

第一反应是参数问题。但在动参数之前,先回去看 WBPP 日志里的配准统计——这一步改变了后面的方向。

取证:先看配准数,而不是看参数

日志里能读到每一帧的注册结果。把“成功注册”的数量和输入帧数一除,结果是 42.3%——440 帧里只有不到一半真正进了叠加,其余被跳过,而且没有在流程层面报错。

这就解释了成片:不是参数没调好,是数据根本没进去。

追因:为什么注册会大面积失败

下一步是看这些帧为什么没注册上。

WBPP 在输入帧数达到 150 时,会自动改用 FastIntegration 这条快速配准路径。它的设计目标是大批量数据的处理速度,但在部分数据集上匹配会失败,失败的帧被直接跳过,流程继续。

NGC 1491 这批正好超过 150 帧,于是走了这条路。几十帧的小批量不会触发这条路径,所以这个行为一直没有暴露。

这里有一个容易误判的地方:日志里没有“错误”,只有“注册数少了”。如果只看退出码,或者只看 master 有没有生成,就会以为成功了。

验证:两版 master 的直接对比

NAS 上还留着两版 master:..._fastIntegration_autocrop.xisf 和分块处理后的 ..._RGB_autocrop.xisf。在同一份 autocrop 输出上统计每通道的 MAD(中位绝对偏差,乘 1.4826 作为噪声估计):

版本RGB
FastIntegration2.75e-52.41e-52.59e-5
分块 + 标准配准2.11e-51.81e-51.91e-5

同一目标、同一裁切,分块版每通道噪声低 23% 到 26%。这和“更多帧进入叠加”的预期一致,也把问题从“感觉不好看”变成了可复核的数字。

处理:按时间分块,每块走标准配准

修法的核心是别让单次 WBPP 的输入超过 150 帧:

  1. 把帧按拍摄时间排序,按不超过 140 帧切成若干块;
  2. 每块单独跑一次 WBPP,走标准 StarAlignment 路径;
  3. 收集各块 master,用 strip_cfa_xisf.py 抹掉 CFA 元数据——这些已经是叠加结果,不能再让下游按 Bayer 阵列处理;
  4. 再跑一次 WBPP,把各块 master 合成最终 master。

140 而不是 150,是留余量:分块边界、参考帧选择和重试都可能让实际输入数浮动。

这套流程固化成了 run_wbpp_chunked.sh,参数是 --chunk-size 140。脚本在跑之前把水冷拉到全速,结束后按快照恢复——分块把一次叠加拆成多次,总时长变长,散热不能按单次叠加估。

结果

两次实测:

目标帧数分块前配准率分块后
NGC 149144042.3%99.3%
IC 44340673.4%98.8%(401/406)

IC 443 那次在流水线文档里有完整记录:401/406 进入叠加,后面接 SPFC、MGC、SPCC,全流程约 16 分钟。

复盘

退出码不代表成功。 WBPP 丢帧不报错,流程照样结束。验收要看日志里的注册数,或者看 master 的实际噪声水平。

分块不是越多越好。 每块都要单独做参考帧选择、局部归一化和积分,块太多会让边界处的背景和噪声不一致。140 是在“不触发 FastIntegration”和“块数尽量少”之间取的。

合并前要处理元数据。 各块 master 如果还带着 CFA 标记,第二次 WBPP 会按 Bayer 阵列处理已经 debayer 过的数据,颜色直接错。

散热按最长连续时间准备。 分块后总时长变长,水冷和电源策略要提前拉满。

现在流水线里有一条硬规则:帧数超过 140 一律分块,不看单次结果好不好。WBPP 是黑盒,它会在你不知情的时候换算法;能做的不是调参数,是把输入控制在它不会换算法的范围内。