# -*- coding: utf-8 -*- """Один мастер-логотип → 8 тем. Изолируем букву по насыщенности, перекрашиваем по яркости.""" from PIL import Image, ImageOps, ImageFilter, ImageDraw, ImageFont import os GEN = r"C:\radiOLA\design\logos\gen" MASTER = os.path.join(GEN, "master_matte_0.png") OUTDIR = os.path.join(GEN, "themed"); os.makedirs(OUTDIR, exist_ok=True) # (id, акцент, фон темы) — как в ThemePalette THEMES = [ ("forest", (0xA8,0xE0,0x5F), (0x0C,0x14,0x10)), ("ocean", (0x4F,0xD6,0xE0), (0x0A,0x0F,0x1A)), ("sunset", (0xFF,0x8A,0x5B), (0x1A,0x0F,0x0C)), ("amethyst",(0xB3,0x88,0xFF), (0x12,0x0E,0x1A)), ("neon", (0xFF,0x4D,0x9D), (0x0D,0x0A,0x12)), ("amber", (0xFF,0xC2,0x47), (0x14,0x11,0x0A)), ("ice", (0x7F,0xB3,0xFF), (0x0C,0x10,0x14)), ("rose", (0xFF,0x7E,0xA8), (0x16,0x0E,0x12)), ] def lerp(a,b,t): return tuple(int(a[i]+(b[i]-a[i])*t) for i in range(3)) im = Image.open(MASTER).convert("RGB") hsv = im.convert("HSV") H,S,V = hsv.split() # --- альфа-маска: буква насыщенная, шахматка-фон серый (низкая S) --- alpha = S.point(lambda s: 0 if s < 40 else min(255, int((s-40)*4))) alpha = alpha.filter(ImageFilter.MaxFilter(3)).filter(ImageFilter.GaussianBlur(0.8)) # --- яркость буквы (3D-тени), нормализуем в рабочий диапазон --- L = ImageOps.grayscale(im) # статистика только по букве import numpy as np la = np.asarray(L, dtype=float); aa = np.asarray(alpha, dtype=float)/255.0 mask = aa > 0.5 if mask.sum() > 0: lo, hi = np.percentile(la[mask], 4), np.percentile(la[mask], 96) else: lo, hi = 40, 150 la = np.clip((la - lo)/(hi - lo), 0, 1) # 0..1 нормализованная яркость буквы Ln = Image.fromarray((la*255).astype("uint8"), "L") def themed(accent): dark = lerp(accent, (0,0,0), 0.55) # тень/боковая грань light = lerp(accent, (255,255,255), 0.30) # свет фронта col = ImageOps.colorize(Ln, black=dark, white=light, mid=accent).convert("RGBA") col.putalpha(alpha) return col # отдельные прозрачные PNG + превью-лист на фоне темы cell=300; pad=26; cols=4; rows=2 W=cols*cell+(cols+1)*pad; Hh=rows*(cell+40)+(rows+1)*pad+56 sheet=Image.new("RGB",(W,Hh),(8,12,10)); d=ImageDraw.Draw(sheet) def f(s): try: return ImageFont.truetype(r"C:\Windows\Fonts\segoeui.ttf",s) except: return ImageFont.load_default() d.text((pad,18),"radiOLA — один логотип, перекрашен под 8 тем",font=f(26),fill=(234,246,223)) for i,(name,acc,bg) in enumerate(THEMES): logo = themed(acc) logo.save(os.path.join(OUTDIR, f"logo_{name}.png")) # превью на фоне темы tile = Image.new("RGBA",(cell,cell), bg+(255,)) lg = logo.resize((int(cell*0.74),)*2) tile.alpha_composite(lg, ((cell-lg.width)//2,(cell-lg.height)//2)) r=i//cols; c=i%cols; x=pad+c*(cell+pad); y=56+pad+r*(cell+40+pad) sheet.paste(tile.convert("RGB"),(x,y)) d.text((x+6,y+cell+8),name,font=f(20),fill=(150,170,150)) out=os.path.join(GEN,"_themed_sheet.png"); sheet.save(out) import shutil; shutil.copy(out, os.path.join(os.environ["LOCALAPPDATA"],"Temp","radiola_themed.png")) print("saved", len(THEMES), "themed logos ->", OUTDIR)