Remove dead colour-model/despill code; simplify despill; refresh stale docs

- chroma: drop BackgroundModel.hsv_center/hue_sigma (unused since the green gating
  went away), their estimation, _hue_distance, and the now-unused hsv channel from
  convert_color_spaces (one fewer cvtColor per image).
- despill: delete the dead green-channel-algebra path (the pipeline always passes a
  colour) and fold the remaining Lab-direction despill into a single despill() with
  no screen_color arg (127 -> 60 lines). Renamed despill_green -> despill.
- settings: remove the now-dead ChromaSettings.hue_sigma_min and
  DespillSettings.green_excess_margin (plus the matching config lines).
- docs/comments: generalise stale "green" wording to "background colour" (module
  docstring, alpha_post/trimap/foreground/settings) and rename the trimap variable
  green_bg -> screen_bg.

Kept: the unmix foreground fallback, --matting-method chroma, and green/blue as
illustrative examples. Verified: package compiles, both configs parse, and an
auto-detect end-to-end run is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 17:49:20 +08:00
parent a6d84845c6
commit fe97d692a7
10 changed files with 50 additions and 140 deletions
+7 -7
View File
@@ -56,12 +56,12 @@ def generate_trimap(
def fuse_trimap(
seg_mask: np.ndarray, bg_confidence: np.ndarray, settings: TrimapSettings
) -> tuple[np.ndarray, dict[str, int]]:
"""Build a trimap from a semantic subject mask, refined by the green key.
"""Build a trimap from a semantic subject mask, refined by the chroma key.
Authority split: the segmentation mask decides subject *topology* (it keeps
colour-contaminated hair as foreground and drops see-through holes), while the
bright-green chroma key sharpens the screen boundary. ViTMatte then refines
the unknown band.
chroma key sharpens the flat-background boundary. ViTMatte then refines the
unknown band.
"""
cv2 = require_cv2()
shape = seg_mask.shape
@@ -70,7 +70,7 @@ def fuse_trimap(
)
band_radius = max(settings.min_fg_safe_radius, fg_safe_radius // 2)
green_bg = bg_confidence >= settings.sure_bg_threshold # bright clean screen
screen_bg = bg_confidence >= settings.sure_bg_threshold # confident flat background
core = (seg_mask >= settings.seg_core_threshold).astype(np.uint8)
loose = (seg_mask >= settings.seg_loose_threshold).astype(np.uint8)
@@ -81,9 +81,9 @@ def fuse_trimap(
band = cv2.dilate(loose, kernel).astype(bool) & ~cv2.erode(loose, kernel).astype(bool)
# Background follows the mask directly so interior holes stay background, minus
# the boundary band; foreground is the confident subject, never bright screen.
sure_fg = core.astype(bool) & ~band & ~green_bg
sure_bg = ((loose == 0) & ~band) | (green_bg & (loose == 0))
# the boundary band; foreground is the confident subject, never the background.
sure_fg = core.astype(bool) & ~band & ~screen_bg
sure_bg = ((loose == 0) & ~band) | (screen_bg & (loose == 0))
trimap = np.full(shape, 128, dtype=np.uint8)
trimap[sure_bg] = 0