# Standbilder freistellen (rembg), Leinwand bleibt erhalten. # python3 werkzeuge/freistellen.py bild1.png bild2.png ... # Freistellen ohne Zuschneiden (gleiche Leinwand -> Posen deckungsgleich) import sys, os, time import numpy as np from PIL import Image, ImageFilter from scipy import ndimage from rembg import remove, new_session s = new_session("isnet-general-use") out_dir = sys.argv[1] for src in sys.argv[2:]: dst = os.path.join(out_dir, os.path.basename(src)[:-4] + ".png") if os.path.exists(dst): continue t = time.time() im = Image.open(src).convert("RGB") m = np.asarray(remove(im, session=s, only_mask=True)).astype(float) hard = m > 110 lab, n = ndimage.label(hard) if n == 0: print("LEER", src); continue sizes = ndimage.sum(hard, lab, range(1, n + 1)) keep = np.zeros_like(hard) for i, sz in enumerate(sizes): # alle großen Teile (Laterne, Bommel) behalten if sz > 0.02 * sizes.max(): keep |= lab == i + 1 hard = keep holes = ndimage.binary_fill_holes(hard) & ~hard hl, hn = ndimage.label(holes) for i in range(1, hn + 1): if (hl == i).sum() < 400: hard |= hl == i rgb = np.asarray(im).astype(float) paper = (rgb.min(-1) > 228) & (rgb.max(-1) - rgb.min(-1) < 18) plab, pn = ndimage.label(paper & hard) for i in range(1, pn + 1): if (plab == i).sum() > 600: hard &= ~(plab == i) hard = ndimage.binary_erosion(hard, iterations=2) a = Image.fromarray((hard * 255).astype("uint8")).filter(ImageFilter.GaussianBlur(0.8)) o = im.convert("RGBA"); o.putalpha(a); o.save(dst) print(os.path.basename(dst), round(time.time() - t, 1), flush=True)