Commit Graph

9 Commits

Author SHA1 Message Date
lhk229 c748fc22a4 Pipeline concurrent requests over a shared GPU with a narrowed lock
The single asyncio lock serialized the whole request (chroma -> seg ->
trimap -> ViTMatte -> cross-check -> foreground -> despill), so the GPU
sat idle ~70-80% of each request while the CPU stages ran. Replace it
with a thread pool that runs several requests concurrently, and move the
mutex down to just the GPU forwards: when one request steps off the GPU
to do CPU work, another steps on, keeping the device fed.

Two opt-in env knobs, both defaulting to the old strictly-serial
behaviour:
  * BGFILTER_CPU_WORKERS (pool size) attacks the CPU-bound regime -- with
    cross-check off, throughput went 0.91 -> 1.67 req/s (1.84x) at pool=8
    on a 4080S, vs a mere +6% from a second worker process, at half the
    VRAM (one model copy).
  * BGFILTER_GPU_CONCURRENCY caps how many forwards may co-schedule on the
    SMs. batch-1 underfills the GPU, so =2 recovers the SM overlap a lock
    forgoes: cross-check on went 0.83 -> 1.01 req/s, beating two worker
    processes (0.92) with one model copy (~19 GB vs 24 GB).

The CLI/single-thread path passes no lock and uses a nullcontext, so it
stays allocation- and contention-free. Concurrent output verified
identical (10x same-frame -> one hash). A per-worker admission semaphore
bounds in-flight decoded images to the pool size.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 12:47:50 +00:00
lhk229 c56e58affe Add complex-background matting mode to the service via background_mode
Backports master's non-flat matting (chroma.enabled: false + the hue-free
cross-check gate) into server-edition, and exposes it over HTTP without
surfacing the internal "chroma" wording: /remove-background gains a
background_mode form field (flat, default | complex). complex maps to
chroma disabled -- no colour key, segmentation alone drives the trimap and
every colour-keyed stage (auto-detect, hue split, chroma suppression,
despill) is bypassed. The cross-check veto still works in complex mode via
its second-opinion-confidence gate (cross_check.second_lo/hi) but defaults
OFF there (it costs the HR-matting forward); an explicit cross_check=on
re-enables it.

No new model weights: complex mode reuses the already-provisioned BiRefNet
seg + ViTMatte (+ optional HR-matting cross-check). Flat mode is unchanged
(bit-identical), and server-edition's own extras (cross_check.lock,
foreground.use_gpu CuPy path) are preserved -- the port is surgical, not a
copy of master's files.

- settings: ChromaSettings.enabled, CrossCheckSettings.second_lo/hi
- config: override_settings chroma passthrough
- despill/foreground: model=None safe guards (foreground keeps GPU path)
- alpha_post: cross_check_alpha hue-free gate when proj is None
- pipeline: _process_rgb complex branch (seg-only trimap, skip colour stages)
- service/app: process(chroma=), background_mode field, complex-defaults-off
  cross-check, X-BGFilter-Background-Mode header
- cli: --chroma/--no-chroma
- configs/docs: gpu.yaml + default.yaml + README/README_ZH

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 14:04:58 +08:00
lhk229 dbd061e119 Add cross_check lock so a locked lane ignores per-request cross_check=on
The cpu-fast lane disables cross-check to fit a low-memory box, but a request
could still send cross_check=on and override it, lazy-loading the HR-matting
model and running the 2048 forward -> the exact OOM (worker SIGKILL -> 502)
cpu-fast exists to avoid. BGFILTER_CROSS_CHECK=0 did not protect against this
because the per-request form field wins.

Add CrossCheckSettings.lock (default False). When set, PipelineManager
resolves cross-check to the base value and ignores per-request overrides, so a
locked off-lane silently honours cross_check=on as off -- no HR-matting load,
no cross-check run, no error (200), and the response header reports "off".
Enable it in cpu-fast.yaml (enabled: false, lock: true). Verified end-to-end:
cross_check=on on the locked lane returns without loading the cross-checker.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 10:26:38 +00:00
lhk229 4d34c96bed Add optional token auth to /remove-background
Adds app-level authentication gated on the BGFILTER_AUTH_TOKEN env var.
When set, /remove-background requires the X-Genarrative-Image-Token header
to match it (constant-time compare via hmac); missing or wrong -> 401. The
header name matches the production nginx layer, so callers only swap the
URL + token value, no interface change.

Fail-open by design: when BGFILTER_AUTH_TOKEN is unset the check is a no-op
(open), preserving current behaviour and leaving auth to an upstream proxy
(nginx in production). A startup WARNING is logged when auth is disabled.
/healthz stays unauthenticated for health checks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 08:26:04 +00:00
lhk229 ad445611ad Make cross-check and reuse-as-seg the service default
Cross-check + reuse-as-seg are already the config/CLI default; align the HTTP
service with them instead of forcing cross-check off. The earlier off-by-default
was to avoid the extra inference, but reuse-as-seg now makes that forward double
as the seg mask (birefnet backend) -- it replaces the primary seg model rather
than adding to it, so the cost concern is gone.

- app.py: honour the config default (on); BGFILTER_CROSS_CHECK still forces
  either way, unset = config.
- service.py preload: warm the cross-check HR-matting model, and skip the now-
  redundant primary segmenter when reuse covers segmentation.
- README / DEPLOY(_ZH): document cross-check as default-on, BiRefNet_HR-matting
  as required (not optional), BGFILTER_CROSS_CHECK=0 to disable.

Verified: service defaults cross_check on, env forces both ways, preload picks
cross-checker and skips the primary segmenter.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 16:11:43 +08:00
lhk229 8417653598 Add per-request cross_check toggle to the HTTP service (lazy-loaded)
POST /remove-background gains an optional cross_check form field
(1/0/true/false/yes/no/on/off; empty = service default). The effective
state is reported in the X-BGFilter-Cross-Check response header.

The cross-check model is now never preloaded at startup: the first
request that enables it pays the one-time load (measured ~8 s on top of
the ~50 s cc inference), after which the instance is cached and shared.

Verified with a functional test against PipelineManager: cc-off requests
never load the model, cc=1 lazy-loads then reuses the cached instance,
and omitting the field falls back to the service default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 22:34:08 +08:00
lhk229 11c591e5b5 Default the HTTP service to cross-check OFF (opt in via BGFILTER_CROSS_CHECK)
The cross-model veto adds one extra large-model inference per request
(measured ~35 s/image on CPU: 14.4 s -> 49.1 s), which is too slow as a
service default. The service now disables it regardless of the config
default; BGFILTER_CROSS_CHECK=1 re-enables it (GPU or quality-first
deployments). CLI behaviour is unchanged (still on by default).

Startup log and /healthz report the effective state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 20:57:39 +08:00
lhk229 578bd1f4d8 Merge master: matte-confidence gate + cross-model hair-gap veto
Brings in the two algorithm optimisations from master:
- Gate chroma suppression by the matte's own confidence (639fdf0)
- Add cross-model veto of hair-gap background residue (adbbb9d)

Server-edition integration on top of the plain merge:
- PipelineManager caches the cross-check model (BiRefNet_HR-matting) and
  injects it into MattingPipeline, so HTTP requests never reload it.
- MattingResult carries cross_check_alpha; debug output matches master.
- /healthz reports crossCheck; README lists models/BiRefNet_HR-matting
  in the local-weights bundle.

Verified: CLI --no-cross-check baseline and full cross-check run both
pass on samples/TestImage3.png; cross_check_alpha.png written to debug.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 20:37:07 +08:00
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