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:
2026-07-02 12:08:25 +00:00
parent cf0ceec496
commit d24c6ed608
6 changed files with 892 additions and 33 deletions
+49 -2
View File
@@ -127,6 +127,42 @@ qa_grid.png
metadata.json
```
## HTTP service
An HTTP wrapper (`app.py` + `bgfilter/service.py`) exposes the pipeline as a
long-running FastAPI service. Models load once and are reused across requests.
```powershell
D:\MiniConda\envs\lightML\python.exe -m uvicorn app:app `
--host 127.0.0.1 --port 18083 --workers 1
```
Two endpoints:
- `GET /healthz` — liveness/config JSON.
- `POST /remove-background` — `multipart/form-data`, returns `image/png`.
- `file` (required) — the source image. The field name is fixed as `file`.
- `screen_color` (optional) — `#RRGGBB` prior; omit/empty for auto-detect.
- `seg_model` (optional) — `birefnet` (default) or `anime-seg`.
```bash
# default (birefnet + auto background colour)
curl -sS -F "file=@input.png" \
http://127.0.0.1:18083/remove-background -o output.png
# explicit background colour + anime segmenter
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
```
Config comes from `BGFILTER_CONFIG` (defaults to `configs/default.yaml` when present);
`BGFILTER_DEVICE` overrides both model and segmentation device; `BGFILTER_MAX_IMAGE_PIXELS`
caps input size (default ~4MP → `413`); `BGFILTER_PRELOAD=1` loads the default models at
startup. Run a single worker (`--workers 1`) — each worker loads its own copy of the models.
## Quality Check
The quality checker measures alpha validity and edge spill on semi-transparent edge
@@ -153,11 +189,22 @@ D:\MiniConda\envs\lightML\python.exe scripts\smoke_samples.py `
## Notes
- `Samples/` and `Outputs/` are ignored by Git.
- ViTMatte and segmentation weights load from Hugging Face on first use. Behind a
firewall set `HF_ENDPOINT=https://hf-mirror.com` (and bypass a flaky local proxy).
- ViTMatte and segmentation weights load from Hugging Face on first use.
`anime-seg` (`skytnt/anime-seg`) is a plain ONNX download; `birefnet`
(`ZhengPeng7/BiRefNet`) ships custom modelling code so it needs
`trust_remote_code=True` plus `timm` / `einops` / `kornia`.
- Downloading the weights behind a firewall — the reliable combination is the
`hf-mirror.com` mirror **with the local proxy bypassed** and **Xet disabled**:
```powershell
$env:HF_ENDPOINT = "https://hf-mirror.com" # domestic mirror
$env:NO_PROXY = "*" # bypass the proxy; the mirror is direct
$env:HF_HUB_DISABLE_XET = "1" # these repos are Xet-backed; force classic HTTP
```
Two gotchas this avoids: (1) mirror + an overseas proxy makes the mirror's `resolve`
bounce back to `huggingface.co`, which recent `huggingface_hub` rejects with
`FileMetadataError`; (2) with `hf-xet` installed the Xet download path fails instantly.
Once the weights are cached, run offline with `HF_HUB_OFFLINE=1 TRANSFORMERS_OFFLINE=1`
(as the service does in production).
- Foreground colour estimation uses pymatting's `estimate_foreground_ml` to propagate
clean foreground colour into semi-transparent edges before de-spill. Set
`foreground.method: unmix` to fall back to the legacy heuristic.