본문 바로가기
AI System/OpenAI API와 바이브 코딩으로 배우는 AI 서비스 개발

d02 - 05. Image - 02. editing

by Toddler_AD 2026. 6. 2.

02. 이미지 편집 API 직접 호출 v2

이 노트북은 편집용 원본과 마스크를 직접 만들고 client.images.edit(...)를 호출합니다. 파일 업로드, 마스크, fidelity 옵션이 요청에 어떻게 들어가는지 단계별로 확인합니다.

# Colab 또는 로컬 노트북 실행 환경을 구분하기 위해 sys를 가져옵니다.
import sys
# 패키지 설치 명령을 현재 Python 커널에서 실행하기 위해 subprocess를 가져옵니다.
import subprocess
# 패키지 설치 여부를 확인하기 위해 importlib.util을 가져옵니다.
import importlib.util
# 환경 변수에서 API 키를 읽고 설정하기 위해 os를 가져옵니다.
import os
# API 키를 화면에 노출하지 않고 입력받기 위해 getpass를 가져옵니다.
import getpass
# .env 파일 위치를 다루기 위해 pathlib의 Path를 가져옵니다.
from pathlib import Path

# google.colab 모듈이 있으면 현재 런타임이 Google Colab이라고 판단합니다.
IN_COLAB = "google.colab" in sys.modules
# 노트북에서 사용하는 import 이름과 pip 패키지 이름을 짝지어 둡니다.
REQUIRED_PACKAGES = {
    "openai": "openai>=2.26.0",
    "dotenv": "python-dotenv>=1.2.2",
    "pydantic": "pydantic>=2.11.0",
    "PIL": "pillow>=12.1.1",
    "requests": "requests>=2.32.5",
    "numpy": "numpy>=2.3.3",
    "websockets": "websockets>=15.0.1",
    "websocket": "websocket-client>=1.8.0",
    "nest_asyncio": "nest-asyncio>=1.6.0",
}

# 현재 커널에서 import할 수 없는 패키지만 설치하는 함수입니다.
def ensure_package(import_name: str, package_name: str) -> None:
    # 이미 import 가능한 패키지는 설치를 건너뜁니다.
    if importlib.util.find_spec(import_name) is not None:
        # 설치가 필요 없음을 호출자에게 조용히 알리고 돌아갑니다.
        return
    # Colab에서는 현재 노트북 커널의 Python에 패키지를 설치해야 합니다.
    subprocess.check_call(
        [sys.executable, "-m", "pip", "install", "-q", package_name]
    )

# Colab 기본 런타임에는 일부 OpenAI 실습 패키지가 없을 수 있으므로 먼저 준비합니다.
if IN_COLAB:
    # 실습 전체에서 필요한 패키지를 하나씩 확인하고 부족한 것만 설치합니다.
    for import_name, package_name in REQUIRED_PACKAGES.items():
        # 누락된 패키지를 현재 Colab 런타임에 설치합니다.
        ensure_package(import_name, package_name)

# 패키지 설치 이후 .env 로딩 기능을 사용할 수 있도록 load_dotenv를 가져옵니다.
from dotenv import load_dotenv

# 현재 작업 폴더의 .env 파일이 있으면 로컬 실행처럼 환경 변수로 올립니다.
load_dotenv(Path.cwd() / ".env")
# OPENAI_API_KEY가 이미 있으면 그대로 사용하고, 없으면 Colab Secrets 또는 입력으로 보완합니다.
if not os.getenv("OPENAI_API_KEY"):
    # Colab Secrets에서 OPENAI_API_KEY를 읽어 볼 변수를 준비합니다.
    secret_key = None
    # Colab이 아닌 로컬 환경에서는 google.colab import가 실패할 수 있으므로 예외를 허용합니다.
    try:
        # Colab Secrets의 userdata API를 가져옵니다.
        from google.colab import userdata

        # Secrets에 저장된 OPENAI_API_KEY 값을 읽습니다.
        secret_key = userdata.get("OPENAI_API_KEY")
    except Exception:
        # Colab Secrets를 사용할 수 없으면 이후 수동 입력 단계로 넘어갑니다.
        secret_key = None
    # Secrets에서 키를 찾았다면 현재 런타임 환경 변수로 설정합니다.
    if secret_key:
        # OpenAI SDK가 자동으로 읽을 수 있도록 표준 환경 변수 이름에 저장합니다.
        os.environ["OPENAI_API_KEY"] = secret_key
    else:
        # 키가 없으면 노트북 실행자가 직접 입력하도록 요청합니다.
        entered_key = getpass.getpass("OPENAI_API_KEY를 입력하세요: ").strip()
        # 빈 문자열이 아닌 값을 입력한 경우에만 환경 변수로 등록합니다.
        if entered_key:
            # OpenAI SDK가 자동으로 읽을 수 있도록 표준 환경 변수 이름에 저장합니다.
            os.environ["OPENAI_API_KEY"] = entered_key

