本 Issue 是 proposal 总纲,不由 PR 直接 Closes。 具体交付另开收窄的 scoped issue(如 #352),由那条 issue 被 PR 关闭;本 Issue 的子项清单逐条勾掉后才关。分法与 #222 / #224 一致。
背景
组内在主仓 pipeline 上试生成,反馈角色忽大忽小、抠图有大块抠不干净。我们自己本地跑却很少遇到这么严重的问题。
查下来原因不是参数没调好,而是:本地那套实现比主仓多了整整几层后处理与自愈机制。本地那些"效果不错"的 GIF 是走完这几层之后的产物,拿它们当"管线现在的水平"是自欺——队友在主仓上跑的是没有这几层的版本。
差异清单(逐条对照代码)
| 能力 |
本地实现 |
主仓 ai_engine |
| 抠图模型 |
rembg + u2net |
u2netp(轻量版) |
| 去溢色 despill |
有 |
无 |
| 收缩 alpha 削白边 |
有 |
无 |
| 封闭白块抠透 |
有(近白判据) |
有同类,但判据不同 |
| 质检 + 坏帧自动重生成 |
有,最多 2 轮 |
完全没有 |
| 提示词 linter |
有 |
无 |
| VFX / 精修 |
有 |
无 |
| 脚线比例 |
FOOT_RATIO = 0.90 |
FOOT_LINE = 0.92 |
| 中间画布 |
1024 再缩到 256 |
直接出到目标尺寸 |
按价值排序
1. 质检 + 坏帧自动重生成(缺口最大)
本地的做法:生成一个动作后跑质检 → 被标记的帧单帧重生成,其它帧不动 → 重新对齐打包,最多 2 轮,仍不合格则保留最好的并记录警告。
质检有两层判据:
- 对齐漂移(纯 CV、零成本):跨帧脚底线与躯干中心的方差,按序列内相对离群判,不用绝对值(绝对值会被姿势差污染)
- 身份漂移(感知距离):每帧对母版的距离相对序列中位数判离群 + 相邻帧抖动
主仓这一层完全没有:一轮出完就交付,坏帧原样上线。这是"生成质量"最大的单点缺口——它让残次品能自愈,而不是靠人肉发现。
注意主仓已有 ports.ActionQuality(motion_scale / dead_frames / loop_seam),它只上报不处置。自愈要的是"据此重生成"那一步。
2. 抠图能力(直接对应"抠不干净")
主仓用 u2netp,分割质量本身弱于 u2net;而它唯一的补救是底色清理(matte._flat_bg_penalty),偏偏那条有个静默前提:四角标准差 > _BG_FLAT_STD = 8.0 就完全不清理、直接返回全 1。
实测(同一张母版加不同扰动):
| 背景 |
四角 std |
清理 |
| 纯色底(我们自己生成的母版) |
0.67 – 1.23 |
✓ 生效 |
| 高斯噪声 σ=10 |
10.07 |
✗ 整个跳过 |
| 横向渐变 25/255 |
12.81 |
✗ 整个跳过 |
这解释了"本地没遇到":我们的母版底色极纯,永远走"清理生效"那条路;队友用带渐变或噪点的参考图,清理静默失效,而 u2netp 对闭合区域(四足腿间、披风与身体围出的空隙)天然失灵,整块背景就留在产物里。
而且它不报错、不告警,产物照常交付。
3. 定标口径不一致
脚线 0.90 vs 0.92、中间画布 1024 vs 直出。两套口径并存意味着同一个角色在两边出来的构图不同,跨端对比会得出错误结论。
建议
分三个 PR,按上面顺序做,别一次全迁——每一层都要能单独验证效果:
- 质检 + 自动重生成(要先定"重生成"在主仓怎么落:单帧重生成需要 provider 支持按帧调用)
- 抠图升级:u2net + despill + 削白边;底色清理守卫触发时至少要上报,不能静默跳过
- 定标口径统一,并写清哪个是唯一真相源
验收
- 用同一个母版在主仓管线上跑,产出与本地实现的产物做同底对照(同一动作、同一帧数、同一画布)
- 抠图那条:构造一张非纯色底的母版,确认清理不再静默跳过
- 自愈那条:故意注入一帧坏帧,确认它被检出并重生成
Refs #171 #273
归入本 Issue 的子项(2026-08-17 按 #334 收敛)
以下原为独立 Issue,颗粒度过细,判据折进本清单后关闭,原文与实测数据仍可在各自 Issue 里查阅。
背景
组内在主仓 pipeline 上试生成,反馈角色忽大忽小、抠图有大块抠不干净。我们自己本地跑却很少遇到这么严重的问题。
查下来原因不是参数没调好,而是:本地那套实现比主仓多了整整几层后处理与自愈机制。本地那些"效果不错"的 GIF 是走完这几层之后的产物,拿它们当"管线现在的水平"是自欺——队友在主仓上跑的是没有这几层的版本。
差异清单(逐条对照代码)
ai_enginerembg+ u2netFOOT_RATIO = 0.90FOOT_LINE = 0.92按价值排序
1. 质检 + 坏帧自动重生成(缺口最大)
本地的做法:生成一个动作后跑质检 → 被标记的帧单帧重生成,其它帧不动 → 重新对齐打包,最多 2 轮,仍不合格则保留最好的并记录警告。
质检有两层判据:
主仓这一层完全没有:一轮出完就交付,坏帧原样上线。这是"生成质量"最大的单点缺口——它让残次品能自愈,而不是靠人肉发现。
注意主仓已有
ports.ActionQuality(motion_scale/dead_frames/loop_seam),它只上报不处置。自愈要的是"据此重生成"那一步。2. 抠图能力(直接对应"抠不干净")
主仓用 u2netp,分割质量本身弱于 u2net;而它唯一的补救是底色清理(
matte._flat_bg_penalty),偏偏那条有个静默前提:四角标准差 >_BG_FLAT_STD = 8.0就完全不清理、直接返回全 1。实测(同一张母版加不同扰动):
这解释了"本地没遇到":我们的母版底色极纯,永远走"清理生效"那条路;队友用带渐变或噪点的参考图,清理静默失效,而 u2netp 对闭合区域(四足腿间、披风与身体围出的空隙)天然失灵,整块背景就留在产物里。
而且它不报错、不告警,产物照常交付。
3. 定标口径不一致
脚线 0.90 vs 0.92、中间画布 1024 vs 直出。两套口径并存意味着同一个角色在两边出来的构图不同,跨端对比会得出错误结论。
建议
分三个 PR,按上面顺序做,别一次全迁——每一层都要能单独验证效果:
验收
Refs #171 #273
归入本 Issue 的子项(2026-08-17 按 #334 收敛)
以下原为独立 Issue,颗粒度过细,判据折进本清单后关闭,原文与实测数据仍可在各自 Issue 里查阅。
对齐锚点用包围盒中心,不是躯干中心(原 逐帧对齐锚在包围盒中心而非躯干中心,延展物摆动把身体反向推走(Refs #171 #21) #196)。剑、披风等延展物摆动会撑大包围盒,把身体朝反方向推走,播放时整体左右摆动肉眼可见。原 Issue 有 512 画布 16 帧走路的横向摆动幅度实测对照。注意与已合入的 fix(postprocess): 定标按本体跨度,不按包围盒 #280 不是同一处:fix(postprocess): 定标按本体跨度,不按包围盒 #280 改的是定标按本体跨度,本条是对齐锚点。
循环选帧取到半个步态周期(原 循环选帧取到半个步态周期,末帧接回首帧时左右腿互换(Refs #171) #197)。末帧接回首帧时左右腿前后互换。原 Issue 有两个角色各一次的两脚水平跨度序列。对症解法是网关 Fal 队列面的
first-last-frame-to-video(首尾帧给同一姿态即闭环),落点见 Proposal: 接入 Fal 队列协议面,并把协议表达为显式接缝 #332 实施顺序第 5 步。抠图的图像后处理应从 framework 迁到 ai_engine(原 抠图的图像后处理(112 行)应从 framework/providers 迁到 ai_engine(Refs #171) #212)。
providers/matte.py全文 205 行里约 112 行是图像后处理,与「用哪个抠图模型」无关。留在 provider 里的后果是换抠图实现时这些拿实测换来的修法会跟着丢掉。迁移时的阈值依据见原 Issue,不要顺手统一。自定义动作缺「循环播放」开关(原 feat(quick-start): 自定义动作补"循环播放"开关 #257)。
loop在应用流程里从未被填过,quick-start 没有任何控件能提供它,于是每个自定义动作实际都走loop = false。后端刻意不从描述文字猜循环性,猜错会让挥手被强行首尾闭环、末帧抽搐,而帧数时长成色全部正常、没有一道会红。全链路没有阶段计时(原 生成耗时 5-6 分钟但无法定位:全链路没有阶段计时 #316)。生成一个动作 5–6 分钟,慢在哪一步答不出来:
time.monotonic/perf_counter/elapsed在ai_engine与orchestrator/executor.py下 grep 零命中。进度上报只有阶段名与序号,没有耗时。角色头部被裁到画面外(原 角色头部被裁到画面外:补边只覆盖两个动作,且溢出裁切不区分部位 #317)。两个独立原因:
master_prep.prepare_master只给 attack 与 jump 补了顶部空间;以及溢出裁切不区分部位,裁掉哪里全看包围盒。自定义动作缺可行性与内容边界(原 自定义动作缺少可行性与内容边界:与角色形态矛盾的描述会直接进入付费生成 #318)。
prompt/lint.py只查形式(否定词、危险名词),不查描述与角色形态是否相容,也不拦不适合进产品的内容,两类都会直接进入付费生成。抠图把角色内部挖出透明洞(原 抠图把角色内部挖出透明洞:背景非纯色时两层清理同时静默失效 #319)。
matte.py的两个后处理层都依赖_bg_key(rgb)取底色,背景非纯色时同时静默失效,背景直接从人物内部透出。与上面「抠图能力」一节的四角 std 阈值是同一个机制。校验失败的 message 恒为通用文案(原 fix(web): 校验失败的 message 给出可读原因 #336)。
web/handler/exception_handlers.py的handle_request_validation_error写死「请求参数校验失败」,真实原因只进data。前端展示的是message,于是自定义动作缺custom_prompt时用户看到的是读不懂的通用文案,无法据此改正。改法:message取第一条校验错误的msg,剥掉 pydantic 的Value error,前缀。