Skip to content

feat(matte): 抠图 provider 加选择开关,让已合入的 BiRefNet 不再是死代码 - #823

Open
johnnyzhang-eng wants to merge 4 commits into
mainfrom
feat/matte-provider-switch
Open

feat(matte): 抠图 provider 加选择开关,让已合入的 BiRefNet 不再是死代码#823
johnnyzhang-eng wants to merge 4 commits into
mainfrom
feat/matte-provider-switch

Conversation

@johnnyzhang-eng

Copy link
Copy Markdown
Contributor

给抠图 provider 加一个选择开关,让 #686 合入的 BiRefNet 不再是死代码。

Why

BiRefNetMatteProvider#686 已经合入,但没有任何调用点实例化它 —— bootstrap/worker.pyorchestrator/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 跑得起来,而组内要产出高质量素材。质量差距是实测过的(同一帧同一判据):

方案 IoU 丢主体 内部非实心
u2net 0.9769 228 5541
BiRefNet 0.9668 1845 106
两者并集 0.9764 53 91

为什么不开分支:分支一定会漂 —— 主线每改一处要么手工搬过去、要么两边越差越远;而"本地更好的管线"产出的素材如果产品复现不出来,那批素材就是孤儿。开关的代价只有一行配置。

Changes

  • 新增 providers/matte_factory.py:按 WINDUP_MATTE_PROVIDER 选 provider,默认 u2net
  • 四个调用点改为走同一个工厂,不再有硬编码。
  • 取值拼错时回落 u2net 并留 WARNING,不抛错 —— 一个拼错的环境变量不该让整个 worker 起不来,两个方向的代价不对称。
WINDUP_MATTE_PROVIDER=u2net      # 默认,服务器用(不设也是这个)
WINDUP_MATTE_PROVIDER=birefnet   # 组员本地用

Verification

  • 4 条新用例:默认值是 u2net(拦"默认给成 BiRefNet 会 OOM worker")、拼错回落(拦"拼错让 worker 起不来")、显式传参优先于环境变量、BiRefNet 能按名字造出来(拦 feat(matte): 引入 BiRefNet 抠图 provider #686 那种"合进仓里却没路径能选到")。最后一条只断言类型不真跑推理 —— 权重 224MB,CI 上不该下载,而它要证明的是接线通了
  • 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=packages1824 passed, 14 skipped, 0 failed
  • 既有两条抠图装配用例(test_warmup_injects_one_matte_into_both_executorstest_concurrent_first_requests_build_one_shared_provider_set)改的是打桩点(生产已走工厂),断言的意图未变 —— 它们仍在保证"预热实例要交给两个执行器"与"并发只建一套"。

Scope

不改服务器默认行为,不改抠图算法本身。

服务器侧跑 BiRefNet 这条建议彻底放弃(内存硬墙,已量死)。面向用户的可能路径是搬到浏览器(#712)——我们已经为 3D 出帧做过一次同样的搬迁(#714/#717)。模型 fp16 114MB,且 WebGPU 原生支持 fp16(CPU 上 fp16 反而慢一倍,这个结论在浏览器里是反过来的)。浏览器侧的真实内存、耗时与不支持 WebGPU 的回退都还没验,不在本 PR 范围。

Closes #822

@vercel

vercel Bot commented Aug 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
windup Ignored Ignored Preview Aug 27, 2026 10:25am

@codecov

codecov Bot commented Aug 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.47059% with 8 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...k/src/windup_framework/providers/matte_birefnet.py 14.28% 6 Missing ⚠️
...dup_app/server/orchestrator/view_sheet_executor.py 0.00% 2 Missing ⚠️

Impacted file tree graph

@@            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              
Flag Coverage Δ
backend 91.40% <76.47%> (-0.04%) ⬇️
frontend 80.52% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...nd/packages/app/src/windup_app/bootstrap/worker.py 37.64% <100.00%> (ø)
...app/src/windup_app/server/orchestrator/executor.py 77.53% <100.00%> (ø)
...amework/src/windup_framework/providers/__init__.py 100.00% <100.00%> (ø)
...rk/src/windup_framework/providers/matte_factory.py 100.00% <100.00%> (ø)
...dup_app/server/orchestrator/view_sheet_executor.py 88.09% <0.00%> (ø)
...k/src/windup_framework/providers/matte_birefnet.py 84.12% <14.28%> (-8.74%) ⬇️

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

审阅了 provider 选择、worker 预热、三个 executor 的惰性装配及新增覆盖用例。默认/回落/显式选择逻辑本身符合 PR 约定,git diff --check 与变更文件的 Python 编译检查也通过;但 BiRefNet 的预热契约存在阻断性问题,见内联意见。由于当前环境没有 uv,未能运行声明的 pytest 命令。

from windup_framework.providers import make_matte_provider

matte = OnnxU2NetMatteProvider()
matte = make_matte_provider()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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"

@xiaocheny214 xiaocheny214 Aug 27, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这边涉及到配置名称,应该在配置文件示范中补充一下

@vercel

vercel Bot commented Aug 27, 2026

Copy link
Copy Markdown

Deployment failed for project windup with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/huyan-s-projects1?upgradeToPro=build-rate-limit

@johnnyzhang-eng

Copy link
Copy Markdown
Contributor Author

两条都处理了。

P1(warmup) —— 确认属实,已修。BiRefNetMatteProvider 没有 warmup()bootstrap.workermatte.warmup() 抛 AttributeError 后 bind_matte 到不了,三个 executor 各自惰性 new 一份;而它默认与 u2net 取并集、每份内部再 new 一个 u2net,进程里 6 个 ONNX 会话。生产 worker 上限 5GiB、本模型单帧峰值 6.85GB —— 这个 PR 想防的 OOM,恰恰会因为它自己的接线漏洞发生

已给 BiRefNet 补 warmup(),并集那一路的 u2net 一并预热(它每帧都要跑,留到首帧再装等于两次冷启动叠在一起)。

新增一条用例走 make_matte_provider provider、断言协议齐全,已用定点变异验证:去掉 warmup 后它会红。原有那条只断言 isinstance,碰不到 warmup;test_mq_worker 那条把工厂换成一个自带 warmup 的桩,断言的是桩的能力 —— 这就是它没被拦住的原因。

配置名写进示范文件 —— 已补进 .env.example,写在既有的 WINDUP_MATTE_REFINE 旁边,并标明「服务器上不要开」(6.85GB vs 5GiB 上限)。不写清楚的话,照着示范文件配的人会把 worker 打 OOM。

CI 原样命令:ruff 通过 / openapi.json 无漂移 / lint-imports 2 kept 0 broken / pytest 1825 passed, 0 failed

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 是装配决定,不是配置数据。
@johnnyzhang-eng
johnnyzhang-eng force-pushed the feat/matte-provider-switch branch from 3e94b56 to 46679b9 Compare August 27, 2026 10:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(matte): 抠图 provider 加选择开关,让已合入的 BiRefNet 不再是死代码

2 participants