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>
This commit is contained in:
@@ -0,0 +1,470 @@
|
||||
# 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 示例直接调用服务。
|
||||
Reference in New Issue
Block a user