6 Commits

Author SHA1 Message Date
lhk229 9a9084b3d6 Cut pipeline cost: seg-reuse, unified bf16 knob, eager mimalloc purge
Three optimizations from profiling the cross-check-dominated pipeline
(9700X CPU, all pilot-validated on TestImage3/FixImage1):

- Reuse the cross-check HR-matting@2048 forward as the segmentation mask
  (cross_check.reuse_as_seg, default ON; --no-cross-check-as-seg to opt
  out). Skips the BiRefNet@1024 load+forward entirely: ~66s -> ~45s,
  one less 0.9GB model. Trimap 99.8% identical, no structural change.

- --precision bf16 now fans out to all three models: ViTMatte keeps its
  weight cast; both BiRefNets run their forward under autocast with a
  dispatcher-level AutocastCPU fp32 shim for torchvision::deform_conv2d
  (no bf16 CPU kernel, no autocast wrapper upstream). Shared hardware
  gate in bgfilter/precision.py falls back to fp32 off native-bf16
  hardware. TestImage3: 51.9s -> 33.1s; alpha diff max 0.15, none >0.25.

- MIMALLOC_PURGE_DELAY=0 (bgfilter/__init__.py, before torch loads):
  Windows torch's bundled mimalloc lazily retains ~10GB of freed
  BiRefNet activations, stacking under ViTMatte's attention peak.
  2048x2048 bf16: peak 25.2 -> 21.4GB and slightly faster (73 -> 63s).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 15:15:12 +08:00
lhk229 8b574bc551 Switch model inference from no_grad to inference_mode
Both call sites convert outputs to numpy immediately, so the stricter
inference-mode tensors are safe; saves autograd view/version tracking
overhead. Verified end-to-end on CUDA (alpha finite, no NaN).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:29:13 +08:00
lhk229 7555321cfc Simplify to single-segmenter pipelines; add screen_color prior; drop rescue + recolor
Strategic pivot: the dropout-fill rescue, the anime-seg fill backend, the
BiRefNet+anime-seg intersection, and the recolor pass all existed to repair
source images whose hair was already green-contaminated or broken at generation
time. Fixing the source instead (clean pastel-background generation) makes the
matte high-contrast, so that whole compensation layer is unnecessary. Remove it.

- Two pipelines via segmentation.enabled:
    false -> chroma-only (Chroma + ViTMatte)
    true  -> single segmenter (default anime-seg, switchable birefnet) -> trimap
  then ViTMatte refines, pymatting estimates foreground, despill cleans spill.

- Generalise the green-hardcoded colour logic to a screen_color prior (hex, e.g.
  "#CFEFFF"; default null = green auto-detect, byte-identical). chroma keys off
  Lab/RGB distance to the colour; despill removes chroma along its Lab direction.
  Added --screen-color CLI flag.

- Removed dead code: fill_seg_dropouts + seg_fill*/intersection params,
  SegmentationSettings.fill_backend/fill_model_name/sharpen, unsharp_mask,
  recolor.py + RecolorSettings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 15:33:58 +08:00
lhk229 9805b51b99 Split trimap and dropout-fill segmentation backends
Add SegmentationSettings.fill_backend / fill_model_name so the dropout-fill can
use a different (higher-recall) segmenter than the trimap. Default is now the
combination the investigation converged on: BiRefNet builds the trimap (reliable
topology -- finger gap removed, strands kept as sure_fg), anime-seg drives the
fill (more, more-coherent anime wisps). Because the fill only adds (max), a
higher-recall-but-less-reliable mask there is harmless.

On TestImage2 this eliminates the strand breakage a full anime-seg pipeline
caused (lost 699 -> 0) while keeping ~91% of its extra wisp recall (gained
1133 -> 1031). Strands stay continuous; the recovered wisps are anime-seg's
coherent curves rather than BiRefNet's grainy fill.

Also adds an optional unsharp-mask knob (segmentation.sharpen, default off).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 11:17:46 +08:00
lhk229 08378e57f8 Add anime-seg segmentation backend (SkyTNT ISNet)
Add AnimeSegSegmenter (skytnt/anime-seg ISNet ONNX via onnxruntime, no remote
code) and a make_segmenter factory selected by SegmentationSettings.backend
("birefnet" | "anime-seg"). The ONNX output is already 0..1, so it slots into the
same soft-mask interface BiRefNetSegmenter uses.

On the anime samples anime-seg recovers more and more-coherent hair wisps than
BiRefNet (TestImage2 shoulder rescue: added px 1886 -> 3278, largest connected
component 147 -> 454), as expected from an anime-trained model. Default backend
stays birefnet.

Adds onnxruntime to requirements.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 23:58:22 +08:00
lhk229 a6a313d2d0 Add BiRefNet semantic segmentation module
Adds bgfilter/segmentation.py: BiRefNetSegmenter produces a soft subject mask
that defines topology -- it includes colour-contaminated foreground (green-tinted
hair) and excludes see-through holes (finger gaps), the distinction chroma/colour
cues cannot make. Validated on both samples: green hair -> foreground, finger gap
-> background, both correct.

Foundation of the semantic-guided redesign; the mask will drive trimap
construction next, with ViTMatte still refining soft edges.

- SegmentationSettings (ZhengPeng7/BiRefNet, 1024 input, fp32).
- Deps: torchvision, timm, einops, kornia. Weights load from HuggingFace via
  trust_remote_code; behind a firewall set HF_ENDPOINT=https://hf-mirror.com and
  bypass the proxy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 21:07:24 +08:00