Files
BGfilter/docs/【服务化】BGfilter服务版设计与交付需求-2026-07-02.md
lhk229 d24c6ed608 Add FastAPI HTTP service for background removal
Wrap the BGfilter matting pipeline in a long-running FastAPI service
(POST /remove-background, GET /healthz) exposing only screen_color and
seg_model, matching the birefnet-service request contract.

- bgfilter/pipeline.py: add in-memory run_rgb entry returning a
  MattingResult; allow injecting a shared runner/segmenter. CLI file
  path (run_image) behaviour is unchanged.
- bgfilter/service.py: PipelineManager caches one shared ViTMatte runner
  and per-backend segmenters, so screen_color/seg_model never reload a
  model.
- app.py: thin FastAPI layer -- param validation, image decode/encode,
  error codes (400/413/500), timing headers, global asyncio lock.
- requirements.txt: fastapi, uvicorn[standard], python-multipart.
- README + design doc: HTTP service usage and HF-mirror/proxy/Xet
  download notes.

Verified end-to-end (base ViTMatte + BiRefNet + anime-seg): 11/11
acceptance checks pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 12:08:25 +00:00

12 KiB
Raw Permalink Blame History

BGfilter 服务版设计与交付需求

本文档给接手实现服务化的 Agent 使用。目标是在 BGfilter-server 仓库内,把现有 BGfilter 抠图算法包装成一个可长期运行的 Python HTTP 服务,接口形态参考当前线上 birefnet-service,但算法仍使用本仓库的 BGfilter 管线。

目标

新增一个服务入口,使调用方可以通过 HTTP 上传图片并得到透明背景 PNG。

服务首版只暴露两个业务参数:

  • screen_color:可选。背景色先验,格式为 #RRGGBB。不传或空值时沿用 BGfilter 默认行为,从图片边框自动检测纯色背景。
  • seg_model:可选。分割模型选择,允许值为 birefnet 或 anime-seg。不传时使用仓库当前默认值 birefnet。

其它算法参数首版不要暴露为 HTTP 参数,直接使用当前默认配置:

  • trimap.mode = directional
  • despill.enabled = true
  • model.matting_method = vitmatte
  • model.model_name = hustvl/vitmatte-base-composition-1k
  • model.device = cpu
  • segmentation.device = cpu
  • segmentation.input_size = 1024

非目标

  • 不把 BGfilter 算法移植到 Rust。
  • 不在 HTTP 接口里暴露 trimap_mode、despill、阈值、设备、模型权重名等高级参数。
  • 不复用当前线上 birefnet-service 的简单 alpha 直出算法;seg_model=birefnet 在本服务中只表示 BGfilter 管线里的语义分割后端。
  • 不在每次请求里加载并释放模型。
  • 不要求首版支持批量图片。

现有算法边界

BGfilter 当前核心入口在:

  • bgfilter/pipeline.py
  • bgfilter/settings.py
  • bgfilter/config.py
  • bgfilter/segmentation.py
  • bgfilter/vitmatte_infer.py

完整管线是:

输入 RGB
-> 背景色自动检测或使用 screen_color
-> chroma 背景置信度
-> 可选语义分割 mask,默认开启
-> trimap 生成,默认 directional
-> ViTMatte 预测 alpha
-> trimap 强约束
-> chroma-guided alpha suppression
-> alpha 小连通域/孔洞清理
-> pymatting foreground estimation
-> despill 去背景色溢色
-> 输出 RGBA PNG

当前默认分割后端已经是:

segmentation.backend = birefnet
segmentation.model_name = ZhengPeng7/BiRefNet

anime-seg 对应权重为:

segmentation.backend = anime-seg
segmentation.model_name = skytnt/anime-seg

bgfilter/config.py 里已有 _SEG_MODELS 和 override_settings(..., seg_backend=...),服务层应复用这套映射,不要让 HTTP 调用方传 model_name。

HTTP 接口契约

GET /healthz

返回 JSON,用于进程存活和基础配置检查。

建议响应:

{
  "ok": true,
  "service": "bgfilter",
  "version": "0.1.0",
  "defaultSegModel": "birefnet",
  "device": "cpu"
}

POST /remove-background

请求类型:

multipart/form-data

字段:

file: UploadFile, required
screen_color: string, optional, "#RRGGBB"
seg_model: string, optional, "birefnet" | "anime-seg"

响应:

200 image/png

响应体是透明背景 PNG。建议附带诊断 header:

X-BGFilter-Elapsed-Ms: 总耗时毫秒
X-BGFilter-Seg-Model: 实际使用的 seg_model
X-BGFilter-Screen-Color: 实际请求传入的 screen_color,未传可省略或设为 auto

错误码建议:

400 empty image
400 invalid image
400 invalid screen_color
400 invalid seg_model
413 image too large
500 inference failed

为兼容现有 Rust 调用方,file 字段名必须保持为 file。

