77 lines
3.7 KiB
Python
77 lines
3.7 KiB
Python
|
|
# WL-814x4 — 부위 그룹(M05/Skin/Hair)별로 _DiffuseColor 역이득을 따로 탐색한다.
|
||
|
|
# 전역 이득 1개로는 못 맞추는 이유 = 부위마다 밝기 분포가 달라 그레이딩 회전량이 다르기 때문.
|
||
|
|
import json, sys
|
||
|
|
import numpy as np
|
||
|
|
from PIL import Image
|
||
|
|
RAW='Screenshots_WL/WL814x/4th/raw'
|
||
|
|
REN=sys.argv[1] if len(sys.argv)>1 else 'a_now'
|
||
|
|
def L(n): return np.asarray(Image.open(RAW+'/'+n+'.png').convert('RGB'),dtype=np.uint8)
|
||
|
|
def lum(c): return 0.299*c[0]+0.587*c[1]+0.114*c[2]
|
||
|
|
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()}
|
||
|
|
PART={'Body_m05':'M05','Arm_m05':'M05','Leg_M05':'M05','Head':'Skin','Hair05':'Hair'}
|
||
|
|
pm={p:(L('mask_'+p)[:,:,0]>127) for p in PART}
|
||
|
|
im=L(REN)
|
||
|
|
cells={}
|
||
|
|
for j in range(G*G):
|
||
|
|
sel=ma&(kid==j); n=int(sel.sum())
|
||
|
|
if n<800: continue
|
||
|
|
grp=PART[max(((k,int((sel&v).sum())) for k,v in pm.items()),key=lambda t:t[1])[0]]
|
||
|
|
cells.setdefault(grp,[]).append((j,n,tgt[j].astype(np.float64),im[sel].mean(0).astype(np.float64)))
|
||
|
|
best={}
|
||
|
|
print('%-6s %-24s %8s %8s (칸 %d)'%('그룹','최적 _DiffuseColor','dHue','최대',0))
|
||
|
|
for grp,cs in cells.items():
|
||
|
|
def ev(g, full=False):
|
||
|
|
e=[];w=[];pen=0.0;pw=0.0
|
||
|
|
for j,n,o,s in cs:
|
||
|
|
c=np.clip(s*g,0,255)
|
||
|
|
if sat(o)>0.12:
|
||
|
|
e.append(abs(dh(hue(o),hue(c)))); w.append(n)
|
||
|
|
# 채도 보존: 보정으로 채도가 줄면(색이 빠지면) 벌점
|
||
|
|
pen += n*max(0.0, sat(s)-sat(c))*260.0; pw += n
|
||
|
|
else:
|
||
|
|
# 무채색 목표(흰옷·부츠)는 화면에서도 무채여야 한다
|
||
|
|
pen += n*max(0.0, sat(c)-0.14)*700.0; pw += n
|
||
|
|
if not e: return 0.0,0.0,0.0
|
||
|
|
e=np.array(e);w=np.array(w,float)
|
||
|
|
return (e*w).sum()/w.sum(), e.max(), pen/max(pw,1)
|
||
|
|
bb=None
|
||
|
|
for gr in np.arange(0.88,1.145,0.005):
|
||
|
|
for gb in np.arange(0.80,1.145,0.005):
|
||
|
|
gg=(1.0-0.299*gr-0.114*gb)/0.587
|
||
|
|
if gg<0.88 or gg>1.15: continue
|
||
|
|
g=np.array([gr,gg,gb]); a,m,pn=ev(g)
|
||
|
|
sc=a+0.4*m+pn
|
||
|
|
if bb is None or sc<bb[0]: bb=(sc,g,a,m)
|
||
|
|
a0,m0,_=ev(np.ones(3))
|
||
|
|
print('%-6s %-24s %8.1f %8.1f (보정 전 %.1f / 최대 %.1f · 칸 %d)'%(grp,str(np.round(bb[1],3)),bb[2],bb[3],a0,m0,len(cs)))
|
||
|
|
best[grp]=[float(x) for x in bb[1]]
|
||
|
|
for j,n,o,s in cs:
|
||
|
|
c=np.clip(s*bb[1],0,255)
|
||
|
|
print(' 칸%2d n=%6d 원본%-16s 지금%-16s → %-16s dHue %+6.1f → %+6.1f'%(j,n,
|
||
|
|
tuple(int(x) for x in o),tuple(int(x) for x in s),tuple(int(x) for x in c),dh(hue(o),hue(s)),dh(hue(o),hue(c))))
|
||
|
|
json.dump(best,open('AgentScripts/WL814x4_pergroup.json','w'),indent=1)
|
||
|
|
# 전체 가중 평균 예측
|
||
|
|
e=[];w=[]
|
||
|
|
for grp,cs in cells.items():
|
||
|
|
g=np.array(best[grp])
|
||
|
|
for j,n,o,s in cs:
|
||
|
|
if sat(o)<=0.12: continue
|
||
|
|
e.append(abs(dh(hue(o),hue(np.clip(s*g,0,255))))); w.append(n)
|
||
|
|
e=np.array(e);w=np.array(w,float)
|
||
|
|
print('예상 전체 유채 가중 dHue %.1f° (최대 %.1f°)'%((e*w).sum()/w.sum(),e.max()))
|