# 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` 完整管线是: ```text 输入 RGB -> 背景色自动检测或使用 screen_color -> chroma 背景置信度 -> 可选语义分割 mask,默认开启 -> trimap 生成,默认 directional -> ViTMatte 预测 alpha -> trimap 强约束 -> chroma-guided alpha suppression -> alpha 小连通域/孔洞清理 -> pymatting foreground estimation -> despill 去背景色溢色 -> 输出 RGBA PNG ``` 当前默认分割后端已经是: ```text segmentation.backend = birefnet segmentation.model_name = ZhengPeng7/BiRefNet ``` `anime-seg` 对应权重为: ```text 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,用于进程存活和基础配置检查。 建议响应: ```json { "ok": true, "service": "bgfilter", "version": "0.1.0", "defaultSegModel": "birefnet", "device": "cpu" } ``` ### `POST /remove-background` 请求类型: ```text multipart/form-data ``` 字段: ```text file: UploadFile, required screen_color: string, optional, "#RRGGBB" seg_model: string, optional, "birefnet" | "anime-seg" ``` 响应: ```text 200 image/png ``` 响应体是透明背景 PNG。建议附带诊断 header: ```text X-BGFilter-Elapsed-Ms: 总耗时毫秒 X-BGFilter-Seg-Model: 实际使用的 seg_model X-BGFilter-Screen-Color: 实际请求传入的 screen_color,未传可省略或设为 auto ``` 错误码建议: ```text 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` 或等价对象: ```text 长期缓存: ViTMatteRunner: 按 model_name + device + matting_method 缓存 Segmenter: 按 backend + model_name + device + input_size 缓存 请求级参数: screen_color: 只影响本次 chroma/despill,不影响模型缓存 seg_model: 选择对应 segmenter;首次使用某后端时加载,之后复用 ``` 推荐行为: ```text 服务启动: 可预加载默认 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 和临时文件清理风险。 推荐新增内部函数: ```text run_rgb(rgb: np.ndarray, settings: PipelineSettings, resources/cache) -> result ``` 返回: ```text corrected_rgb: np.ndarray uint8 HxWx3 alpha: np.ndarray float32 HxW, 0..1 metadata: dict ``` 服务层再把它编码成 PNG: ```text RGBA = corrected_rgb + alpha PIL.Image.save(..., format="PNG") ``` 如果首版为了速度使用临时文件: - 临时目录应使用 `tempfile.TemporaryDirectory()`。 - 文件名不要使用上传原始文件名。 - 请求结束必须清理。 - 不要把 debug 输出默认打开。 ## 推荐文件结构 可以参考线上 `birefnet-service` 的简洁形态,在仓库根目录新增: ```text app.py requirements.txt ``` 也可以新增: ```text bgfilter/service.py ``` 用于放 `PipelineManager` 和内存运行逻辑。`app.py` 保持薄入口。 推荐分层: ```text app.py FastAPI 路由、参数校验、图片 decode/encode、错误码、计时、锁 bgfilter/service.py PipelineManager、模型缓存、按请求组装 settings、调用算法管线 bgfilter/pipeline.py 尽量保留原 CLI 兼容行为;必要时新增 run_rgb ``` ## 依赖 现有算法依赖保留。服务化需要补充: ```text fastapi uvicorn[standard] python-multipart ``` 现有 `requirements.txt` 中已经有: ```text 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 能力。 ## 环境变量 建议支持: ```text 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`,可以新增: ```ini [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 ``` 端口只是建议值。真正接入时由部署方决定。 ## 调用示例 默认参数: ```bash curl -sS \ -F "file=@input.png" \ http://127.0.0.1:18083/remove-background \ -o output.png ``` 指定背景色: ```bash curl -sS \ -F "file=@input.png" \ -F "screen_color=#CFEFFF" \ http://127.0.0.1:18083/remove-background \ -o output.png ``` 切换 Anime Seg: ```bash curl -sS \ -F "file=@input.png" \ -F "seg_model=anime-seg" \ http://127.0.0.1:18083/remove-background \ -o output.png ``` 指定背景色并切换 Anime Seg: ```bash 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 已有远端抠图调用边界: ```text POST {GENARRATIVE_EDITOR_BACKGROUND_REMOVAL_BASE_URL}/remove-background multipart field: file response: image/png ``` BGfilter 服务应保持同样的 `POST /remove-background` 和 `file` 字段名。Rust 侧后续只需要在 multipart 中追加可选文本字段: ```text 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 示例直接调用服务。