File size: 1,997 Bytes
ebd40f6 3f7a009 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | import re
from pathlib import Path
from PIL import Image
def natural_key(s: str):
"""Natural sort key: '10.png' sorts after '2.png' correctly."""
return [int(t) if t.isdigit() else t.lower() for t in re.split(r"(\d+)", s)]
def load_rgb_image(path: str | Path) -> Image.Image:
img = Image.open(path)
if img.mode != "RGB":
img = img.convert("RGB")
return img
def save_rgb_png(img: Image.Image, path: str | Path):
path = Path(path)
path.parent.mkdir(parents=True, exist_ok=True)
img.save(path, format="PNG")
def make_rgba_with_alpha(rgb_img: Image.Image, alpha_mask: Image.Image) -> Image.Image:
"""
rgb_img: RGB PIL
alpha_mask: L PIL, same size or will be resized
Returns RGBA PIL where alpha channel is the mask.
"""
if rgb_img.mode != "RGB":
rgb_img = rgb_img.convert("RGB")
if alpha_mask.mode != "L":
alpha_mask = alpha_mask.convert("L")
if alpha_mask.size != rgb_img.size:
alpha_mask = alpha_mask.resize(rgb_img.size, Image.NEAREST)
rgba = rgb_img.copy()
rgba.putalpha(alpha_mask)
return rgba
def save_rgba_png(img: Image.Image, path: str | Path):
path = Path(path)
path.parent.mkdir(parents=True, exist_ok=True)
img.save(path, format="PNG")
def remove_white_background_alpha(img: Image.Image, threshold: int = 240) -> Image.Image:
"""
Create an alpha mask by treating near-white pixels as background.
Pixels where R, G, B are all >= threshold are considered background (alpha=0).
All other pixels get alpha=255.
Returns L mode image (0=background, 255=foreground).
"""
import numpy as np
if img.mode != "RGB":
img = img.convert("RGB")
arr = np.array(img) # (H, W, 3)
# Background = all channels >= threshold
is_white = (arr[:, :, 0] >= threshold) & (arr[:, :, 1] >= threshold) & (arr[:, :, 2] >= threshold)
alpha = np.where(is_white, 0, 255).astype(np.uint8)
return Image.fromarray(alpha, mode="L")
|