Drop the silhouette band overwrite in the directional trimap
The band (dilate-minus-erode ring on the seg silhouette, added in 5f48f7a)
was meant to give ViTMatte room to anti-alias the boundary. Measured on a
warm-background sample (fix_image02, #FFD6C2): ViTMatte outputs near-binary
alpha in the band on crisp-edged anime images (96% hard values, 82% of
silhouette crossings with zero soft pixels), so the band delivered no
anti-aliasing -- it only let ViTMatte relocate the boundary (chewed edges)
and wholly confiscated structures thinner than the ring: 74% of lost hair
strand pixels were rule-FG demoted by the overwrite, then dropped by
ViTMatte. Without the band, 88% of those strands survive, edges follow the
rule-decided contour, and cross-check no longer bites strand edges because
strand cores stay anchored at 255 (re-destroyed: 0 px).
The rules own unknown pixels (background-hued blends, low-seg transitions)
remain ViTMatte territory. fuse_trimap (seg mode) keeps its band untouched --
its rules were designed around it and the removal is only validated for the
directional path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+6
-14
@@ -123,16 +123,14 @@ def fuse_trimap_directional(
|
||||
strongly displaced toward it (e.g. blue between hair strands) is left
|
||||
unknown for ViTMatte / chroma-suppress. Lower-seg unknowns stay unknown.
|
||||
|
||||
A thin unknown band at the segmentation silhouette is preserved so ViTMatte can
|
||||
anti-alias the boundary (it is deliberately small so interior holes survive).
|
||||
No silhouette band is forced back to unknown: structures thinner than the band
|
||||
ring (flying hair strands) got wholly confiscated by dilate-minus-erode and then
|
||||
dropped by ViTMatte, which on crisp-edged (anime) images outputs near-binary
|
||||
alpha in the band instead of anti-aliasing (measured: 96% hard values, and 74%
|
||||
of lost strand pixels were rule-FG demoted by the band). The rules' own unknown
|
||||
pixels (background-hued blends, low-seg transitions) remain ViTMatte's domain.
|
||||
"""
|
||||
cv2 = require_cv2()
|
||||
shape = seg_mask.shape
|
||||
fg_safe_radius = radius_from_ratio(
|
||||
shape, settings.fg_safe_radius_ratio, settings.min_fg_safe_radius
|
||||
)
|
||||
band_radius = max(settings.min_fg_safe_radius, fg_safe_radius // 2)
|
||||
|
||||
chroma_bg = bg_confidence >= settings.sure_bg_threshold
|
||||
chroma_fg = bg_confidence <= settings.sure_fg_threshold
|
||||
chroma_unknown = ~chroma_bg & ~chroma_fg
|
||||
@@ -161,16 +159,10 @@ def fuse_trimap_directional(
|
||||
trimap[bg] = 0
|
||||
trimap[fg | rule3_fg | force_fg] = 255
|
||||
|
||||
loose = (seg_mask >= settings.seg_loose_threshold).astype(np.uint8)
|
||||
kernel = elliptical_kernel(band_radius)
|
||||
band = cv2.dilate(loose, kernel).astype(bool) & ~cv2.erode(loose, kernel).astype(bool)
|
||||
trimap[band & ~chroma_bg] = 128
|
||||
|
||||
stats = {
|
||||
"sure_bg_pixels": int((trimap == 0).sum()),
|
||||
"unknown_pixels": int((trimap == 128).sum()),
|
||||
"sure_fg_pixels": int((trimap == 255).sum()),
|
||||
"band_radius": int(band_radius),
|
||||
"rule3_fg_pixels": int(rule3_fg.sum()),
|
||||
"force_fg_pixels": int((force_fg & ~fg & ~rule3_fg).sum()),
|
||||
"bg_hued_pixels": int((bg_hued & chroma_unknown & core).sum()),
|
||||
|
||||
Reference in New Issue
Block a user