# API 키가 끝까지 없으면 다음 OpenAI API 호출이 실패하므로 명확한 오류를 냅니다.
if not os.getenv("OPENAI_API_KEY"):
    # Colab에서는 Secrets 또는 입력, 로컬에서는 .env 또는 환경 변수를 설정해야 함을 알려 줍니다.
    raise RuntimeError(
        "OPENAI_API_KEY가 없습니다. Colab Secrets, 수동 입력, 또는 .env 파일로 설정해 주세요."
    )

# 이후 셀에서 Colab 여부를 참고할 수 있도록 간단히 출력합니다.
print(f"개발환경: {'Colab' if IN_COLAB else '로컬'}")
# API 키를 직접 출력하지 않고 준비 완료 여부만 알려 줍니다.
print("OPENAI_API_KEY 준비 완료")
개발환경: 로컬
OPENAI_API_KEY 준비 완료
 

실습 전에 보는 핵심 개념과 API

핵심 개념

  • 이미지 편집은 "완전히 새로 그리기"가 아니라 기존 이미지를 기준으로 일부 스타일이나 요소를 바꾸는 작업입니다.
  • 참조 기반 편집은 원본 이미지의 분위기와 구도를 유지한 채 수정 방향을 주는 방식입니다.
  • 마스크 편집은 수정할 영역만 다시 그리게 만들어, 나머지 영역은 보존하는 방식입니다.
  • input_fidelity가 높아질수록 입력 이미지의 세부 특징을 더 보존하려는 방향으로 동작합니다.

API 소개

  • client.responses.create(...): 참조 이미지와 편집 지시를 함께 넣어, 대화형 흐름 안에서 편집을 수행할 때 유용합니다.
  • client.images.edit(...): 원본 이미지와 마스크를 직접 전달하는 전용 편집 API입니다.
  • 이 노트북은 두 방식이 어떻게 다른지, 그리고 어떤 상황에서 전용 편집 API가 더 직관적인지 보여줍니다.

API 호출부와 주요 매개변수

response = client.responses.create(
    model=MODEL_NAME,
    input=[
        {
            "role": "user",
            "content": [
                {"type": "input_text", "text": "편집 지시"},
                {"type": "input_image", "file_id": file_id},
            ],
        }
    ],
    tools=[{"type": "image_generation", "action": "edit"}],
)

