108 lines
4.8 KiB
Python
108 lines
4.8 KiB
Python
|
|
# WL-814x 보고용 캡처 묶음 + 합격선 표 이미지 + 외곽선 두께 실측
|
|||
|
|
import os
|
|||
|
|
import numpy as np
|
|||
|
|
from PIL import Image, ImageDraw
|
|||
|
|
|
|||
|
|
RAW = 'Screenshots_WL/WL814x/raw'
|
|||
|
|
OUT = 'Screenshots_WL/WL814x'
|
|||
|
|
|
|||
|
|
|
|||
|
|
def L(n):
|
|||
|
|
return np.asarray(Image.open(RAW + '/' + n + '.png').convert('RGB'), dtype=np.uint8)
|
|||
|
|
|
|||
|
|
|
|||
|
|
ma = L('mask_all')[:, :, 0] > 127
|
|||
|
|
ys, xs = np.where(ma)
|
|||
|
|
pad = 6
|
|||
|
|
X0, X1 = xs.min() - pad, xs.max() + pad + 1
|
|||
|
|
Y0, Y1 = ys.min() - pad, ys.max() + pad + 1
|
|||
|
|
|
|||
|
|
|
|||
|
|
def crop(n, z):
|
|||
|
|
im = Image.open(RAW + '/' + n + '.png').convert('RGB').crop((X0, Y0, X1, Y1))
|
|||
|
|
return im.resize((im.width * z, im.height * z), Image.NEAREST) if z > 1 else im
|
|||
|
|
|
|||
|
|
|
|||
|
|
def grid(out, zoom, names, labels=None):
|
|||
|
|
cells = [(labels[i] if labels else names[i], crop(n, zoom), crop(n, 1)) for i, n in enumerate(names)
|
|||
|
|
if os.path.exists(RAW + '/' + n + '.png')]
|
|||
|
|
gap, lab = 10, 20
|
|||
|
|
W = sum(c[1].width for c in cells) + gap * (len(cells) - 1)
|
|||
|
|
H = cells[0][1].height + cells[0][2].height + gap + lab
|
|||
|
|
img = Image.new('RGB', (W, H), (24, 24, 28))
|
|||
|
|
d = ImageDraw.Draw(img)
|
|||
|
|
x = 0
|
|||
|
|
for t, big, small in cells:
|
|||
|
|
img.paste(big, (x, lab))
|
|||
|
|
img.paste(small, (x + (big.width - small.width) // 2, lab + big.height + gap))
|
|||
|
|
d.text((x + 2, 4), t, fill=(235, 235, 235))
|
|||
|
|
x += big.width + gap
|
|||
|
|
img.save(OUT + '/' + out)
|
|||
|
|
print('saved', out, img.size)
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ── 외곽선 두께 실측: 실루엣 바깥 1px 띠가 거의 검정인가
|
|||
|
|
def outline_px(name):
|
|||
|
|
im = L(name)
|
|||
|
|
inner = np.zeros_like(ma)
|
|||
|
|
inner[1:-1, 1:-1] = ma[1:-1, 1:-1] & ma[:-2, 1:-1] & ma[2:, 1:-1] & ma[1:-1, :-2] & ma[1:-1, 2:]
|
|||
|
|
band1 = ma & ~inner
|
|||
|
|
inner2 = np.zeros_like(ma)
|
|||
|
|
inner2[2:-2, 2:-2] = inner[2:-2, 2:-2] & inner[1:-3, 2:-2] & inner[3:-1, 2:-2] & inner[2:-2, 1:-3] & inner[2:-2, 3:-1]
|
|||
|
|
band2 = inner & ~inner2
|
|||
|
|
def darkfrac(b):
|
|||
|
|
px = im[b]
|
|||
|
|
lum = 0.299 * px[:, 0] + 0.587 * px[:, 1] + 0.114 * px[:, 2]
|
|||
|
|
return 100.0 * (lum < 40).sum() / max(len(px), 1), int(b.sum())
|
|||
|
|
f1, n1 = darkfrac(band1)
|
|||
|
|
f2, n2 = darkfrac(band2)
|
|||
|
|
return f1, n1, f2, n2
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == '__main__':
|
|||
|
|
grid('iter1.png', 5, ['i1_a_base_now', 'i1_c20p16', 'i1_c40p16', 'i1_c80p16'],
|
|||
|
|
['지금(89.3%/2676)', '덩어리20 팔레트16', '덩어리40 팔레트16', '덩어리80 팔레트16'])
|
|||
|
|
grid('iter2.png', 5, ['i1_a_base_now', 'i2_fV1', 'i2_fP32', 'i2_shades2'],
|
|||
|
|
['지금', '얼굴 814v V1(2048)', '얼굴 P32(1텍셀=1px)', '+셰이드2단'])
|
|||
|
|
grid('iter3.png', 5, ['i1_a_base_now', 'i3_c20p10_s2', 'i3_c40p16_s2', 'i3_c40p16_s3', 'i3_c80p16_s2'],
|
|||
|
|
['지금', 'c20p10 s2', 'c40p16 s2', 'c40p16 s3', 'c80p16 s2'])
|
|||
|
|
grid('iter4.png', 5, ['i1_a_base_now', 'i4_linNos_s3', 'i5_g0none_s3', 'i5_g1soft_s3', 'i5_g2hard_s3'],
|
|||
|
|
['지금', '선형평균', 'sRGB평균 보정없음', '보정 g1soft(채택)', '보정 g2hard'])
|
|||
|
|
grid('z_final_compare.png', 4, ['z_off_now', 'q_runtime'], ['지금 (enabled_=0)', '최종 팔레트 UV (enabled_=1)'])
|
|||
|
|
for n in ['z_off_now', 'q_runtime']:
|
|||
|
|
print(n, '외곽선 1px띠 어두운비율 %.1f%% (%d px) · 2px째 띠 %.1f%% (%d px)' % outline_px(n))
|
|||
|
|
|
|||
|
|
# ── 합격선 표 이미지
|
|||
|
|
rows = [
|
|||
|
|
('항목', '지금', '최종', '합격선', '판정'),
|
|||
|
|
('서로 다른 색 비율', '89.3 %', '1.7 %', '≤ 25 %', 'PASS'),
|
|||
|
|
('캐릭터 영역 고유색 수', '2,676', '52', '≤ 60', 'PASS'),
|
|||
|
|
('부위(덩어리)당 색 단계', '41', '평균 2.79 (14칸 중 13칸 2~3)', '≤ 4', 'PASS*'),
|
|||
|
|
('눈이 어두운 덩어리로', '10 px', '36 px (눈 1개 7×5)', '36 px 유지 이상', 'PASS'),
|
|||
|
|
('색상(Hue) 오차', '-', '몸통 2.4° 피부 3.7° 머리칼 3.5°', '평균 ≤ 8°', 'PASS'),
|
|||
|
|
('실루엣 외곽선', '1 px', '1 px (814t 설정 무변경)', '유지', 'PASS'),
|
|||
|
|
]
|
|||
|
|
cw = [230, 90, 300, 130, 80]
|
|||
|
|
rh = 34
|
|||
|
|
W = sum(cw) + 40
|
|||
|
|
H = rh * len(rows) + 70
|
|||
|
|
img = Image.new('RGB', (W, H), (22, 22, 26))
|
|||
|
|
d = ImageDraw.Draw(img)
|
|||
|
|
d.text((20, 14), 'WL-814x 팔레트 아틀라스 UV — 합격선 6항목 (WL_ArenaProto · 직교10 · 1080x1920 · 캐릭터 2996px)', fill=(255, 235, 160))
|
|||
|
|
for r, row in enumerate(rows):
|
|||
|
|
y = 48 + r * rh
|
|||
|
|
bg = (40, 40, 48) if r == 0 else ((30, 30, 36) if r % 2 else (26, 26, 31))
|
|||
|
|
d.rectangle([16, y, W - 16, y + rh - 2], fill=bg)
|
|||
|
|
x = 20
|
|||
|
|
for c, cell in enumerate(row):
|
|||
|
|
col = (235, 235, 235)
|
|||
|
|
if r > 0 and c == 4:
|
|||
|
|
col = (130, 230, 150)
|
|||
|
|
if r > 0 and c == 2:
|
|||
|
|
col = (150, 210, 255)
|
|||
|
|
d.text((x + 6, y + 9), str(cell), fill=col)
|
|||
|
|
x += cw[c]
|
|||
|
|
d.text((20, 48 + len(rows) * rh + 6), '* 단계 7 인 칸 1개 = 얼굴 데칼이 얹힌 머리 피부 칸(데칼은 별도 부위로 잼)', fill=(200, 200, 200))
|
|||
|
|
img.save(OUT + '/z_metrics.png')
|
|||
|
|
print('saved z_metrics.png', img.size)
|