리노컷 공통 파이프라인 캐스트 확장 — 정렬·정규화·얼굴 해칭·원화 눈썹·눈 실측

- register_edit(편집본 ECC 정렬·챔퍼 지표), normalize_base(P1 머리 크기 정규화, 위쪽 종이색 채움)
- faceDetail region=face(얼굴 해칭을 원화 픽셀로 보존)·matchFacelessColor
- brow_sprite 단계: 원화 눈썹 잉크 스프라이트(hairFront 아래 제외), 윗눈꺼풀 두께·홍채 실측
- 리그 계약 RigBrow.halfWidth/sprite·RigEye.lineScale, 렌더러 눈썹 16띠·바깥쪽 눈꺼풀 굵기
- 실행기 UTF-8 하위 실행, brow_centerline 패딩본 자체 생성, QA 시트 촬영 도구(깜빡임 회피·고해상도 대조)
- P1은 새 필드를 모두 끄고 게시 자산·리그 바이트 동일
This commit is contained in:
Yun Chan 2026-10-01 22:38:27 +09:00
parent 9a22d1647b
commit 98577cf4ae
18 changed files with 2524 additions and 85 deletions

View file

@ -1,7 +1,7 @@
"""공통 리노컷 리그 파이프라인 — 단일 진입점.
페르소나 폴더 하나(예: docs/avatar-art/p1-linocut/, persona.json이 있어야 한다)에
대해 1~8단계를 정해진 의존 순서대로 실행한다. 각 단계는 독립 프로세스로 돌린다
대해 1~11단계를 정해진 의존 순서대로 실행한다. 각 단계는 독립 프로세스로 돌린다
(mediapipe FaceLandmarker+ImageSegmenter를 같은 프로세스에서 같이 쓰면 세그폴트가
나는 것과 같은 이유로, 모든 단계를 서브프로세스로 분리해 두면 한 단계의 문제가
다른 단계·전체 파이프라인을 오염시키지 않는다).
@ -15,11 +15,12 @@ export-rig-report.json을 읽으므로 반드시 export_rig *다음*이다):
3. segmentation — layers/{body,head,hairFront}.png(v1) + manifest 기준 섹션
4. layers_v2 — layers/v2/{body,head,hairFront}.png(결함 보정)
5. face_detail — layers/v2/face-detail.png
6. paper_grain — layers/v2/paper-grain.png(다른 단계와 독립, 순서 유연)
7. lip_texture — layers/v2/lip-{upper,lower,shadow}.png
8. jaw_pieces — layers/v2/jaw-{head,detail}.png
9. export_rig — WebP 게시 + rigs/<code>Rig.ts 생성
10. final_previews — 게시된 WebP로 모션·눈/입 확대 미리보기
6. brow_sprite — layers/v2/brow-{left,right}.png + manifest.eyeLineScale(결정문 §8.4.1)
7. paper_grain — layers/v2/paper-grain.png(다른 단계와 독립, 순서 유연)
8. lip_texture — layers/v2/lip-{upper,lower,shadow}.png
9. jaw_pieces — layers/v2/jaw-{head,detail}.png
10. export_rig — WebP 게시 + rigs/<code>Rig.ts 생성
11. final_previews — 게시된 WebP로 모션·눈/입 확대 미리보기
주의(재실행 순서): landmarks를 혼자 다시 돌리면 manifest.landmarks를 통째로
새로 쓰므로, brow_centerline이 보정한 eyebrowLeft/Right가 사라진다. landmarks만
@ -36,6 +37,7 @@ from __future__ import annotations
import argparse
import json
import os
import subprocess
import sys
import time
@ -52,6 +54,7 @@ STAGES: list[tuple[str, str]] = [
("segmentation", "segmentation.py"),
("layers_v2", "layers_v2.py"),
("face_detail", "face_detail.py"),
("brow_sprite", "brow_sprite.py"),
("paper_grain", "paper_grain.py"),
("lip_texture", "lip_texture.py"),
("jaw_pieces", "jaw_pieces.py"),
@ -64,9 +67,13 @@ STAGE_NAMES = [name for name, _ in STAGES]
def run_stage(name: str, script: str, persona_dir: Path) -> tuple[bool, str, float]:
script_path = SCRIPTS_DIR / script
t0 = time.monotonic()
# 단계 스크립트 출력은 UTF-8로 주고받는다. Windows 콘솔 기본(cp949)은 em dash 같은 문자를
# 못 그려, 모티프 없는 리그 분기(export_rig) 같은 데서 단계가 UnicodeEncodeError로 죽었다
# (2026-10-01 P7). 부모가 콘솔에 옮겨 쓸 때 못 그리는 문자는 main()에서 대체 문자로 바꾼다.
proc = subprocess.run(
[sys.executable, "-u", str(script_path), str(persona_dir)],
capture_output=True, text=True,
capture_output=True, text=True, encoding="utf-8", errors="replace",
env={**os.environ, "PYTHONIOENCODING": "utf-8"},
)
elapsed = time.monotonic() - t0
output = proc.stdout + proc.stderr
@ -75,6 +82,8 @@ def run_stage(name: str, script: str, persona_dir: Path) -> tuple[bool, str, flo
def main() -> int:
sys.stdout.reconfigure(errors="replace")
sys.stderr.reconfigure(errors="replace")
parser = argparse.ArgumentParser(description="공통 리노컷 파이프라인 실행")
parser.add_argument("persona_dir", nargs="?", help="페르소나 폴더(예: docs/avatar-art/p1-linocut)")
parser.add_argument("--only", choices=STAGE_NAMES, help="이 단계만 실행한다")