""" Make the (opaque, near-white) background of the mascot cutouts transparent, WITHOUT punching holes in the character's white clothing. Method: flag near-white pixels, then keep only the near-white regions that are CONNECTED TO THE IMAGE BORDER (that's the background). Interior whites (hoodie / tee), enclosed by the character's outline, are preserved. A tiny alpha blur feathers the edge to avoid a white halo. Usage: python tools/make_transparent.py [ ...] Originals are backed up to Characters/_opaque_backup/ (once) before overwrite. """ import sys, os, glob import numpy as np from PIL import Image from scipy import ndimage WHITE_THRESH = 236 # min channel value to be considered "near white" FEATHER = 0.8 # gaussian sigma for alpha edge feather (px) BACKUP_ROOT = os.path.join("Characters", "_opaque_backup") def process(path: str): im = Image.open(path).convert("RGBA") arr = np.array(im) rgb = arr[:, :, :3].astype(np.int16) # near-white mask: all channels high AND low chroma (avoids eating pale skin) mn = rgb.min(axis=2) mx = rgb.max(axis=2) near_white = (mn >= WHITE_THRESH) & ((mx - mn) <= 12) # connected components of near-white; keep those touching the border = background labels, n = ndimage.label(near_white) border_labels = set(labels[0, :]) | set(labels[-1, :]) | set(labels[:, 0]) | set(labels[:, -1]) border_labels.discard(0) bg = np.isin(labels, list(border_labels)) alpha = np.where(bg, 0, 255).astype(np.float32) if FEATHER > 0: alpha = ndimage.gaussian_filter(alpha, sigma=FEATHER) arr[:, :, 3] = np.clip(alpha, 0, 255).astype(np.uint8) # backup once rel = os.path.relpath(path, "Characters") if path.replace("\\", "/").startswith("Characters") else os.path.basename(path) bpath = os.path.join(BACKUP_ROOT, rel) os.makedirs(os.path.dirname(bpath), exist_ok=True) if not os.path.exists(bpath): Image.open(path).save(bpath) Image.fromarray(arr, "RGBA").save(path) covered = 100.0 * bg.mean() print(f" {os.path.basename(path)} bg removed {covered:4.1f}%") def collect(target): if os.path.isdir(target): return sorted(glob.glob(os.path.join(target, "*.png"))) return [target] if __name__ == "__main__": targets = sys.argv[1:] or ["Characters/sori", "Characters/dan", "Characters/duo"] for t in targets: files = collect(t) if files: print(t) for f in files: process(f) print("done.")