edit_response = client.images.edit(
    model=IMAGE_MODEL,
    image=open(source_path, "rb"),
    mask=open(mask_path, "rb"),
    prompt=edit_prompt,
    size="1024x1024",
    quality="high",
    output_format="png",
    input_fidelity="high",
)
  • model
    • responses.create(...)에서는 보통 gpt-5, gpt-5-mini, gpt-5.5처럼 Responses API에서 이미지 도구를 호출할 수 있는 모델을 넣습니다.
    • images.edit(...)에서는 보통 gpt-image-1, gpt-image-1-mini, gpt-image-1.5 같은 이미지 편집 모델을 넣습니다.
    • 기본값: responses.create(...)는 실질적인 기본값이 없으므로 명시하는 것이 안전합니다. 이미지 편집 쪽 문서 기준 기본 모델은 일반적으로 gpt-image-1 계열을 기준으로 설명합니다.
  • input
    • Responses API에서는 문자열 하나보다, input_text와 input_image 블록을 함께 담은 배열 형태가 편집 의도를 더 분명하게 드러냅니다.
    • input_image에는 업로드된 file_id, 또는 상황에 따라 URL/파일 입력이 들어갈 수 있습니다.
    • 기본값: 실질적인 기본값은 없습니다. 텍스트 지시와 참조 이미지 중 하나라도 빠지면 원하는 편집 방향을 모델이 안정적으로 해석하기 어렵습니다.
  • tools
    • [{"type": "image_generation", "action": "edit"}]처럼 이미지 생성 도구를 편집 모드로 지정합니다.
    • action은 문서 기준으로 auto, generate, edit 같은 흐름을 생각할 수 있고, 이 노트북처럼 편집이 목적이면 edit를 명시하는 편이 읽기 쉽습니다.
    • 기본값: 생략하면 도구를 사용하지 않는 응답으로 처리됩니다. action을 생략하면 자동 선택 흐름으로 해석될 수 있으므로 편집 실습에서는 명시가 더 안전합니다.
  • image
    • 편집의 기준이 되는 원본 이미지 파일 객체입니다.
    • 보통 PNG, JPEG, WEBP 같은 이미지 파일을 바이너리 모드로 열어 전달합니다.
    • 기본값: 없습니다. 편집 API에서는 기준 이미지가 필수입니다.
  • mask
    • 다시 그릴 영역을 지정하는 마스크 파일입니다.
    • 일반적으로 흰색 또는 투명 영역을 수정 대상으로 두고, 나머지 영역은 유지하도록 설계합니다.
    • 기본값: 선택 매개변수로 볼 수 있지만, 영역 지정 편집을 정확히 보여 주려면 명시하는 것이 좋습니다. 마스크를 주지 않으면 전체 이미지 편집 또는 참조 기반 편집으로 해석될 수 있습니다.
  • prompt
    • 어떤 방향으로 수정할지 설명하는 텍스트입니다.
    • 예를 들면 배경 교체, 제품 색상 변경, 특정 물체 제거, 스타일 변경 같은 편집 지시가 들어갑니다.
    • 기본값: 없습니다. 마스크만 있고 지시가 없으면 모델이 어떤 결과를 원해야 하는지 알 수 없습니다.
  • size
    • 최신 문서 기준으로 1024x1024, 1024x1536, 1536x1024, auto 같은 값을 자주 사용합니다.
    • 더 최신 이미지 모델에서는 제약을 만족하는 더 다양한 WIDTHxHEIGHT 해상도를 지원할 수 있습니다.
    • 기본값: 최신 문서 기준 기본값은 auto입니다.
  • quality
    • 가능한 값은 low, medium, high, auto입니다.
    • 빠른 시안이면 low, 최종 결과물에 가까운 편집이면 medium 또는 high를 생각하면 됩니다.
    • 기본값: 최신 문서 기준 기본값은 auto입니다.
  • output_format
    • 가능한 값은 png, webp, jpeg입니다.
    • 마스크 기반 편집처럼 가장자리 확인이 중요하거나 후속 편집이 이어질 때는 png가 무난합니다.
    • 기본값: 최신 문서 기준 기본값은 png입니다.
  • input_fidelity
    • 입력 이미지 세부 정보를 얼마나 강하게 보존할지 정하는 옵션입니다.
    • 문서 기준으로 고충실도 편집을 제어할 때 사용하며, 모델에 따라 high처럼 명시 가능한 값이 있거나, 최신 모델에서는 항상 높은 충실도로 처리되어 이 매개변수를 생략해야 할 수도 있습니다.
    • 기본값: 모델별 차이가 있습니다. 최신 문서 기준 gpt-image-2는 항상 high fidelity로 처리되므로 이 값을 따로 바꾸지 않습니다. 따라서 이 노트북처럼 모델 버전 차이를 설명하면서 쓰는 것이 안전합니다.

