playreel/backend/services/styling/style_analysis.py

58 lines
2.0 KiB
Python

"""스타일링 ② — 레퍼런스 이미지에서 화풍 명세를 뽑는다
번들 템플릿 빌드와 사용자 업로드가 같은 경로를 탄다.
둘이 갈라지면 사용자 템플릿만 품질이 떨어지고 원인을 찾기 어려워진다.
"""
import io
from PIL import Image
from answers.style_prompt_answer import StylePromptAnswer
from models.styling import ReferenceVariants
from settings import settings
from utils.common_llm import StructuredLLM
from utils.image import to_data_uri
from utils.prompt import load_prompt
REFERENCE_LONG_EDGE = 2400 # 붓질이 보이는 최소선
THUMBNAIL_LONG_EDGE = 600 # 화면 카드
VISION_SIZE = (1536, 1536)
SMALL_REFERENCE_WARN = 600 # 이보다 작으면 결과가 나빠진다. 막지는 않는다
REFERENCE_QUALITY = 92
THUMBNAIL_QUALITY = 88
STYLE_PROMPT_PROMPT = load_prompt("style_prompt")
style_llm = StructuredLLM("gpt-4o", settings.chatgpt_api_key)
def encode(image: Image.Image, quality: int) -> bytes:
buffer = io.BytesIO()
image.save(buffer, "JPEG", quality=quality, optimize=True)
return buffer.getvalue()
def build_variants(raw: bytes) -> ReferenceVariants:
"""올린 바이트에서 전이용 고해상본과 카드용 썸네일을 만든다"""
Image.MAX_IMAGE_PIXELS = None
source = Image.open(io.BytesIO(raw)).convert("RGB")
source.thumbnail((REFERENCE_LONG_EDGE, REFERENCE_LONG_EDGE), Image.LANCZOS)
thumbnail = source.copy()
thumbnail.thumbnail((THUMBNAIL_LONG_EDGE, THUMBNAIL_LONG_EDGE), Image.LANCZOS)
return ReferenceVariants(reference=encode(source, REFERENCE_QUALITY),
thumbnail=encode(thumbnail, THUMBNAIL_QUALITY),
size=source.size)
async def analyze_style(reference: bytes) -> str:
Image.MAX_IMAGE_PIXELS = None
image = Image.open(io.BytesIO(reference)).convert("RGB")
answer = await style_llm.ask_with_images(
StylePromptAnswer, STYLE_PROMPT_PROMPT,
[("레퍼런스", to_data_uri(image, VISION_SIZE))], temperature=0.2)
return answer.style_prompt