LLM멀티모달 AI · 2중급

이미지 이해 — Vision LLM 활용

LLMVision이미지분석GPT-4oOCR구조화

이미지 전송 방식

import base64
from openai import OpenAI
from pathlib import Path

client = OpenAI()

# 방법 1: URL로 전송
def analyze_image_url(image_url: str, prompt: str) -> str:
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{
            "role": "user",
            "content": [
                {"type": "text", "text": prompt},
                {"type": "image_url", "image_url": {"url": image_url}},
            ]
        }],
        max_tokens=1000,
    )
    return response.choices[0].message.content

# 방법 2: base64로 로컬 파일 전송
def analyze_local_image(image_path: str, prompt: str) -> str:
    with open(image_path, "rb") as f:
        image_data = base64.b64encode(f.read()).decode("utf-8")

    # MIME 타입 감지
    suffix = Path(image_path).suffix.lower()
    mime_type = {"jpg": "jpeg", "jpeg": "jpeg", "png": "png", "gif": "gif", "webp": "webp"}.get(suffix[1:], "jpeg")

    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{
            "role": "user",
            "content": [
                {"type": "text", "text": prompt},
                {"type": "image_url", "image_url": {
                    "url": f"data:image/{mime_type};base64,{image_data}"
                }},
            ]
        }],
    )
    return response.choices[0].message.content

영수증/문서 구조화

from pydantic import BaseModel
from typing import Optional

class ReceiptItem(BaseModel):
    name: str
    quantity: int
    unit_price: int
    total: int

class Receipt(BaseModel):
    store_name: str
    date: str
    items: list[ReceiptItem]
    subtotal: int
    tax: Optional[int] = None
    total: int

def extract_receipt(image_path: str) -> Receipt:
    with open(image_path, "rb") as f:
        image_data = base64.b64encode(f.read()).decode("utf-8")

    response = client.beta.chat.completions.parse(
        model="gpt-4o",
        messages=[{
            "role": "user",
            "content": [
                {"type": "text", "text": "이 영수증에서 정보를 추출해주세요."},
                {"type": "image_url", "image_url": {
                    "url": f"data:image/jpeg;base64,{image_data}"
                }},
            ]
        }],
        response_format=Receipt,
    )
    return response.choices[0].message.parsed

# 사용
receipt = extract_receipt("receipt.jpg")
print(f"가게: {receipt.store_name}")
print(f"합계: {receipt.total:,}원")
for item in receipt.items:
    print(f"  - {item.name}: {item.unit_price:,}원 × {item.quantity}")

차트/그래프 분석

def analyze_chart(image_path: str) -> dict:
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": """이 차트를 분석해주세요:
1. 차트 종류와 제목
2. X축, Y축 의미
3. 주요 데이터 포인트나 트렌드
4. 핵심 인사이트 (2~3가지)

JSON 형식으로 응답해주세요."""
                },
                {"type": "image_url", "image_url": {
                    "url": f"data:image/png;base64,{encode_image(image_path)}"
                }},
            ]
        }],
        response_format={"type": "json_object"},
    )
    return json.loads(response.choices[0].message.content)

여러 이미지 비교

def compare_images(image_paths: list[str], question: str) -> str:
    content = [{"type": "text", "text": question}]

    for i, path in enumerate(image_paths):
        content.append({
            "type": "text",
            "text": f"이미지 {i+1}:"
        })
        content.append({
            "type": "image_url",
            "image_url": {"url": f"data:image/jpeg;base64,{encode_image(path)}"}
        })

    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": content}],
    )
    return response.choices[0].message.content

# 사용: 제품 이미지 A/B 비교
result = compare_images(
    ["product_v1.jpg", "product_v2.jpg"],
    "두 제품 디자인의 차이점을 분석하고, 어느 것이 더 사용자 친화적인지 평가해주세요."
)

Claude로 이미지 분석

import anthropic
import httpx

client = anthropic.Anthropic()

# URL에서 바이트로 다운로드
def analyze_with_claude(image_url: str, prompt: str) -> str:
    image_data = base64.standard_b64encode(
        httpx.get(image_url).content
    ).decode("utf-8")

    message = client.messages.create(
        model="claude-opus-4-7",
        max_tokens=1024,
        messages=[{
            "role": "user",
            "content": [
                {
                    "type": "image",
                    "source": {
                        "type": "base64",
                        "media_type": "image/jpeg",
                        "data": image_data,
                    },
                },
                {"type": "text", "text": prompt}
            ],
        }],
    )
    return message.content[0].text

정리

방식장점단점
URL 전송간단외부 접근 가능해야 함
base64로컬 파일 가능요청 크기 증가
Structured Output타입 안전복잡한 구조

다음 편에서는 이미지 생성 — DALL-E 3와 Stable Diffusion으로 이미지를 생성하는 방법을 배웁니다.

궁금한 점이 있으신가요?

협업·의뢰는 아래로, 가벼운 소통은 인스타그램 @bluefox._.hi도 환영이에요.