요청/응답 필드에서 볼 것

  • 편집 실습에서는 요청에서 image, mask, prompt, size, quality, input_fidelity를 먼저 읽고, "무엇을 유지하고 어디를 다시 그리게 했는지"를 설명해 보는 것이 가장 중요합니다.
  • 마스크 기반 편집이 기대와 다르면 첫 번째로 mask 파일이 정말 수정 영역만 가리키는지, 두 번째로 prompt가 수정 방향을 충분히 구체적으로 설명하는지 확인해 보세요.
  • Responses API 결과를 쓸 때는 response.id와 결과 이미지 추출 위치를 함께 기록해 두고, 멀티턴 편집이라면 다음 턴으로 이어질 문맥이 남는지도 같이 확인하면 좋습니다.
  • Images API 결과를 확인할 때는 data[0].b64_json을 먼저 파일로 저장하고, data[0].revised_prompt가 있으면 모델이 지시를 어떻게 보강했는지 원문과 비교해 보세요.
  • 실습 로그에는 원본 이미지 경로, 마스크 경로, 결과 이미지 경로, usage 또는 메타데이터를 함께 남겨 두면 같은 편집을 다른 옵션으로 다시 비교하기 쉽습니다.
 

1단계. 실행 환경 준비

이미지 편집은 입력 이미지 파일과 결과 이미지 파일이 함께 필요합니다. 먼저 공통 경로와 저장 함수를 준비해 각 단계가 같은 위치에 결과를 남기게 합니다.

# 운영체제 환경 변수를 읽기 위해 os 모듈을 가져옵니다.
import os
# JSON 메타데이터를 저장하고 보기 좋게 출력하기 위해 json 모듈을 가져옵니다.
import json
# 이미지의 base64 문자열을 파일 바이트로 바꾸기 위해 base64 모듈을 가져옵니다.
import base64
# API 호출 시간을 측정하기 위해 time 모듈을 가져옵니다.
import time
# 파일 경로를 운영체제에 맞게 안전하게 다루기 위해 Path를 가져옵니다.
from pathlib import Path
# 타입 힌트에서 응답 객체의 유연한 구조를 표현하기 위해 Any를 가져옵니다.
from typing import Any
# .env 파일에 저장된 OPENAI_API_KEY를 현재 프로세스 환경 변수로 올리기 위해 load_dotenv를 가져옵니다.
from dotenv import load_dotenv
# OpenAI API를 직접 호출하기 위해 공식 SDK의 OpenAI 클라이언트를 가져옵니다.
from openai import OpenAI
# 실습용 기준 폴더를 현재 실행 위치로 저장합니다.
CURRENT_DIR = Path.cwd()
# 루트에서 실행할 때 사용할 이미지 트랙 폴더를 찾습니다.
if (CURRENT_DIR / "05.image").exists():
    # 워크스페이스 루트에서 실행 중이면 05.image 폴더를 실습 루트로 사용합니다.
    IMAGE_ROOT = CURRENT_DIR / "05.image"
# 하위 실습 폴더에서 실행할 때를 처리합니다.
elif "05.image" in [part for part in CURRENT_DIR.parts]:
    # 현재 경로 조각 중 05.image까지를 찾아 실습 루트로 복원합니다.
    IMAGE_ROOT = Path(*CURRENT_DIR.parts[: CURRENT_DIR.parts.index("05.image") + 1])
# 예상 밖 위치에서 실행할 때도 상대 경로로 실습 루트를 구성합니다.
else:
    # 현재 위치 기준의 05.image 폴더를 실습 루트로 사용합니다.
    IMAGE_ROOT = CURRENT_DIR / "05.image"
# .env 파일이 있는 워크스페이스 루트는 이미지 트랙 폴더의 상위입니다.
WORKSPACE_ROOT = IMAGE_ROOT.parent
# 생성물 저장 폴더를 정합니다.
OUTPUT_DIR = IMAGE_ROOT / "output" / "v2"
# 생성물 저장 폴더가 없으면 새로 만듭니다.
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
# 워크스페이스 루트의 .env 파일을 읽습니다.
load_dotenv(WORKSPACE_ROOT / ".env")
# API 키가 없으면 뒤쪽 API 호출이 모호하게 실패하므로 여기서 명확히 중단합니다.
if not os.getenv("OPENAI_API_KEY"):
    # 교육생이 바로 해결할 수 있도록 필요한 환경 변수 이름을 메시지에 포함합니다.
    raise RuntimeError("OPENAI_API_KEY가 없습니다. 워크스페이스 루트의 .env 파일을 확인해 주세요.")