参数默认与校验

screen_color:

  • 允许省略、空字符串、null 等价于自动检测。
  • 非空时必须匹配 #RRGGBB。
  • 大小写都可以,内部可以统一为大写或小写。
  • 校验失败返回 400 invalid screen_color。

seg_model:

  • 允许省略,默认 birefnet。
  • 允许值只包括 birefnet、anime-seg。
  • 可以兼容内部命名 seg_backend,但 HTTP 对外字段用 seg_model。
  • 校验失败返回 400 invalid seg_model。

MAX_IMAGE_PIXELS:

  • 建议参考线上 birefnet-service,通过环境变量控制,例如 BGFILTER_MAX_IMAGE_PIXELS。
  • 默认建议先设为 4194304,即约 4MP。
  • 超限返回 413 image too large。

模型加载与缓存要求

服务必须做到模型只加载一次或按后端首次懒加载一次,不能每次请求都重新加载模型。

现有 MattingPipeline 已经有实例级懒加载:

  • self._runner 缓存 ViTMatteRunner
  • self._segmenter 缓存当前 settings 下的 segmenter

但如果每次请求新建 MattingPipeline(settings),缓存会失效。因此服务版需要持有长期对象。

推荐实现一个 PipelineManager 或等价对象:

长期缓存:
  ViTMatteRunner: 按 model_name + device + matting_method 缓存
  Segmenter: 按 backend + model_name + device + input_size 缓存

请求级参数:
  screen_color: 只影响本次 chroma/despill,不影响模型缓存
  seg_model: 选择对应 segmenter;首次使用某后端时加载,之后复用

推荐行为:

服务启动:
  可预加载默认 birefnet + ViTMatte,也可懒加载。

第一次 seg_model=birefnet:
  加载 BiRefNet segmenter 和 ViTMatte。

后续 seg_model=birefnet:
  复用已有 BiRefNet segmenter 和 ViTMatte。

第一次 seg_model=anime-seg:
  加载 AnimeSeg segmenter,ViTMatte 继续复用。

后续 seg_model=anime-seg:
  复用已有 AnimeSeg segmenter 和 ViTMatte。

可接受的首版简化方案:

  • 长期缓存两个 MattingPipeline 实例:一个 birefnet,一个 anime-seg。
  • 每个实例内部复用自己的 _runner 和 _segmenter。
  • 如果这样做导致 ViTMatte 被加载两次,应优先改为共享 ViTMatteRunner,因为 ViTMatte 是两条分割后端共同使用的 matting 模型。

更推荐的实现是把 MattingPipeline 改造成可以注入共享 runner/segmenter,或新增服务专用 pipeline runner,避免重复加载 ViTMatte。

并发控制

首版建议采用全局 asyncio.Lock,与当前线上 birefnet-service 类似,保证同一进程内一次只跑一个推理请求。

原因:

  • BGfilter 管线包含 ViTMatte、segmenter、pymatting、OpenCV/NumPy 后处理,CPU 和内存压力较大。
  • CPU 默认部署时并发推理容易放大延迟和内存。
  • 后续确认资源充足后,再考虑按模型或设备拆锁。

如果使用 Uvicorn 多 worker,每个 worker 都会各自加载一份模型,内存会成倍增长。首版建议 --workers 1。

内存入口改造建议

当前 MattingPipeline.run_image(...) 主要面向 CLI 文件输入输出。服务可以先用临时文件桥接,但更推荐新增内存入口,减少磁盘 IO 和临时文件清理风险。

推荐新增内部函数:

run_rgb(rgb: np.ndarray, settings: PipelineSettings, resources/cache) -> result

返回:

corrected_rgb: np.ndarray uint8 HxWx3
alpha: np.ndarray float32 HxW, 0..1
metadata: dict

服务层再把它编码成 PNG:

RGBA = corrected_rgb + alpha
PIL.Image.save(..., format="PNG")

如果首版为了速度使用临时文件:

  • 临时目录应使用 tempfile.TemporaryDirectory()。
  • 文件名不要使用上传原始文件名。
  • 请求结束必须清理。
  • 不要把 debug 输出默认打开。

推荐文件结构

可以参考线上 birefnet-service 的简洁形态,在仓库根目录新增:

app.py
requirements.txt

也可以新增:

bgfilter/service.py

用于放 PipelineManager 和内存运行逻辑。app.py 保持薄入口。

推荐分层:

app.py
  FastAPI 路由、参数校验、图片 decode/encode、错误码、计时、锁

bgfilter/service.py
  PipelineManager、模型缓存、按请求组装 settings、调用算法管线

bgfilter/pipeline.py
  尽量保留原 CLI 兼容行为;必要时新增 run_rgb

依赖

现有算法依赖保留。服务化需要补充:

fastapi
uvicorn[standard]
python-multipart

现有 requirements.txt 中已经有:

