Add cross-model veto of hair-gap background residue (default on)

Colour-drifted background trapped between hair strands defeats every
single-signal defence: the chroma key reads it as foreground (bgc ~0.07),
the segmenter backs it, ViTMatte rates it opaque, and post-hoc removal is
a proven dead end (it shreds the hair volume the same pixels belong to).
A second, trimap-free matting model (BiRefNet_HR-matting) is the only
tested model that separates this residue from the subject, so its opinion
is fused in as a veto: min-fusion that may only LOWER alpha, restricted to
the background-hued bright suspect zone (proj >= 3, L >= 45, feathered)
and gated by primary-alpha confidence (0.70 -> 0.95 ramp) so soft wisps
and dark hair are exempt by construction.

- settings/config/CLI: cross_check block, --cross-check/--no-cross-check
- alpha_post.cross_check_alpha after clean_alpha; second opinion reuses
  BiRefNetSegmenter; saved to debug as cross_check_alpha.png
- chroma.bg_hue_projection extracted and shared with the trimap
- docs: methodology.md (new), hair_gap_artifacts.md (investigation log)

Verified: cross-check ON reproduces the visually-reviewed B1gate
prototype byte-for-byte on TestImage3; --no-cross-check reproduces the
previous baseline byte-for-byte; pink-bg FixImage1 face untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-04 19:52:24 +08:00
parent 639fdf01b6
commit adbbb9d620
11 changed files with 474 additions and 15 deletions
+19
View File
@@ -63,6 +63,24 @@ class AlphaPostSettings:
suppress_raw_hi: float = 0.98
@dataclass(frozen=True)
class CrossCheckSettings:
# Cross-model veto: a trimap-free matting model gives a second opinion that
# may only LOWER alpha (min-fusion), only on background-hued, non-dark pixels
# (the suspect zone) where the primary result is confident (gate_lo->gate_hi
# alpha ramp). Clears colour-drifted background residue between hair strands
# that chroma, segmentation and the primary matte all read as foreground;
# already-soft wisps and dark hair are exempt by construction.
enabled: bool = True
model_name: str = "ZhengPeng7/BiRefNet_HR-matting"
input_size: int = 2048
proj_min: float = 3.0 # bg-hue projection above which a pixel is suspect
l_min: float = 45.0 # Lab lightness below which a pixel is exempt (dark hair)
feather_sigma: float = 2.0 # Gaussian feather of the zone boundary, in px
gate_lo: float = 0.70 # primary alpha below this -> fully exempt
gate_hi: float = 0.95 # primary alpha above this -> fully vetoable
@dataclass(frozen=True)
class ForegroundSettings:
enabled: bool = True
@@ -117,6 +135,7 @@ class PipelineSettings:
chroma: ChromaSettings = ChromaSettings()
trimap: TrimapSettings = TrimapSettings()
alpha_post: AlphaPostSettings = AlphaPostSettings()
cross_check: CrossCheckSettings = CrossCheckSettings()
foreground: ForegroundSettings = ForegroundSettings()
despill: DespillSettings = DespillSettings()
model: ModelSettings = ModelSettings()