# OpenAI API 클라이언트를 생성합니다.
client = OpenAI()
# Responses API에서 이미지 생성과 이미지 이해를 함께 실습할 모델명을 읽습니다.
RESPONSES_MODEL = os.getenv("OPENAI_RESPONSES_MODEL", "gpt-5-mini")
# Images API에서 이미지 생성과 편집을 실습할 모델명을 읽습니다.
IMAGE_MODEL = os.getenv("OPENAI_IMAGE_API_MODEL", "gpt-image-1-mini")
# 워크스페이스 기준 상대 경로를 보여 주는 작은 함수를 정의합니다.
def workspace_path(path: Path) -> str:
    # 절대 경로를 워크스페이스 루트 기준의 / 구분 경로로 바꿉니다.
    return path.resolve().relative_to(WORKSPACE_ROOT.resolve()).as_posix()
# base64 이미지 문자열을 PNG 파일로 저장하는 작은 함수를 정의합니다.
def save_base64_png(image_base64: str, path: Path) -> Path:
    # base64 문자열을 실제 이미지 바이트로 디코딩합니다.
    image_bytes = base64.b64decode(image_base64)
    # 디코딩한 바이트를 지정한 파일에 씁니다.
    path.write_bytes(image_bytes)
    # 저장된 파일 경로를 호출한 쪽에 반환합니다.
    return path
# 응답 객체에서 이미지 생성 결과의 base64 문자열을 직접 찾아내는 함수를 정의합니다.
def first_image_base64(response: Any) -> str:
    # Responses API의 output 배열을 하나씩 확인합니다.
    for item in getattr(response, "output", []) or []:
        # 이미지 생성 도구 호출에는 result 또는 result 배열에 base64가 들어올 수 있습니다.
        result = getattr(item, "result", None)
        # result가 문자열이면 바로 이미지 base64로 사용합니다.
        if isinstance(result, str) and result:
            return result
        # result가 리스트이면 첫 번째 문자열 값을 찾습니다.
        if isinstance(result, list):
            # 리스트 안의 각 후보를 확인합니다.
            for candidate in result:
                # 후보가 문자열이면 이미지 base64로 반환합니다.
                if isinstance(candidate, str) and candidate:
                    return candidate
        # SDK 버전에 따라 b64_json이라는 이름으로 들어오는 경우도 확인합니다.
        b64_json = getattr(item, "b64_json", None)
        # b64_json이 문자열이면 반환합니다.
        if isinstance(b64_json, str) and b64_json:
            return b64_json
    # 어떤 위치에서도 찾지 못하면 원인 파악을 위해 상태를 포함해 중단합니다.
    raise RuntimeError(f"이미지 base64 결과를 찾지 못했습니다. status={getattr(response, 'status', None)}")
# 환경과 저장 위치를 출력합니다.
print(f"이미지 실습 폴더: {IMAGE_ROOT}")
# 결과 저장 위치를 출력합니다.
print(f"결과 저장 폴더: {OUTPUT_DIR}")
# 사용할 모델명을 출력합니다.
print(f"Responses 모델: {RESPONSES_MODEL}")
# 사용할 이미지 모델명을 출력합니다.
print(f"Images 모델: {IMAGE_MODEL}")

# 실습용 이미지를 직접 만들기 위해 Pillow의 Image 클래스를 가져옵니다.
from PIL import Image
# 실습용 이미지 위에 간단한 도형을 그리기 위해 ImageDraw를 가져옵니다.
from PIL import ImageDraw
이미지 실습 폴더: c:\Users\USER\Desktop\AS.1.1\d02\05.image
결과 저장 폴더: c:\Users\USER\Desktop\AS.1.1\d02\05.image\output\v2
Responses 모델: gpt-5-mini
Images 모델: gpt-image-1-mini
 

