63 lines
4.1 KiB
Python
63 lines
4.1 KiB
Python
# WL-814x3 — 최종 수치표 + 필수 캡처 4종
|
|
import json
|
|
import numpy as np
|
|
from PIL import Image, ImageDraw
|
|
RAW='Screenshots_WL/WL814x/3rd/raw'; OUT='Screenshots_WL/WL814x/3rd'
|
|
def L(n): return np.asarray(Image.open(RAW+'/'+n+'.png').convert('RGB'),dtype=np.uint8)
|
|
def hue(c):
|
|
r,g,b=[float(x) for x in c]; mx,mn=max(r,g,b),min(r,g,b); d=mx-mn
|
|
if d<1e-6: return 0.0
|
|
if mx==r: h=((g-b)/d)%6
|
|
elif mx==g: h=(b-r)/d+2
|
|
else: h=(r-g)/d+4
|
|
return h*60.0
|
|
def sat(c):
|
|
mx=max(c); return (mx-min(c))/mx if mx>1e-6 else 0
|
|
def dh(a,b): return (b-a+540)%360-180
|
|
idm=L('idmap'); maF=L('mask_Face')[:,:,0]>127; ma=(L('mask_all')[:,:,0]>127)&~maF
|
|
G=np.array(Image.open('Assets/WL/Look/Character/Textures/WLPalette_FINAL.png')).shape[0]//8
|
|
idref=np.array([[9+j*15,240-j*11,40+j*7] for j in range(G*G)],np.float32)
|
|
fl=idm.reshape(-1,3).astype(np.float32); d2=((fl[:,None,:]-idref[None,:,:])**2).sum(2)
|
|
kid=np.where((d2.min(1).reshape(idm.shape[:2])<400)&(idm.sum(2)>12), d2.argmin(1).reshape(idm.shape[:2]), -1)
|
|
tgt={int(k):np.array(v,np.float32) for k,v in json.load(open('AgentScripts/WL814x2_target.json')).items()}
|
|
NAME={8:'머리칼(주)',4:'머리칼(밝은단)',12:'머리칼(하이라이트)',2:'흰 튜닉',14:'부츠 흰면',1:'가죽 갑옷',13:'가죽(어두운)',3:'부츠 갈색',15:'붉은 바지',9:'붉은 천',10:'벨트',11:'팔 소매',0:'머리 피부',5:'다리 피부'}
|
|
m10=L('mask_all10')[:,:,0]>127
|
|
print('%-16s %7s | %-16s %-16s %8s | %-16s %8s'%('부위','px','원본 RGB','2차(보라 앰비언트)','dHue','3차(중성+보정)','dHue'))
|
|
ch0=[];ch1=[];w=[]
|
|
for j in sorted(NAME):
|
|
sel=ma&(kid==j); n=int(sel.sum())
|
|
if n<800: continue
|
|
o=tgt[j]; a=L('pre_v0_purple')[sel].mean(0); b=L('f_final')[sel].mean(0)
|
|
ea=dh(hue(o),hue(a)); eb=dh(hue(o),hue(b))
|
|
print('%-16s %7d | %-16s %-16s %+8.1f | %-16s %+8.1f'%(NAME[j],n,tuple(int(x) for x in o),
|
|
tuple(int(x) for x in a),ea,tuple(int(x) for x in b),eb))
|
|
if sat(o)>0.12: ch0.append(abs(ea)); ch1.append(abs(eb)); w.append(n)
|
|
w=np.array(w,float); ch0=np.array(ch0); ch1=np.array(ch1)
|
|
print('유채색 가중 dHue: 2차 %.1f°(최대 %.1f) → 3차 %.1f°(최대 %.1f)'%((ch0*w).sum()/w.sum(),ch0.max(),(ch1*w).sum()/w.sum(),ch1.max()))
|
|
for v,lbl in [('nc_gray','보정전(중성 앰비언트)'),('s0_now','3차 최종')]:
|
|
g=L(v+'_g')[m10]; k=(g[:,0].astype(np.int32)<<16)|(g[:,1].astype(np.int32)<<8)|g[:,2].astype(np.int32)
|
|
u=len(np.unique(k)); print('%s 게임 크기: 캐릭터 %d px · 고유색 %d · 서로 다른 색 %.1f %%'%(lbl,m10.sum(),u,100.0*u/m10.sum()))
|
|
# 캡처
|
|
ys,xs=np.where(ma|maF); x0,x1,y0,y1=xs.min()-24,xs.max()+25,ys.min()-24,ys.max()+25
|
|
def crop(n): return Image.open(RAW+'/'+n+'.png').convert('RGB').crop((x0,y0,x1,y1))
|
|
def grid(out,items,scale=1.0):
|
|
ims=[(t,crop(n)) for t,n in items]
|
|
if scale!=1.0: ims=[(t,im.resize((int(im.width*scale),int(im.height*scale)),Image.NEAREST)) for t,im in ims]
|
|
W=sum(i.width for _,i in ims)+18*(len(ims)-1); H=ims[0][1].height+26
|
|
o=Image.new('RGB',(W,H),(22,22,26)); d=ImageDraw.Draw(o); x=0
|
|
for t,im in ims:
|
|
o.paste(im,(x,26)); d.text((x+3,7),t,fill=(240,232,190)); x+=im.width+18
|
|
o.save(OUT+'/'+out); print('saved',out,o.size)
|
|
grid('d_compare.png',[('2차 (보라 앰비언트)','pre_v0_purple'),('3차 최종 (중성 앰비언트 + 보정)','s0_now')])
|
|
grid('d_ambient_steps.png',[('보라(기존)','pre_v0_purple'),('중성 회색','pre_v1_gray'),('옅은 청회','v2_cool'),('옅은 난색','v3_warm')],0.72)
|
|
def wide(out,items):
|
|
ims=[(t,Image.open(RAW+'/'+n+'.png').convert('RGB').resize((405,720))) for t,n in items]
|
|
W=sum(i.width for _,i in ims)+16*(len(ims)-1); H=ims[0][1].height+26
|
|
o=Image.new('RGB',(W,H),(22,22,26)); d=ImageDraw.Draw(o); x=0
|
|
for t,im in ims:
|
|
o.paste(im,(x,26)); d.text((x+3,7),t,fill=(240,232,190)); x+=im.width+16
|
|
o.save(OUT+'/'+out); print('saved',out,o.size)
|
|
wide('d_key_bg.png',[('기존 키라이트','w1_gray'),('키 (1,.95,.90)','w4_gray_keysoft'),('키 순백 (1,1,1)','w5_gray_keywhite')])
|
|
Image.open(RAW+'/f_wide.png').convert('RGB').save(OUT+'/d_final_wide.png')
|
|
print('saved d_final_wide.png')
|