feat(matte): 抠图 provider 加选择开关,让已合入的 BiRefNet 不再是死代码 - #823
feat(matte): 抠图 provider 加选择开关,让已合入的 BiRefNet 不再是死代码#823johnnyzhang-eng wants to merge 4 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Codecov Report❌ Patch coverage is
@@ Coverage Diff @@
## main #823 +/- ##
==========================================
- Coverage 86.47% 86.46% -0.02%
==========================================
Files 296 297 +1
Lines 22136 22162 +26
Branches 3356 3356
==========================================
+ Hits 19142 19162 +20
- Misses 2055 2061 +6
Partials 939 939
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 2 files with indirect coverage changes 🚀 New features to boost your workflow:
|
| from windup_framework.providers import make_matte_provider | ||
|
|
||
| matte = OnnxU2NetMatteProvider() | ||
| matte = make_matte_provider() |
There was a problem hiding this comment.
[P1] Keep the selected provider bindable during worker warmup
make_matte_provider() now returns BiRefNetMatteProvider when WINDUP_MATTE_PROVIDER=birefnet, but that class only implements cutout() and has no warmup() method. Consequently _warmup_local_inference() raises AttributeError before bind_matte(matte) runs; the exception is swallowed as a generic warmup failure, and the action, image, and view-sheet executors each lazily create their own BiRefNet instance instead. This defeats the shared-provider lifecycle and can load multiple 224MB/large-runtime sessions in the worker, making the documented opt-in path materially more likely to hit the memory limit. Give the selected provider a compatible warmup implementation (including its union u2net session), or make warmup conditional while still binding the one factory-created instance.
| logger = logging.getLogger("windup.matte.factory") | ||
|
|
||
| #: 环境变量名。取值 ``u2net``(默认) / ``birefnet``。 | ||
| ENV = "WINDUP_MATTE_PROVIDER" |
There was a problem hiding this comment.
这边涉及到配置名称,应该在配置文件示范中补充一下
|
Deployment failed for project windup with the following error: Learn More: https://vercel.com/huyan-s-projects1?upgradeToPro=build-rate-limit |
|
两条都处理了。 P1(warmup) —— 确认属实,已修。 已给 BiRefNet 补 新增一条用例走 配置名写进示范文件 —— 已补进 CI 原样命令:ruff 通过 / |
BiRefNetMatteProvider 在 #686 已合入,但四个调用点全部硬编码 OnnxU2NetMatteProvider(), 也没有开关 —— 代码在仓里,跑的仍是 u2net。 不能直接切:BiRefNet 单帧峰值实测 6.85GB,而生产 worker 上限 5GiB、宿主 7.7GB, 换过去必 OOM,表现是 worker 无声重启、任务卡在 RUNNING。它的输入写死 1024(喂 512 会 InvalidArgument),降输入省内存不成立;u2net 同样写死 320,提分辨率也不成立。 但组员本机 16GB 跑得起来,而组内要产出高质量素材。故同一份代码两种装配,不为它开分支: 分支一定会漂,而漂出来的"更好的管线"产出的素材,产品复现不出来。 默认是 u2net:忘配等于用得起的那个,而不是忘配就把生产打 OOM。取值拼错回落 u2net 并留 WARNING —— 一个拼错的环境变量不该让整个 worker 起不来,两个方向的代价不对称。 既有的两条抠图装配用例改的是打桩点(生产已走工厂),断言的意图未变。 Closes #822
bootstrap.worker 是 matte.warmup() 然后 bind_matte(matte),两句包在同一个 except Exception 里。BiRefNet 没有 warmup,第一句抛 AttributeError,bind_matte 就到不了 —— 三个 executor 各自惰性 new 一份,而它默认与 u2net 取并集、每份内部 再 new 一个 u2net,进程里 6 个 ONNX 会话。生产 worker 容器上限 5GiB,而本模型 单帧峰值 6.85GB:**这个 PR 想防的 OOM,恰恰会因为它自己的接线漏洞发生。** 表面上只有一条 "ONNX 预热失败" 的 WARNING,开发机上完全跑得通。 原有那条用例只断言 isinstance,碰不到 warmup;test_mq_worker 那条把工厂换成一个 自带 warmup 的桩,断言的是桩的能力。新用例走 make_matte_provider 取真 provider、 断言协议齐全,已用定点变异验证:去掉 warmup 后它会红。
评审指出新配置名没写进示范文件。写在既有的 WINDUP_MATTE_REFINE 旁边,并标明 "服务器上不要开" —— 单帧峰值 6.85GB 而 worker 容器上限 5GiB,这条不写清楚, 照着示范文件配的人会把 worker 打 OOM。
仓里有条守卫测试拦"填了不生效"的配置键:.env.example 里的键必须真被某个 BaseSettings 读到。这个开关走 os.environ —— 与旁边的 WINDUP_MATTE_REFINE 同类, 按同一方式登记。走 BaseSettings 的话 framework 的配置层就要认识 provider 的名字, 而选哪个 provider 是装配决定,不是配置数据。
3e94b56 to
46679b9
Compare
给抠图 provider 加一个选择开关,让 #686 合入的 BiRefNet 不再是死代码。
Why
BiRefNetMatteProvider在 #686 已经合入,但没有任何调用点实例化它 ——bootstrap/worker.py、orchestrator/executor.py(两处)、view_sheet_executor.py四处全部硬编码OnnxU2NetMatteProvider(),也没有开关。代码在仓里,实际跑的仍是 u2net。不能直接切过去:BiRefNet 单帧峰值实测 6.85GB,生产 worker 容器上限 5GiB、宿主 4 核 7.7GB,换过去必 OOM,而 OOM 的表现是 worker 无声重启、任务卡在 RUNNING。它的输入尺寸写死 1024(喂 512 会 InvalidArgument),"降输入省内存"不成立;u2net 同样写死 320,"提分辨率"也不成立。
但组员本机 16GB 跑得起来,而组内要产出高质量素材。质量差距是实测过的(同一帧同一判据):
为什么不开分支:分支一定会漂 —— 主线每改一处要么手工搬过去、要么两边越差越远;而"本地更好的管线"产出的素材如果产品复现不出来,那批素材就是孤儿。开关的代价只有一行配置。
Changes
providers/matte_factory.py:按WINDUP_MATTE_PROVIDER选 provider,默认u2net。Verification
uv run ruff check .:All checks passed。uv run python -m scripts.export_openapi:rc=0,openapi.json无漂移。uv run lint-imports:Contracts: 2 kept, 0 broken。uv run pytest -q --cov=packages:1824 passed, 14 skipped, 0 failed。test_warmup_injects_one_matte_into_both_executors、test_concurrent_first_requests_build_one_shared_provider_set)改的是打桩点(生产已走工厂),断言的意图未变 —— 它们仍在保证"预热实例要交给两个执行器"与"并发只建一套"。Scope
不改服务器默认行为,不改抠图算法本身。
服务器侧跑 BiRefNet 这条建议彻底放弃(内存硬墙,已量死)。面向用户的可能路径是搬到浏览器(#712)——我们已经为 3D 出帧做过一次同样的搬迁(#714/#717)。模型 fp16 114MB,且 WebGPU 原生支持 fp16(CPU 上 fp16 反而慢一倍,这个结论在浏览器里是反过来的)。浏览器侧的真实内存、耗时与不支持 WebGPU 的回退都还没验,不在本 PR 范围。
Closes #822