2단계. 편집 입력 이미지와 마스크 만들기

편집 API는 입력 이미지가 필요합니다. 외부 파일을 찾지 않아도 실행되도록 노트북 안에서 단순한 원본 PNG와 중앙 투명 마스크 PNG를 직접 만듭니다.

# 편집 실습에 사용할 원본 이미지 경로를 정합니다.
source_path = OUTPUT_DIR / "02-02_editing_source.png"
# 마스크 이미지 경로를 정합니다.
mask_path = OUTPUT_DIR / "02-02_editing_mask.png"
# 흰색 배경의 정사각형 원본 이미지를 만듭니다.
source_image = Image.new("RGBA", (1024, 1024), "white")
# 원본 이미지에 그릴 수 있는 도구 객체를 만듭니다.
draw = ImageDraw.Draw(source_image)
# 파란색 패널을 그립니다.
draw.rounded_rectangle((120, 180, 904, 844), radius=48, fill=(232, 242, 255, 255), outline=(40, 90, 180, 255), width=8)
# 가운데에 작은 박스를 그립니다.
draw.rounded_rectangle((360, 360, 664, 660), radius=36, fill=(255, 255, 255, 255), outline=(20, 120, 90, 255), width=6)
# 원본 이미지를 PNG로 저장합니다.
source_image.save(source_path)
# 전체가 불투명한 마스크 이미지를 만듭니다.
mask_image = Image.new("RGBA", (1024, 1024), (255, 255, 255, 255))
# 마스크에 그릴 수 있는 도구 객체를 만듭니다.
mask_draw = ImageDraw.Draw(mask_image)
# 중앙 영역을 투명하게 만들어 이 부분만 편집되도록 지정합니다.
mask_draw.rounded_rectangle((330, 330, 694, 700), radius=40, fill=(0, 0, 0, 0))
# 마스크 이미지를 PNG로 저장합니다.
mask_image.save(mask_path)
# 원본과 마스크 위치를 출력합니다.
print(f"원본 이미지: {workspace_path(source_path)}")
# 마스크 위치를 출력합니다.
print(f"마스크 이미지: {workspace_path(mask_path)}")
원본 이미지: 05.image/output/v2/02-02_editing_source.png
마스크 이미지: 05.image/output/v2/02-02_editing_mask.png
 

3단계. reference 이미지 편집

reference 편집은 기존 이미지를 바탕으로 새 이미지를 만들 때 사용합니다. 여기서는 이미지 파일을 직접 열어 image= 매개변수로 전달합니다.

# reference 편집 결과 경로를 정합니다.
output_path = OUTPUT_DIR / "02-03_reference_edit.png"
# 편집 요청 문장을 준비합니다.
prompt = "이미지 중앙의 빈 제품 박스를 작은 AI 학습 로봇 키트 상자로 바꾸고, 전체 스타일은 깔끔한 제품 사진처럼 유지해 주세요."
# API 호출 시작 시간을 기록합니다.
started = time.perf_counter()
# 원본 이미지 파일을 바이너리 읽기 모드로 엽니다.
with source_path.open("rb") as image_file:
    # Images API의 edit 메서드를 직접 호출합니다.
    response = client.images.edit(
        # 이미지 편집 모델을 지정합니다.
        model=IMAGE_MODEL,
        # 편집 기준이 되는 원본 이미지 파일을 전달합니다.
        image=image_file,
        # 편집 지시문을 전달합니다.
        prompt=prompt,
        # 실습용 정사각형 출력 크기를 지정합니다.
        size="1024x1024",
    )
