feat(saju): 호령 캐릭터 PNG 6개 추출 (horyung.png + saju_color_sheet.png)

PIL-based extraction script with measured crop coordinates:
- horyung.png (1055x1491, 3-view layout): bust shot + front view
- saju_color_sheet.png (1536x1024, 6 emotion stickers row):
  greeting, thinking, pointing, happy (left 4 of 6)

Output files (public/images/saju/horyung/):
- horyung-bust.png (590x478)
- horyung-front.png (697x507)
- horyung-greeting.png (150x216)
- horyung-thinking.png (150x216)
- horyung-pointing.png (150x216)
- horyung-happy.png (150x216)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-26 08:20:36 +09:00
parent eab52ca424
commit 3e30612b38
7 changed files with 86 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -0,0 +1,86 @@
"""호령 캐릭터 PNG 추출.
source/characters/horyung.png (3 view layout: bust / back / front)
source/images/saju_page/saju_color_sheet.png (5 emotion stickers in bottom row)
에서 호령 PNG 6개를 추출하여 public/images/saju/horyung/에 저장.
"""
import os
from PIL import Image
SOURCE_ROOT = "../source"
HORYUNG_PATH = f"{SOURCE_ROOT}/characters/horyung.png"
COLORSHEET_PATH = f"{SOURCE_ROOT}/images/saju_page/saju_color_sheet.png"
OUT_DIR = "public/images/saju/horyung"
os.makedirs(OUT_DIR, exist_ok=True)
def crop_save(src_path, box, out_name):
im = Image.open(src_path).convert("RGBA")
cropped = im.crop(box)
cropped.save(f"{OUT_DIR}/{out_name}")
print(f"saved {out_name} ({cropped.size})")
# ---- horyung.png: 1055 x 1491 (3 vertical views) ----
# 1. BUST SHOT (top): character is slightly right of center.
# 2. BACK VIEW (middle): not used.
# 3. FRONT VIEW (bottom): character with sword, slightly right of center.
src_horyung = Image.open(HORYUNG_PATH)
HW, HH = src_horyung.size
print(f"horyung dims: {HW}x{HH}")
# bust shot — top 1/3, character occupies horizontal middle-right area
crop_save(
HORYUNG_PATH,
(int(HW * 0.22), int(HH * 0.01), int(HW * 0.78), int(HH * 0.33)),
"horyung-bust.png",
)
# front view — bottom 1/3. Measured: top of hat at y≈0.66, character spans x≈0.18~0.82
crop_save(
HORYUNG_PATH,
(int(HW * 0.16), int(HH * 0.66), int(HW * 0.82), int(HH * 1.0)),
"horyung-front.png",
)
# ---- saju_color_sheet.png: 1536 x 1024 ----
# Bottom row contains 6 emotion stickers laid horizontally.
# Visual inspection: stickers occupy roughly x=0.439~0.986, y=0.82~1.0.
# We need 4 stickers (greeting, thinking, pointing, happy) from positions 0~3.
src_sheet = Image.open(COLORSHEET_PATH)
SW, SH = src_sheet.size
print(f"colorsheet dims: {SW}x{SH}")
# 6 stickers in a single row at bottom-right
# Measured visually with red grid overlay:
# Sticker 1 (greeting): x = 600..750
# Sticker 2 (thinking): x = 750..900
# Sticker 3 (pointing): x = 900..1050
# Sticker 4 (happy): x = 1050..1200
# Sticker 5 (caution): x = 1200..1350 (unused)
# Sticker 6 (lucky): x = 1350..1500 (unused)
EMO_X_START = 600
EMO_X_END = 1500
EMO_Y_START = int(SH * 0.79)
EMO_Y_END = int(SH * 1.00)
EMO_COLS = 6
EMO_W = (EMO_X_END - EMO_X_START) / EMO_COLS
EMO_H = EMO_Y_END - EMO_Y_START
# left-to-right: greeting, thinking, pointing, happy, (5th: unused)
positions = [
("horyung-greeting.png", 0),
("horyung-thinking.png", 1),
("horyung-pointing.png", 2),
("horyung-happy.png", 3),
]
for name, col in positions:
x1 = int(EMO_X_START + col * EMO_W)
y1 = EMO_Y_START
x2 = int(EMO_X_START + (col + 1) * EMO_W)
y2 = EMO_Y_END
crop_save(COLORSHEET_PATH, (x1, y1, x2, y2), name)
print("Done.")