现象
抠图会把角色身体中间的色块变透明 —— 放大看是背景直接从人物内部透出来。用户持续反馈。
根因:两层清理在同一个条件下同时失效
providers/matte.py 有两个后处理层,都依赖 _bg_key(rgb) 取到底色:
| 层 |
作用 |
_flat_bg_penalty |
按颜色做减法,把闭合空隙里残留的底色清掉 |
_fill_enclosed_holes |
把"被主体围住、且整块都不是底色"的透明连通域填回主体 |
后者正是为这个现象写的 —— 它的 docstring 明写:要解决的问题:u2netp 判错或键控误杀会在主体内部留下透明洞,放大看是背景直接透出来。
但 _bg_key 有一道前置门槛:
if float(corners.std(axis=0).max()) > _BG_FLAT_STD: # _BG_FLAT_STD = 8.0
return None
四角采样的标准差超过 8.0 就返回 None,于是:
_flat_bg_penalty 返回全 1 → 键控清理整段跳过
_fill_enclosed_holes 第一行就 return alpha → 补洞整段跳过
结果:u2net 判错留下的洞,没有任何一层会修。
为什么我们自己测不出来
我们用的母版背景是纯色,四角标准差实测在 0.67–1.23,远低于 8.0,所以两层清理每次都生效。
用户上传的角色图常带渐变、噪点、纹理背景 —— 一旦超过 8.0,两层同时静默失效。这是典型的"在我这儿是好的":不是概率问题,是输入分布不同。
这个门槛本身是对的,不该简单调大
_BG_FLAT_STD 存在的理由成立:底色不是纯色时,"哪块是背景"无从判断,硬清理会误杀主体像素。调大阈值只是把误杀的门槛推后,换来另一类坏结果。
真正的问题是:门槛不满足时没有降级路径,直接什么都不做。
建议方向
- 补洞与键控清理解耦。补洞的判据是"连通性 + 颜色"两条,其中颜色那条用于否决(腿间空隙整块是底色,实测中位色距 6.2,据此排除)。背景不是纯色时,颜色否决确实失效,但连通性那条仍然可用 —— 可以退化成更保守的策略(只填不与边界连通、且面积小于某阈值的洞),而不是完全不填。
- 失效要可见。当前是静默跳过,线上无从知道有多少次生成落进了这条路径。至少要有一个读数(背景是否被判为纯色),落进成色账本,这样才能回答"这个问题影响了多大比例的生成"。
- 先量分布再定方案。我们不知道用户实际输入里有多少比例超过 8.0。加读数(第 2 条)攒一批数据,再决定该做保守补洞还是换抠图模型。
验收
- 构造一组背景带渐变/噪点、且主体内部有洞的样本,确认修复后洞被填上而腿间空隙不被填实(后者是既有测试已覆盖的回归点,不能破)。
- 成色账本里能读到"本次生成的背景是否被判为纯色"。
相关
_fill_enclosed_holes 的 docstring 记着一条实测教训:只按连通性判会把两腿之间填实(迈步相里两只靴子交叠,把腿间空隙封成不与边界连通的区域)。任何改动都要保住这条。
现象
抠图会把角色身体中间的色块变透明 —— 放大看是背景直接从人物内部透出来。用户持续反馈。
根因:两层清理在同一个条件下同时失效
providers/matte.py有两个后处理层,都依赖_bg_key(rgb)取到底色:_flat_bg_penalty_fill_enclosed_holes后者正是为这个现象写的 —— 它的 docstring 明写:要解决的问题:u2netp 判错或键控误杀会在主体内部留下透明洞,放大看是背景直接透出来。
但
_bg_key有一道前置门槛:四角采样的标准差超过 8.0 就返回
None,于是:_flat_bg_penalty返回全 1 → 键控清理整段跳过_fill_enclosed_holes第一行就return alpha→ 补洞整段跳过结果:u2net 判错留下的洞,没有任何一层会修。
为什么我们自己测不出来
我们用的母版背景是纯色,四角标准差实测在 0.67–1.23,远低于 8.0,所以两层清理每次都生效。
用户上传的角色图常带渐变、噪点、纹理背景 —— 一旦超过 8.0,两层同时静默失效。这是典型的"在我这儿是好的":不是概率问题,是输入分布不同。
这个门槛本身是对的,不该简单调大
_BG_FLAT_STD存在的理由成立:底色不是纯色时,"哪块是背景"无从判断,硬清理会误杀主体像素。调大阈值只是把误杀的门槛推后,换来另一类坏结果。真正的问题是:门槛不满足时没有降级路径,直接什么都不做。
建议方向
验收
相关
_fill_enclosed_holes的 docstring 记着一条实测教训:只按连通性判会把两腿之间填实(迈步相里两只靴子交叠,把腿间空隙封成不与边界连通的区域)。任何改动都要保住这条。