Carve background green embedded in the foreground (hole handling)

The green in the finger gap survives keying because it is dark and desaturated
(brightness ~0.58, hue-shifted), so chroma confidence reads ~0 and the trimap
leaves it in the unknown band, where ViTMatte fills the pocket as foreground.
It is also connected to the exterior screen (an open notch), so neither
confidence suppression nor classic hole-filling removes it.

Add carve_background_holes (doc section 10): a pixel that is clearly
green-dominant in the input *and* mostly surrounded by genuine (non-green)
foreground is set to alpha 0. The surround cue separates such an intrusion
(finger gaps, inter-strand pockets) from the outer hair edge, which has
background on one side and is preserved. Runs after alpha cleanup.

On TestImage the finger-gap survivors (green & alpha>0.5) drop 170 -> 28 with
no visible loss of hair wisps; inter-strand green pockets clear too. Knobs in
AlphaPostSettings (hole_*, default on, surround_min 0.4).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-30 18:36:43 +08:00
parent d6cf6ffe0c
commit f7e12d3498
4 changed files with 50 additions and 1 deletions
+7 -1
View File
@@ -6,7 +6,12 @@ from pathlib import Path
import numpy as np
from .alpha_post import clean_alpha, enforce_trimap, suppress_alpha_by_chroma
from .alpha_post import (
carve_background_holes,
clean_alpha,
enforce_trimap,
suppress_alpha_by_chroma,
)
from .chroma import compute_bg_confidence
from .despill import despill_green
from .foreground import estimate_foreground_rgb
@@ -73,6 +78,7 @@ def _run_image(
alpha = enforce_trimap(alpha, trimap)
alpha = suppress_alpha_by_chroma(alpha, bg_confidence, trimap, settings.alpha_post)
alpha = clean_alpha(alpha, trimap, settings.alpha_post)
alpha = carve_background_holes(alpha, rgb, settings.alpha_post)
foreground = estimate_foreground_rgb(
rgb, alpha, bg_confidence, model, settings.foreground
)