#!/usr/bin/env python3 """Generate recolored outfit textures for Kira's Live2D model. Takes the base texture_02.png (clothing layer) and creates color variants for each outfit using HSL shifts on non-skin areas. """ from PIL import Image import colorsys import os BASE_DIR = os.path.expanduser( "~/Projects/ai-body-double/frontend/public/live2d/models/kira" ) TEXTURE_DIR = os.path.join(BASE_DIR, "Epsilon.1024") OUT_DIR = os.path.join(BASE_DIR, "outfits") # Outfit → HSL shift mapping (hue_shift_deg, sat_mult, light_mult) # Applied to non-white, non-skin pixels OUTFITS = { "cozy-hoodie": (10, 1.1, 1.0), # warm pink shift "girly-dress": (240, 1.2, 0.95), # lavender/purple "pajama-set": (140, 0.8, 1.1), # minty green "study-sweater": (30, 1.1, 1.0), # warm orange/amber "going-out": (320, 1.3, 1.05), # bright pink/magenta } def is_skin(r, g, b): """Roughly detect skin tones (avoid recoloring skin).""" return 180 < r < 250 and 120 < g < 200 and 80 < b < 170 def is_white_or_void(r, g, b, a): """Detect transparent or near-white pixels.""" if a < 10: return True return r > 240 and g > 240 and b > 240 def recolor_texture(src_path, dst_path, hue_shift, sat_mult, light_mult): """Recolor a texture by shifting HSL on non-skin, non-white pixels.""" img = Image.open(src_path).convert("RGBA") pixels = img.load() w, h = img.size for y in range(h): for x in range(w): r, g, b, a = pixels[x, y] if is_white_or_void(r, g, b, a) or is_skin(r, g, b): continue # Convert to HSL h_val, l_val, s_val = colorsys.rgb_to_hls(r / 255, g / 255, b / 255) # Apply shifts h_val = (h_val + hue_shift / 360) % 1.0 s_val = min(1.0, s_val * sat_mult) l_val = max(0, min(1.0, l_val * light_mult)) # Convert back to RGB nr, ng, nb = colorsys.hls_to_rgb(h_val, l_val, s_val) pixels[x, y] = (int(nr * 255), int(ng * 255), int(nb * 255), a) os.makedirs(os.path.dirname(dst_path), exist_ok=True) img.save(dst_path) size = os.path.getsize(dst_path) print(f" Created {os.path.basename(dst_path)} ({size//1024}KB)") def main(): src = os.path.join(TEXTURE_DIR, "texture_02.png") if not os.path.exists(src): print(f"Source not found: {src}") return 1 print(f"Generating outfit textures from {src}") print() for outfit_name, (hue, sat, light) in OUTFITS.items(): dst = os.path.join(OUT_DIR, f"{outfit_name}.png") print(f" {outfit_name}: hue={hue}° sat={sat:.1f}x light={light:.2f}x") recolor_texture(src, dst, hue, sat, light) print() print("Done! Texture variants ready.") return 0 if __name__ == "__main__": exit(main())