opencv-python
pillow
numpy
scipy
scikit-image
pymatting
torch
torchvision
transformers
accelerate
safetensors
huggingface_hub
timm
einops
kornia
onnxruntime
tqdm
typer
rich
PyYAML

如果部署在无 GUI Linux 服务器,opencv-python-headless 通常比 opencv-python 更合适,但替换前需要确认现有本地脚本是否依赖 GUI 能力。

环境变量

建议支持:

BGFILTER_CONFIG=configs/default.yaml
BGFILTER_DEVICE=cpu
BGFILTER_MAX_IMAGE_PIXELS=4194304
BGFILTER_PRELOAD=1
HF_HOME=/opt/genarrative-image-host/model-cache/hf
HF_HUB_OFFLINE=1
TRANSFORMERS_OFFLINE=1
OMP_NUM_THREADS=4
MKL_NUM_THREADS=4

BGFILTER_DEVICE 应同时覆盖:

  • settings.model.device
  • settings.segmentation.device

如果机器没有 GPU,默认保持 cpu。

systemd 部署参考

参考当前线上 genarrative-birefnet.service,可以新增:

[Unit]
Description=Genarrative BGfilter background removal service
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
WorkingDirectory=/opt/genarrative-image-host/bgfilter-service
Environment=HF_HOME=/opt/genarrative-image-host/model-cache/hf
Environment=HF_HUB_OFFLINE=1
Environment=TRANSFORMERS_OFFLINE=1
Environment=BGFILTER_CONFIG=configs/default.yaml
Environment=BGFILTER_DEVICE=cpu
Environment=BGFILTER_MAX_IMAGE_PIXELS=4194304
Environment=BGFILTER_PRELOAD=1
Environment=OMP_NUM_THREADS=4
Environment=MKL_NUM_THREADS=4
ExecStart=/opt/genarrative-image-host/bgfilter-service/.venv/bin/uvicorn app:app --host 127.0.0.1 --port 18083 --workers 1
Restart=on-failure
RestartSec=5
MemoryMax=8G
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ReadWritePaths=/opt/genarrative-image-host

[Install]
WantedBy=multi-user.target

端口只是建议值。真正接入时由部署方决定。

调用示例

默认参数:

curl -sS \
  -F "file=@input.png" \
  http://127.0.0.1:18083/remove-background \
  -o output.png

指定背景色:

curl -sS \
  -F "file=@input.png" \
  -F "screen_color=#CFEFFF" \
  http://127.0.0.1:18083/remove-background \
  -o output.png

切换 Anime Seg:

curl -sS \
  -F "file=@input.png" \
  -F "seg_model=anime-seg" \
  http://127.0.0.1:18083/remove-background \
  -o output.png

指定背景色并切换 Anime Seg:

curl -sS \
  -F "file=@input.png" \
  -F "screen_color=#CFEFFF" \
  -F "seg_model=anime-seg" \
  http://127.0.0.1:18083/remove-background \
  -o output.png

与 Genarrative Rust 服务的接入预期

当前 Genarrative Rust BFF 已有远端抠图调用边界:

POST {GENARRATIVE_EDITOR_BACKGROUND_REMOVAL_BASE_URL}/remove-background
multipart field: file
response: image/png

BGfilter 服务应保持同样的 POST /remove-background 和 file 字段名。Rust 侧后续只需要在 multipart 中追加可选文本字段:

screen_color
seg_model

如果 Rust 侧暂时不传新字段,本服务仍应可用,并按默认 birefnet + 自动背景色 执行。

验收要求

实现完成后至少验证:

  1. GET /healthz 返回 200 和 JSON。
  2. POST /remove-background 只传 file 可返回 image/png。
  3. screen_color=#CFEFFF 可正常返回 image/png。
  4. seg_model=birefnet 可正常返回 image/png。
  5. seg_model=anime-seg 可正常返回 image/png。
  6. 非法 seg_model 返回 400。
  7. 非法 screen_color 返回 400。
  8. 空文件或非图片返回 400。
  9. 超过 BGFILTER_MAX_IMAGE_PIXELS 的图片返回 413。
  10. 同一进程连续请求同一 seg_model 时不会重复加载对应模型。
  11. 从 birefnet 切到 anime-seg 后,再切回 birefnet,已加载模型可复用。
  12. Uvicorn --workers 1 下内存不随请求次数持续增长。

建议在日志中打印模型加载事件,但不要每个请求打印完整配置或文件内容。日志中不要输出用户上传图片数据。

完成定义

交付时应包含:

  • 可运行的 FastAPI 服务入口。
  • 更新后的依赖文件。
  • 本地启动命令说明。
  • 至少一组 curl 示例。
  • 若新增内存入口,应保持原 CLI 文件入口不破坏。
  • 若为了服务化调整 pipeline,应保证默认 CLI 行为仍按当前配置工作。

完成后,不看内部代码也应能通过本文档的 HTTP 契约和 curl 示例直接调用服务。