Align defaults with server-edition: reuse-as-seg and despill both OFF
Adopt server-edition's settings.py and default.yaml verbatim (zero diff on
these files between branches now):
- reuse_as_seg: false. The reuse equivalence holds for solid figures but
breaks on thin pale strokes over a low-contrast background: HR-matting
(trained on P3M-10k portraits + AM-2k animals) reads them as
semi-transparent wisps, and as the trimap topology source it destroys
white calligraphy (TestImage4: stroke alpha 0.92 -> 0.51, 48% of stroke
pixels below 0.5) that the DIS5K-trained segmenter keeps solid (seg 0.97
vs 0.40). As the gated cross-check veto the same signal stays harmless
(hue gate excludes neutral strokes; confidence gate exempts soft edges).
Opt in with --cross-check-as-seg for character batches.
- despill.enabled: false (mirrors server ba5d667): no positional/semantic
guard, desaturates and hue-shifts subjects sharing the background's hue.
README updated accordingly; the performance table notes its numbers were
measured with reuse on.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -25,9 +25,12 @@ Two pipelines, selected by `segmentation.enabled` in the config:
|
||||
`trust_remote_code`) or `anime-seg` (ONNX, tuned for anime characters). Switch at
|
||||
runtime with `--seg-backend anime-seg` (it also selects the matching weights).
|
||||
|
||||
Both share pymatting foreground estimation and a colour de-spill. There is **no**
|
||||
green-contamination rescue / recolour layer — with clean source images it is
|
||||
unnecessary, so it was removed.
|
||||
Both share pymatting foreground estimation and an optional colour de-spill
|
||||
(**off by default**: de-spill has no positional/semantic guard, so a subject
|
||||
sharing the background's hue — a blue suit on a blue backdrop — gets
|
||||
desaturated and hue-shifted; enable with `--despill` when edge spill genuinely
|
||||
matters). There is **no** green-contamination rescue / recolour layer — with
|
||||
clean source images it is unnecessary, so it was removed.
|
||||
|
||||
Both also run a **cross-model veto** by default: a second, trimap-free matting
|
||||
model (`ZhengPeng7/BiRefNet_HR-matting`) may only *lower* alpha, only on
|
||||
@@ -38,15 +41,17 @@ download (~0.9 GB) and one inference pass per image; disable with
|
||||
`--no-cross-check` (see the `cross_check` config section, and
|
||||
`docs/hair_gap_artifacts.md` for the analysis behind it).
|
||||
|
||||
When the cross-check is on, that same HR-matting forward is **reused as the
|
||||
segmentation mask by default**, skipping the primary seg model entirely (one
|
||||
less model to load, ~20 s faster per image on CPU). Pilot-validated
|
||||
(TestImage3 / FixImage1): trimap 99.8% identical, no structural change to
|
||||
fingers, hair wisps or thin lines. Disable with `--no-cross-check-as-seg` to
|
||||
run the dedicated seg model instead. The reuse is a same-family swap, so it
|
||||
applies to the `birefnet` backend only: with `--seg-backend anime-seg` the
|
||||
anime segmenter keeps its own forward and the cross-check veto still runs
|
||||
independently on top.
|
||||
When the cross-check is on, `--cross-check-as-seg` reuses that same HR-matting
|
||||
forward as the segmentation mask, skipping the primary seg model entirely (one
|
||||
less model to load, ~20 s faster per image on CPU). **Off by default** — use it
|
||||
only when the subjects are characters or similar solid figures. HR-matting is
|
||||
trained on portrait/animal matting (P3M-10k, AM-2k) and reads thin pale strokes
|
||||
on a low-contrast background as semi-transparent wisps: white calligraphy on
|
||||
the pastel backdrop (TestImage4) loses half its strokes under reuse, while the
|
||||
dedicated DIS5K-trained segmenter keeps them solid. The reuse is a same-family
|
||||
swap, so it applies to the `birefnet` backend only: with `--seg-backend
|
||||
anime-seg` the anime segmenter keeps its own forward and the cross-check veto
|
||||
still runs independently on top.
|
||||
|
||||
The segmentation trimap defaults to `directional` mode (chroma + seg + a hue-direction
|
||||
split: it keeps a background-coloured garment such as a white shirt while dropping a
|
||||
@@ -119,8 +124,9 @@ D:\MiniConda\envs\lightML\python.exe -m bgfilter.cli `
|
||||
|
||||
## Performance (CPU reference numbers)
|
||||
|
||||
Measured on a Ryzen 9700X (Zen 5, native bf16), 32 GB RAM, default settings
|
||||
(cross-check on, reused as seg; chunked global attention on):
|
||||
Measured on a Ryzen 9700X (Zen 5, native bf16), 32 GB RAM, with cross-check on
|
||||
and `--cross-check-as-seg` (reuse); chunked global attention on. Without reuse
|
||||
(the default) add ~9 s fp32 / ~6 s bf16 for the dedicated seg forward:
|
||||
|
||||
| input | precision | warm / image | peak memory |
|
||||
|---|---|---|---|
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ def main(
|
||||
fg_safe_radius_ratio: float | None = typer.Option(None, "--fg-safe-radius-ratio", min=0.0),
|
||||
despill: bool | None = typer.Option(None, "--despill/--no-despill"),
|
||||
cross_check: bool | None = typer.Option(None, "--cross-check/--no-cross-check", help="Second-opinion veto of background-hued residue between hair strands (default: on; costs one extra model inference)"),
|
||||
cross_check_as_seg: bool | None = typer.Option(None, "--cross-check-as-seg/--no-cross-check-as-seg", help="Reuse the cross-check forward as the segmentation mask, skipping the primary seg model (default: on; saves ~20s on CPU; needs cross-check on)"),
|
||||
cross_check_as_seg: bool | None = typer.Option(None, "--cross-check-as-seg/--no-cross-check-as-seg", help="Reuse the cross-check forward as the segmentation mask, skipping the primary seg model (default: off; saves ~20s on CPU; needs cross-check on)"),
|
||||
trimap_mode: str | None = typer.Option(None, "--trimap-mode", help="Trimap mode (segmentation pipeline): directional | seg | directional-hard-bg"),
|
||||
seg_backend: str | None = typer.Option(None, "--seg-backend", help="Segmentation backend: birefnet (default) | anime-seg"),
|
||||
) -> None:
|
||||
|
||||
+13
-7
@@ -84,12 +84,13 @@ class CrossCheckSettings:
|
||||
input_size: int = 2048
|
||||
# Reuse the cross-check forward as the segmentation mask: one HR-matting
|
||||
# inference serves both trimap topology and the second opinion, skipping the
|
||||
# primary seg model entirely (saves its load + forward). Pilot-validated on
|
||||
# TestImage3/FixImage1: trimap 99.8% identical, no structural change. Only
|
||||
# applies to the birefnet segmentation backend (a same-family swap); with
|
||||
# anime-seg the chosen segmenter keeps its own forward and the cross-check
|
||||
# runs independently. Needs cross_check and segmentation both enabled.
|
||||
reuse_as_seg: bool = True
|
||||
# primary seg model entirely (saves its load + forward; measured ~20 s/image
|
||||
# on CPU, ~0.9-1.3 GB VRAM on GPU). Pilot-validated on TestImage3/FixImage1:
|
||||
# trimap 99.8% identical. Default OFF on server-edition: the dedicated
|
||||
# segmenter keeps its own forward and the veto stays an independent signal;
|
||||
# opt in with --cross-check-as-seg. Only applies to the birefnet backend
|
||||
# (a same-family swap); needs cross_check and segmentation both enabled.
|
||||
reuse_as_seg: bool = False
|
||||
# Compute precision of the cross-check forward: "fp32" or "bf16" (autocast;
|
||||
# needs bf16-capable hardware, else falls back to fp32 with a warning).
|
||||
precision: str = "fp32"
|
||||
@@ -121,7 +122,12 @@ class ForegroundSettings:
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DespillSettings:
|
||||
enabled: bool = True
|
||||
# Off by default: despill pulls foreground chroma along the background-hue axis
|
||||
# with no positional or semantic guard, so a subject sharing the background's hue
|
||||
# (e.g. a blue suit on a blue backdrop) is desaturated/hue-shifted (~ΔE 35, blue
|
||||
# -> purple). This is the root default the cpu-fast lane inherits. Re-enable
|
||||
# per-config or with --despill only when edge spill genuinely matters.
|
||||
enabled: bool = False
|
||||
edge_low: float = 0.005
|
||||
strength: float = 0.92
|
||||
edge_expand_radius: int = 2
|
||||
|
||||
@@ -80,8 +80,9 @@ cross_check:
|
||||
# trimap 99.8% identical, no structural change (fingers/wisps/bowstring OK).
|
||||
# Only applies to the birefnet backend (same-family swap); anime-seg keeps
|
||||
# its own forward, with the cross-check running independently on top.
|
||||
# Disable with --no-cross-check-as-seg. No effect when cross_check is off.
|
||||
reuse_as_seg: true
|
||||
# Default OFF (server-edition): the dedicated segmenter keeps its own forward
|
||||
# and the veto stays independent; enable with --cross-check-as-seg.
|
||||
reuse_as_seg: false
|
||||
# Cross-check forward precision, same semantics as segmentation.precision
|
||||
# (bf16 via autocast: 2048 pass ~35s -> ~26s on a native-bf16 CPU).
|
||||
precision: fp32
|
||||
@@ -109,7 +110,10 @@ foreground:
|
||||
bg_confidence_weight: 0.70
|
||||
|
||||
despill:
|
||||
enabled: true
|
||||
# Off: despill has no positional/semantic guard, so a subject sharing the
|
||||
# background's hue (blue suit on a blue backdrop) gets desaturated/hue-shifted
|
||||
# (~ΔE 35, blue -> purple). Re-enable per-job only when edge spill matters.
|
||||
enabled: false
|
||||
edge_low: 0.005
|
||||
strength: 0.92
|
||||
edge_expand_radius: 2
|
||||
|
||||
Reference in New Issue
Block a user