90 lines
6.0 KiB
Python
90 lines
6.0 KiB
Python
|
|
# 2차 시험: SD1.5 + Diablo4 LoRA만 · '단일 오브젝트 · 검정 배경 · 여백' 구도 강화 · 키잉 · 비교표까지 한 번에
|
||
|
|
import json, os, sys, time, uuid, urllib.request, urllib.parse
|
||
|
|
from PIL import Image, ImageDraw, ImageFont
|
||
|
|
|
||
|
|
HOST = "http://127.0.0.1:8000"
|
||
|
|
S = os.path.dirname(os.path.abspath(__file__))
|
||
|
|
OUT = os.path.join(S, "comfy_out2"); os.makedirs(OUT, exist_ok=True)
|
||
|
|
CKPT = "difconsistencyCheckpoint_v10.safetensors"; LORA = "Diablo4_sd15.safetensors"
|
||
|
|
STYLE = ("(single {subject}:1.3), game inventory item icon, isolated on solid pure black background, nothing else in the frame, "
|
||
|
|
"the whole object fully visible and centered with generous empty margin, blackened steel and dark leather with gold filigree, "
|
||
|
|
"warm orange glowing runes, subtle blue-white rim light, painted realistic dark fantasy diablo style, ultra detailed, sharp")
|
||
|
|
NEG = ("emblem, badge, medallion, circular frame, ornate border, background pattern, flames in background, glow filling the frame, cropped, cut off, "
|
||
|
|
"text, watermark, blurry, lowres, multiple objects, character, person, hands, ui panel, cartoon, anime")
|
||
|
|
SUBJECTS = [("helmet_knight", "closed knight helmet"), ("helmet_horned", "horned iron great helm"), ("ring_ruby", "gold ring with a red gem"),
|
||
|
|
("shield_kite", "kite shield with skull emblem"), ("orb_arcane", "crystal magic orb in an iron claw stand"), ("grimoire", "leather spellbook with rune lock"),
|
||
|
|
("quiver", "leather arrow quiver with arrows"), ("amulet", "gothic amulet on a chain")]
|
||
|
|
|
||
|
|
|
||
|
|
def get(path):
|
||
|
|
return json.load(urllib.request.urlopen(HOST + path, timeout=120))
|
||
|
|
|
||
|
|
|
||
|
|
def build(subject, seed):
|
||
|
|
return {"1": {"class_type": "CheckpointLoaderSimple", "inputs": {"ckpt_name": CKPT}},
|
||
|
|
"2": {"class_type": "LoraLoader", "inputs": {"model": ["1", 0], "clip": ["1", 1], "lora_name": LORA, "strength_model": 0.65, "strength_clip": 0.65}},
|
||
|
|
"4": {"class_type": "CLIPTextEncode", "inputs": {"clip": ["2", 1], "text": STYLE.format(subject=subject)}},
|
||
|
|
"5": {"class_type": "CLIPTextEncode", "inputs": {"clip": ["2", 1], "text": NEG}},
|
||
|
|
"6": {"class_type": "EmptyLatentImage", "inputs": {"width": 640, "height": 640, "batch_size": 1}},
|
||
|
|
"7": {"class_type": "KSampler", "inputs": {"model": ["2", 0], "positive": ["4", 0], "negative": ["5", 0], "latent_image": ["6", 0], "seed": seed, "steps": 32, "cfg": 7.5, "sampler_name": "dpmpp_2m", "scheduler": "karras", "denoise": 1.0}},
|
||
|
|
"8": {"class_type": "VAEDecode", "inputs": {"samples": ["7", 0], "vae": ["1", 2]}},
|
||
|
|
"9": {"class_type": "UpscaleModelLoader", "inputs": {"model_name": "4x-UltraSharp.pth"}},
|
||
|
|
"10": {"class_type": "ImageUpscaleWithModel", "inputs": {"upscale_model": ["9", 0], "image": ["8", 0]}},
|
||
|
|
"11": {"class_type": "ImageScale", "inputs": {"image": ["10", 0], "upscale_method": "lanczos", "width": 1024, "height": 1024, "crop": "disabled"}},
|
||
|
|
"13": {"class_type": "SaveImage", "inputs": {"images": ["11", 0], "filename_prefix": "bt25_icon2"}}}
|
||
|
|
|
||
|
|
|
||
|
|
def run(wf):
|
||
|
|
req = urllib.request.Request(HOST + "/prompt", data=json.dumps({"prompt": wf, "client_id": str(uuid.uuid4())}).encode(), headers={"Content-Type": "application/json"})
|
||
|
|
r = json.load(urllib.request.urlopen(req, timeout=60)); pid = r["prompt_id"]
|
||
|
|
for _ in range(240):
|
||
|
|
time.sleep(2)
|
||
|
|
h = get(f"/history/{pid}")
|
||
|
|
if pid in h:
|
||
|
|
if h[pid].get("status", {}).get("status_str") == "error":
|
||
|
|
return None
|
||
|
|
for nid, o in h[pid]["outputs"].items():
|
||
|
|
for im in o.get("images", []):
|
||
|
|
q = urllib.parse.urlencode({"filename": im["filename"], "subfolder": im.get("subfolder", ""), "type": im.get("type", "output")})
|
||
|
|
return urllib.request.urlopen(HOST + "/view?" + q, timeout=120).read()
|
||
|
|
return None
|
||
|
|
|
||
|
|
|
||
|
|
def key_black(src, dst):
|
||
|
|
im = Image.open(src).convert("RGBA"); px = im.load()
|
||
|
|
for y in range(im.height):
|
||
|
|
for x in range(im.width):
|
||
|
|
r, g, b, a = px[x, y]; m = max(r, g, b)
|
||
|
|
px[x, y] = (r, g, b, 0 if m < 16 else (255 if m > 64 else int((m - 16) * 255 / 48)))
|
||
|
|
im.save(dst)
|
||
|
|
|
||
|
|
|
||
|
|
t0 = time.time()
|
||
|
|
for i, (key, subject) in enumerate(SUBJECTS):
|
||
|
|
t = time.time()
|
||
|
|
data = run(build(subject, 917000 + i))
|
||
|
|
if data is None:
|
||
|
|
print(key, "FAILED"); continue
|
||
|
|
p = os.path.join(OUT, key + ".png")
|
||
|
|
with open(p, "wb") as f:
|
||
|
|
f.write(data)
|
||
|
|
key_black(p, p[:-4] + "_alpha.png")
|
||
|
|
print(key, f"ok {time.time() - t:.1f}s")
|
||
|
|
print(f"gen total {time.time() - t0:.0f}s")
|
||
|
|
|
||
|
|
# 비교표: FR 기존(위) · 2차 생성(아래)
|
||
|
|
u = r"C:\BurningTimes\FirstRising\Assets\GameAssets\UI\EquipmentItemIcon"
|
||
|
|
ref = [(os.path.join(u, n + ".png"), t) for n, t in [("1001", "FR 검"), ("1015", "FR 둔기"), ("1085", "FR 목걸이"), ("1155", "FR 장갑"), ("1225", "FR 갑옷"), ("1295", "FR 벨트"), ("1365", "FR 신발"), ("1043", "FR 단검")]]
|
||
|
|
ko = {"helmet_knight": "투구(기사)", "helmet_horned": "투구(뿔)", "ring_ruby": "반지", "shield_kite": "방패", "orb_arcane": "오브", "grimoire": "마도서", "quiver": "화살통", "amulet": "목걸이"}
|
||
|
|
gen = [(os.path.join(OUT, k + "_alpha.png"), "2차 " + ko[k]) for k, _ in SUBJECTS if os.path.exists(os.path.join(OUT, k + "_alpha.png"))]
|
||
|
|
cell, cols = 190, 8
|
||
|
|
sheet = Image.new("RGB", (cols * cell, 2 * (cell + 26)), (58, 58, 58)); d = ImageDraw.Draw(sheet)
|
||
|
|
font = ImageFont.truetype("C:/Windows/Fonts/malgunbd.ttf", 13)
|
||
|
|
for row, items in enumerate((ref, gen)):
|
||
|
|
for i, (p, t) in enumerate(items[:cols]):
|
||
|
|
im = Image.open(p).convert("RGBA"); im.thumbnail((cell - 10, cell - 10))
|
||
|
|
bg = Image.new("RGBA", (cell, cell), (58, 58, 58, 255)); bg.paste(im, ((cell - im.width) // 2, (cell - im.height) // 2), im)
|
||
|
|
x, y = i * cell, row * (cell + 26); sheet.paste(bg.convert("RGB"), (x, y))
|
||
|
|
d.text((x + 4, y + cell + 5), t, fill=(240, 220, 160) if row else (220, 220, 220), font=font)
|
||
|
|
sheet.save(os.path.join(S, "icon_compare2.png")); print("sheet ok", len(gen))
|