结论
framework/providers/matte.py 里混了两种性质不同的东西,其中约 112 行(全文 205 行)属于图像后处理,与"用哪个抠图模型"无关,应该迁到 ai_engine。
评审意见原话:「抠图调用逻辑可以放在这里,但有关后面的图像预处理算法,应该放在 ai_engine 处理流程中。」这条成立。
现状拆分(按行数实测)
| 性质 |
行数 |
内容 |
| 模型适配 |
71 |
u2netp 权重下载/缓存、onnxruntime 会话、预处理归一化常量、cutout 主流程 |
| 图像后处理 |
112 |
_flat_bg_penalty 键控清理、_fill_enclosed_holes 封闭空洞填充、_spread 连通域传播、_bg_key / _corner_pixels 取样 |
| 其它 |
22 |
import、模块 docstring |
为什么该搬
这些算法修的是显著性抠图模型的通病,不是 u2netp 特有的:
_flat_bg_penalty:显著性模型对闭合区域天然失灵(四足角色腿间的背景、盔甲缝隙会被判成主体)。换成 BiRefNet 或云端抠图同样需要。
_fill_enclosed_holes:修的是"主体内部透出背景"。成因既包括模型判错,也包括上面那个键控清理自己误杀的像素(实测浅肤色到灰底欧氏距离仅 31.3,窄于清理阈值 38,每帧误杀 820~2346 个像素)。
_spread:为上面两个服务的连通域传播原语。
留在 provider 里的实际后果:换抠图实现时这些修法会跟着丢掉,而它们是拿实测换来的。
迁移时必须一起搬的实测依据(不要顺手"统一"阈值)
这些阈值的窗口非常窄,是逐个试出来的:
- 锈橙色毛发 (222,130,70) 与洋红底 (222,41,124) 共享红通道,欧氏距离只有 104。 放宽 chroma 容差会把毛发先变成橄榄绿、再变亮绿。
_EDGE_SKIP = 2:视频帧最外一两行/列是编码器伪影不是底色。实测 9 段真 i2v × 16 帧 = 144 帧,贴边采样时 26 帧(18%)被判"底不均匀"而跳过清理;往里让 2px 后归零,而三张静态母版的取样中位色一个字节都没变。
_HOLE_BG_TOL = 14:纯背景色距 p99.9≈6.5、最大 11.1(压缩噪点),而被误杀区域的中位色距 ≥17.1,14 落在这条 1.5 倍间隙里。
- 空洞判定必须同时看连通性与颜色。 只按"不与画幅边界连通"判会把两腿之间填实——迈步相里两只靴子在下方交叠,把腿间空隙彻底封死。实测 121 帧中 80 帧存在这种封闭的底色空隙、共 25,173 px,朴素版会把它们全部填成主体(最惨单帧 3,172 px,两腿焊死)。
_bg_key 与键控清理必须用同一个取样口径,否则一个把某块当背景清掉、另一个又把它当主体填回来。
迁移方案(要拍板的点)
搬过去之后 MatteProvider 的契约会变:现在 cutout(bytes) -> bytes 是"抠图 + 后处理"一步到位,搬走后 provider 只做纯抠图,后处理由 ai_engine 显式调。
消费方是 ai_engine/strategy/concrete.py(VideoFrameStrategy 与 PerFrameStrategy 各一处 self._matte.cutout(...))。所以要定:
- 后处理放
ai_engine.postprocess 下的新模块,还是并入现有 postprocess.pack 那一层?
- strategy 里是每次
cutout 后手动调,还是包一层"抠图 + 后处理"的组合函数?
我倾向前者各自独立、strategy 显式调两步——链路里每一步都看得见,符合这层"出帧流水线"的写法。但这是 ai_engine 的内部结构,想听一下意见。
不在本 Issue 范围
- 换抠图实现本身(
MatteProvider 已是 Protocol、消费方按 Protocol 注入,换实现不需要动现有代码)
- 抠图算法本身的改进
相关
代码现在在 #179 / #181 的范围内。刻意不塞进那两个 PR:这条要动 ai_engine.postprocess 的接口,而 #180 / #181 正在评审,现在动会让三个 PR 互相牵扯、评审锚点再移一次。等那批合入后单独提。
结论
framework/providers/matte.py里混了两种性质不同的东西,其中约 112 行(全文 205 行)属于图像后处理,与"用哪个抠图模型"无关,应该迁到ai_engine。评审意见原话:「抠图调用逻辑可以放在这里,但有关后面的图像预处理算法,应该放在 ai_engine 处理流程中。」这条成立。
现状拆分(按行数实测)
cutout主流程_flat_bg_penalty键控清理、_fill_enclosed_holes封闭空洞填充、_spread连通域传播、_bg_key/_corner_pixels取样为什么该搬
这些算法修的是显著性抠图模型的通病,不是 u2netp 特有的:
_flat_bg_penalty:显著性模型对闭合区域天然失灵(四足角色腿间的背景、盔甲缝隙会被判成主体)。换成 BiRefNet 或云端抠图同样需要。_fill_enclosed_holes:修的是"主体内部透出背景"。成因既包括模型判错,也包括上面那个键控清理自己误杀的像素(实测浅肤色到灰底欧氏距离仅 31.3,窄于清理阈值 38,每帧误杀 820~2346 个像素)。_spread:为上面两个服务的连通域传播原语。留在 provider 里的实际后果:换抠图实现时这些修法会跟着丢掉,而它们是拿实测换来的。
迁移时必须一起搬的实测依据(不要顺手"统一"阈值)
这些阈值的窗口非常窄,是逐个试出来的:
_EDGE_SKIP = 2:视频帧最外一两行/列是编码器伪影不是底色。实测 9 段真 i2v × 16 帧 = 144 帧,贴边采样时 26 帧(18%)被判"底不均匀"而跳过清理;往里让 2px 后归零,而三张静态母版的取样中位色一个字节都没变。_HOLE_BG_TOL = 14:纯背景色距 p99.9≈6.5、最大 11.1(压缩噪点),而被误杀区域的中位色距 ≥17.1,14 落在这条 1.5 倍间隙里。_bg_key与键控清理必须用同一个取样口径,否则一个把某块当背景清掉、另一个又把它当主体填回来。迁移方案(要拍板的点)
搬过去之后
MatteProvider的契约会变:现在cutout(bytes) -> bytes是"抠图 + 后处理"一步到位,搬走后 provider 只做纯抠图,后处理由 ai_engine 显式调。消费方是
ai_engine/strategy/concrete.py(VideoFrameStrategy与PerFrameStrategy各一处self._matte.cutout(...))。所以要定:ai_engine.postprocess下的新模块,还是并入现有postprocess.pack那一层?cutout后手动调,还是包一层"抠图 + 后处理"的组合函数?我倾向前者各自独立、strategy 显式调两步——链路里每一步都看得见,符合这层"出帧流水线"的写法。但这是 ai_engine 的内部结构,想听一下意见。
不在本 Issue 范围
MatteProvider已是 Protocol、消费方按 Protocol 注入,换实现不需要动现有代码)相关
代码现在在 #179 / #181 的范围内。刻意不塞进那两个 PR:这条要动
ai_engine.postprocess的接口,而 #180 / #181 正在评审,现在动会让三个 PR 互相牵扯、评审锚点再移一次。等那批合入后单独提。