vignette/docs/avatar-art/linocut-pipeline/scripts/_run_face_landmarks.py
Yun Chan ecb36d123f 리노컷 자산 파이프라인 공통화와 캐스트 외형 설계
- P1 전용 스크립트를 docs/avatar-art/linocut-pipeline 으로 옮겨 persona.json 설정으로 일반화(P1 재실행 리그 바이트 동일)
- 7명 외형·상징 설계(linocut-cast.md)와 P2~P7 정면 원화 생성 프롬프트, 얼굴 없는 화풍 참조
- P1 원화 생성 프롬프트 보존
2026-10-01 16:11:28 +09:00

49 lines
1.6 KiB
Python

"""단독 프로세스로 FaceLandmarker를 실행해 478 랜드마크를 JSON으로 출력한다.
(같은 프로세스에서 ImageSegmenter와 함께 쓰면 세그폴트가 재현되어 분리했다.)
사용: python _run_face_landmarks.py <이미지경로> <출력json경로>
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
import mediapipe as mp
import numpy as np
from mediapipe.tasks import python as mp_python
from mediapipe.tasks.python import vision
from PIL import Image
MODEL_FACE = Path(__file__).resolve().parent / "_models" / "face_landmarker.task"
def main() -> int:
image_path = Path(sys.argv[1])
out_path = Path(sys.argv[2])
base_options = mp_python.BaseOptions(model_asset_path=str(MODEL_FACE))
options = vision.FaceLandmarkerOptions(
base_options=base_options, running_mode=vision.RunningMode.IMAGE, num_faces=1
)
im = Image.open(image_path).convert("RGB")
w, h = im.size
arr = np.array(im)
mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=arr)
with vision.FaceLandmarker.create_from_options(options) as landmarker:
result = landmarker.detect(mp_image)
if not result.face_landmarks:
out_path.write_text(json.dumps({"ok": False}), encoding="utf-8")
print("FACE_LANDMARKS_FAILED")
return 1
lm = result.face_landmarks[0]
pts = [[p.x * w, p.y * h] for p in lm]
out_path.write_text(json.dumps({"ok": True, "width": w, "height": h, "points": pts}), encoding="utf-8")
print(f"FACE_LANDMARKS_OK n={len(pts)}")
return 0
if __name__ == "__main__":
sys.exit(main())