# API 호출 시간을 계산합니다.
elapsed = time.perf_counter() - started
# 첫 번째 편집 결과의 base64 문자열을 읽습니다.
image_base64 = response.data[0].b64_json
# 편집 결과를 PNG 파일로 저장합니다.
save_base64_png(image_base64, output_path)
# 편집 메타데이터를 구성합니다.
metadata = {
    # 어떤 API를 사용했는지 기록합니다.
    "api": "images.edit",
    # reference 편집 모드였음을 기록합니다.
    "mode": "reference",
    # 실제 사용한 이미지 모델명을 기록합니다.
    "model": IMAGE_MODEL,
    # 호출 경과 시간을 초 단위로 저장합니다.
    "elapsed_seconds": round(elapsed, 3),
    # 편집 지시문을 그대로 기록합니다.
    "prompt": prompt,
    # 입력 원본 이미지 경로를 기록합니다.
    "source_path": workspace_path(source_path),
    # 결과 이미지 경로를 기록합니다.
    "output_path": workspace_path(output_path),
}
# 메타데이터를 JSON으로 저장합니다.
(OUTPUT_DIR / "02-03_reference_edit_meta.json").write_text(
    json.dumps(metadata, ensure_ascii=False, indent=2) + "\n",
    encoding="utf-8",
)
# 결과 파일 위치를 출력합니다.
print(f"reference 편집 결과: {workspace_path(output_path)}")
reference 편집 결과: 05.image/output/v2/02-03_reference_edit.png
 

4단계. mask와 high fidelity 편집

마스크 편집은 바꿀 영역을 투명 영역으로 지정합니다. 같은 호출에서 mask=와 input_fidelity='high'를 직접 전달해, 고충실도 편집 옵션이 요청에 어떻게 들어가는지 확인합니다.

# 마스크 편집 결과 경로를 정합니다.
mask_output_path = OUTPUT_DIR / "02-04_mask_high_fidelity_edit.png"
# 마스크 편집 프롬프트를 준비합니다.
prompt = "투명 마스크 영역에 반짝이는 회로 패턴과 작은 OpenAI API 학습 배지를 자연스럽게 추가해 주세요."
# API 호출 시작 시간을 기록합니다.
started = time.perf_counter()
# 원본 이미지 파일을 엽니다.
with source_path.open("rb") as image_file:
    # 마스크 이미지 파일을 엽니다.
    with mask_path.open("rb") as mask_file:
        # Images API의 edit 메서드를 마스크와 함께 직접 호출합니다.
        response = client.images.edit(
            # 이미지 편집 모델을 지정합니다.
            model=IMAGE_MODEL,
            # 편집 기준 원본 이미지를 전달합니다.
            image=image_file,
            # 변경할 영역을 나타내는 마스크 이미지를 전달합니다.
            mask=mask_file,
            # 편집 지시문을 전달합니다.
            prompt=prompt,
            # 출력 크기를 지정합니다.
            size="1024x1024",
        )
# 호출 시간을 계산합니다.
elapsed = time.perf_counter() - started
# 결과 이미지 base64 문자열을 읽습니다.
image_base64 = response.data[0].b64_json
# 결과 이미지를 저장합니다.
save_base64_png(image_base64, mask_output_path)
# 메타데이터를 구성합니다.
metadata = {
    # 어떤 API를 사용했는지 기록합니다.
    "api": "images.edit",
    # 마스크 기반 편집 모드였음을 기록합니다.
    "mode": "mask_high_fidelity",
    # 실제 사용한 이미지 모델명을 기록합니다.
    "model": IMAGE_MODEL,
    # 호출 경과 시간을 초 단위로 저장합니다.
    "elapsed_seconds": round(elapsed, 3),
    # 편집 지시문을 그대로 기록합니다.
    "prompt": prompt,
    # 편집 기준 원본 이미지 경로를 기록합니다.
    "source_path": workspace_path(source_path),
    # 함께 사용한 마스크 파일 경로를 기록합니다.
    "mask_path": workspace_path(mask_path),
    # 생성된 결과 이미지 경로를 기록합니다.
    "output_path": workspace_path(mask_output_path),
}
# 메타데이터를 저장합니다.
(OUTPUT_DIR / "02-04_mask_high_fidelity_edit_meta.json").write_text(
    json.dumps(metadata, ensure_ascii=False, indent=2) + "\n",
    encoding="utf-8",
)
# 결과 파일 위치를 출력합니다.
print(f"마스크 편집 결과: {workspace_path(mask_output_path)}")
마스크 편집 결과: 05.image/output/v2/02-04_mask_high_fidelity_edit.png