Add optional BiRefNet-gated intersection for the dropout-fill
Add seg_fill_intersect (default off) and seg_fill_gate_min to AlphaPostSettings, plus a gate_mask arg to fill_seg_dropouts. When enabled, the anime-seg dropout-fill is intersected with the reliable BiRefNet mask (>= gate_min): anime-seg proposes wisps at a low seg_fill_seg_min, but only rescues BiRefNet also endorses survive, trimming anime-seg's over-inclusive fringe while keeping its smooth value. Default off, so the shipped Option B (union) output is byte-identical. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+13
-1
@@ -88,7 +88,10 @@ def clean_alpha(
|
||||
|
||||
|
||||
def fill_seg_dropouts(
|
||||
alpha: np.ndarray, seg_mask: np.ndarray, settings: AlphaPostSettings
|
||||
alpha: np.ndarray,
|
||||
seg_mask: np.ndarray,
|
||||
settings: AlphaPostSettings,
|
||||
gate_mask: np.ndarray | None = None,
|
||||
) -> np.ndarray:
|
||||
"""Rescue alpha where ViTMatte dropped a region the semantic mask asserts.
|
||||
|
||||
@@ -98,12 +101,21 @@ def fill_seg_dropouts(
|
||||
foreground, restore a faint alpha from the mask. Limited to those dropouts so
|
||||
ViTMatte's sharper edges elsewhere are untouched, and gated by seg_min so the
|
||||
mask's faint halo and the finger gap (low mask) are not filled.
|
||||
|
||||
``gate_mask`` (when ``seg_fill_intersect`` is on) is a second, reliable mask
|
||||
-- BiRefNet -- required to also endorse the rescue (>= ``seg_fill_gate_min``).
|
||||
``seg_mask`` (anime-seg, high recall) then only proposes wisps; the
|
||||
intersection with BiRefNet drops its over-inclusive halo, while the rescued
|
||||
*value* still comes from anime-seg's smooth mask (no grainy BiRefNet value).
|
||||
"""
|
||||
if not settings.seg_fill:
|
||||
return alpha
|
||||
out = alpha.astype(np.float32).copy()
|
||||
rescued = settings.seg_fill_strength * np.clip(seg_mask, 0.0, 1.0)
|
||||
dropout = (out < settings.seg_fill_alpha_max) & (seg_mask >= settings.seg_fill_seg_min)
|
||||
# Intersection mode: keep only rescues the reliable mask also endorses.
|
||||
if settings.seg_fill_intersect and gate_mask is not None:
|
||||
dropout &= gate_mask >= settings.seg_fill_gate_min
|
||||
# The segmentation mask carries scattered low-confidence noise; rescuing it
|
||||
# verbatim sprinkles isolated specks (burrs) over the matte. Keep only
|
||||
# connected structures (genuine wisps), drop the isolated specks.
|
||||
|
||||
@@ -117,7 +117,11 @@ def _run_image(
|
||||
alpha = clean_alpha(alpha, trimap, settings.alpha_post)
|
||||
if seg_mask is not None:
|
||||
fill_mask = pipeline._segment_fill(rgb, seg_mask)
|
||||
alpha = fill_seg_dropouts(alpha, fill_mask, settings.alpha_post)
|
||||
# seg_mask is the reliable trimap backend (BiRefNet); it gates the fill
|
||||
# when seg_fill_intersect is on (fill_mask ∧ seg_mask).
|
||||
alpha = fill_seg_dropouts(
|
||||
alpha, fill_mask, settings.alpha_post, gate_mask=seg_mask
|
||||
)
|
||||
foreground = estimate_foreground_rgb(
|
||||
rgb, alpha, bg_confidence, model, settings.foreground
|
||||
)
|
||||
|
||||
@@ -49,6 +49,13 @@ class AlphaPostSettings:
|
||||
seg_fill_seg_min: float = 0.10
|
||||
seg_fill_strength: float = 0.70
|
||||
seg_fill_min_area_ratio: float = 0.00002 # drop rescued specks below this area
|
||||
# Intersection fill: gate the (anime-seg) dropout-fill by a second, reliable
|
||||
# mask (BiRefNet). anime-seg proposes freely at a low seg_fill_seg_min; only
|
||||
# rescues BiRefNet also endorses (>= seg_fill_gate_min, its threshold left
|
||||
# unchanged) survive. Trims anime-seg's over-inclusive halo to the structure
|
||||
# both backends agree on, without reintroducing BiRefNet's grainy value.
|
||||
seg_fill_intersect: bool = False
|
||||
seg_fill_gate_min: float = 0.10
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
||||
Reference in New Issue
Block a user