diff --git a/bgfilter/trimap.py b/bgfilter/trimap.py index 2e2813f..78648da 100644 --- a/bgfilter/trimap.py +++ b/bgfilter/trimap.py @@ -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()),