test-100 / app.py
Kims12's picture
Update app.py
95df87a verified
raw
history blame
10.1 kB
import json
import os
import time
import uuid
import tempfile
from PIL import Image
import gradio as gr
import base64
import mimetypes
import logging
# ๊ธฐ์กด์˜ "from google import genai" ๋Œ€์‹  ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •
import google.generativeai as genai
from google.generativeai import types
# .env ํŒŒ์ผ์— ์ €์žฅ๋œ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋กœ๋“œ (python-dotenv ์„ค์น˜ ํ•„์š”: pip install python-dotenv)
from dotenv import load_dotenv
load_dotenv()
# ๋กœ๊น… ์„ค์ • (๋กœ๊ทธ ๋ ˆ๋ฒจ: DEBUG)
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
def save_binary_file(file_name, data):
logger.debug(f"ํŒŒ์ผ์— ์ด์ง„ ๋ฐ์ดํ„ฐ ์ €์žฅ ์ค‘: {file_name}")
with open(file_name, "wb") as f:
f.write(data)
logger.debug(f"ํŒŒ์ผ ์ €์žฅ ์™„๋ฃŒ: {file_name}")
def generate(text, file_name, background_file=None, style_file=None, model="gemini-2.0-flash-exp-image-generation"):
logger.debug(f"generate ํ•จ์ˆ˜ ์‹œ์ž‘ - ํ…์ŠคํŠธ: '{text}', ํŒŒ์ผ๋ช…: '{file_name}', ๋ชจ๋ธ: '{model}'")
try:
# API ํ‚ค๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ ๋ถˆ๋Ÿฌ์˜ด
effective_api_key = os.environ.get("GEMINI_API_KEY")
if effective_api_key:
logger.debug("ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ API ํ‚ค ๋ถˆ๋Ÿฌ์˜ด")
else:
logger.error("API ํ‚ค๊ฐ€ ํ™˜๊ฒฝ๋ณ€์ˆ˜์— ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
raise ValueError("API ํ‚ค๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.")
client = genai.Client(api_key=effective_api_key)
logger.debug("Gemini ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” ์™„๋ฃŒ.")
# ์›๋ณธ, ๋ฐฐ๊ฒฝ, ์Šคํƒ€์ผ ์ด๋ฏธ์ง€๋ฅผ ๊ฐ๊ฐ ์—…๋กœ๋“œ (์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ)
uploaded_files = []
uploaded_files.append(client.files.upload(file=file_name))
logger.debug(f"์›๋ณธ ํŒŒ์ผ ์—…๋กœ๋“œ ์™„๋ฃŒ. URI: {uploaded_files[0].uri}, MIME ํƒ€์ž…: {uploaded_files[0].mime_type}")
if background_file is not None:
bg_file = client.files.upload(file=background_file)
uploaded_files.append(bg_file)
logger.debug(f"๋ฐฐ๊ฒฝ ํŒŒ์ผ ์—…๋กœ๋“œ ์™„๋ฃŒ. URI: {bg_file.uri}, MIME ํƒ€์ž…: {bg_file.mime_type}")
if style_file is not None:
style_uploaded = client.files.upload(file=style_file)
uploaded_files.append(style_uploaded)
logger.debug(f"์Šคํƒ€์ผ ํŒŒ์ผ ์—…๋กœ๋“œ ์™„๋ฃŒ. URI: {style_uploaded.uri}, MIME ํƒ€์ž…: {style_uploaded.mime_type}")
# ์ปจํ…์ธ  ๊ฐ์ฒด ์ƒ์„ฑ: ์—…๋กœ๋“œํ•œ ์ด๋ฏธ์ง€๋“ค์„ ์ˆœ์„œ๋Œ€๋กœ(์›๋ณธ, ๋ฐฐ๊ฒฝ, ์Šคํƒ€์ผ) ์ถ”๊ฐ€ํ•˜๊ณ  ๋งˆ์ง€๋ง‰์— ํ…์ŠคํŠธ ํ”„๋กฌํ”„ํŠธ ์ถ”๊ฐ€
parts = []
# ์›๋ณธ ์ด๋ฏธ์ง€ ํŒŒํŠธ
parts.append(
types.Part.from_uri(
file_uri=uploaded_files[0].uri,
mime_type=uploaded_files[0].mime_type,
)
)
# ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ํŒŒํŠธ (์กด์žฌ ์‹œ)
if background_file is not None:
parts.append(
types.Part.from_uri(
file_uri=uploaded_files[1].uri,
mime_type=uploaded_files[1].mime_type,
)
)
# ์Šคํƒ€์ผ ์ด๋ฏธ์ง€ ํŒŒํŠธ (์กด์žฌ ์‹œ)
if style_file is not None:
style_index = 2 if background_file is not None else 1
parts.append(
types.Part.from_uri(
file_uri=uploaded_files[style_index].uri,
mime_type=uploaded_files[style_index].mime_type,
)
)
# ๋งˆ์ง€๋ง‰์œผ๋กœ ํ…์ŠคํŠธ ํŒŒํŠธ ์ถ”๊ฐ€
parts.append(types.Part.from_text(text=text))
contents = [
types.Content(
role="user",
parts=parts,
),
]
logger.debug(f"์ปจํ…์ธ  ๊ฐ์ฒด ์ƒ์„ฑ ์™„๋ฃŒ: {contents}")
generate_content_config = types.GenerateContentConfig(
temperature=1,
top_p=0.95,
top_k=40,
max_output_tokens=8192,
response_modalities=[
"image",
"text",
],
response_mime_type="text/plain",
)
logger.debug(f"์ƒ์„ฑ ์„ค์ •: {generate_content_config}")
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
temp_path = tmp.name
logger.debug(f"์ž„์‹œ ํŒŒ์ผ ์ƒ์„ฑ๋จ: {temp_path}")
response_stream = client.models.generate_content_stream(
model=model,
contents=contents,
config=generate_content_config,
)
logger.debug("์‘๋‹ต ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ ์‹œ์ž‘...")
for chunk in response_stream:
if not chunk.candidates or not chunk.candidates[0].content or not chunk.candidates[0].content.parts:
logger.warning("chunk์— ํ›„๋ณด, ์ปจํ…์ธ , ๋˜๋Š” ํŒŒํŠธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค.")
continue
inline_data = chunk.candidates[0].content.parts[0].inline_data
if inline_data:
save_binary_file(temp_path, inline_data.data)
logger.info(f"MIME ํƒ€์ž… {inline_data.mime_type}์˜ ํŒŒ์ผ์ด ์ €์žฅ๋จ: {temp_path} (ํ”„๋กฌํ”„ํŠธ: {text})")
else:
logger.info(f"์ˆ˜์‹ ๋œ ํ…์ŠคํŠธ: {chunk.text}")
print(chunk.text)
logger.debug(f"Raw chunk: {chunk}")
del uploaded_files
logger.debug("์—…๋กœ๋“œ๋œ ํŒŒ์ผ ์ •๋ณด ์‚ญ์ œ ์™„๋ฃŒ.")
return temp_path
except Exception as e:
logger.exception("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
return None
def process_image_and_prompt(original_pil, prompt, background_pil=None, style_pil=None):
logger.debug(f"process_image_and_prompt ํ•จ์ˆ˜ ์‹œ์ž‘ - ํ”„๋กฌํ”„ํŠธ: '{prompt}'")
try:
# ์›๋ณธ ์ด๋ฏธ์ง€ ์ €์žฅ
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
original_path = tmp.name
original_pil.save(original_path)
logger.debug(f"์›๋ณธ ์ด๋ฏธ์ง€ ์ €์žฅ ์™„๋ฃŒ: {original_path}")
# ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ์ €์žฅ (์ œ๊ณต๋œ ๊ฒฝ์šฐ)
background_path = None
if background_pil is not None:
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_bg:
background_path = tmp_bg.name
background_pil.save(background_path)
logger.debug(f"๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ์ €์žฅ ์™„๋ฃŒ: {background_path}")
# ์Šคํƒ€์ผ ์ด๋ฏธ์ง€ ์ €์žฅ (์ œ๊ณต๋œ ๊ฒฝ์šฐ)
style_path = None
if style_pil is not None:
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_style:
style_path = tmp_style.name
style_pil.save(style_path)
logger.debug(f"์Šคํƒ€์ผ ์ด๋ฏธ์ง€ ์ €์žฅ ์™„๋ฃŒ: {style_path}")
input_text = prompt
model = "gemini-2.0-flash-exp-image-generation"
gemma_edited_image_path = generate(text=input_text,
file_name=original_path,
background_file=background_path,
style_file=style_path,
model=model)
if gemma_edited_image_path:
logger.debug(f"์ด๋ฏธ์ง€ ์ƒ์„ฑ ์™„๋ฃŒ. ๊ฒฝ๋กœ: {gemma_edited_image_path}")
result_img = Image.open(gemma_edited_image_path)
if result_img.mode == "RGBA":
result_img = result_img.convert("RGB")
return [result_img]
else:
logger.error("generate ํ•จ์ˆ˜์—์„œ None ๋ฐ˜ํ™˜๋จ.")
return []
except Exception as e:
logger.exception("process_image_and_prompt ํ•จ์ˆ˜์—์„œ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
return []
# --- Gradio ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌ์„ฑ ---
with gr.Blocks() as demo:
gr.HTML(
"""
<div style='display: flex; align-items: center; justify-content: center; gap: 20px'>
<div style="background-color: var(--block-background-fill); border-radius: 8px">
<img src="https://www.gstatic.com/lamda/images/gemini_favicon_f069958c85030456e93de685481c559f160ea06b.png" style="width: 100px; height: 100px;">
</div>
<div>
<h1>Gemini๋ฅผ ์ด์šฉํ•œ ์ด๋ฏธ์ง€ ํŽธ์ง‘</h1>
<p>Gemini API ํ‚ค๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜(GEMINI_API_KEY)๋กœ ์„ค์ •๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.</p>
</div>
</div>
"""
)
gr.Markdown("์›๋ณธ, ๋ฐฐ๊ฒฝ, ์Šคํƒ€์ผ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๊ณ , ํŽธ์ง‘ํ•  ๋‚ด์šฉ์„ ์ž…๋ ฅํ•˜์„ธ์š”.")
with gr.Row():
with gr.Column():
original_input = gr.Image(type="pil", label="์›๋ณธ ์ด๋ฏธ์ง€", image_mode="RGBA")
background_input = gr.Image(type="pil", label="๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€", image_mode="RGBA")
style_input = gr.Image(type="pil", label="์Šคํƒ€์ผ ์ด๋ฏธ์ง€", image_mode="RGBA")
prompt_input = gr.Textbox(
lines=2,
placeholder="ํŽธ์ง‘ํ•  ๋‚ด์šฉ์„ ์ž…๋ ฅํ•˜์„ธ์š”...",
label="ํŽธ์ง‘ ํ”„๋กฌํ”„ํŠธ"
)
submit_btn = gr.Button("์ด๋ฏธ์ง€ ํŽธ์ง‘ ์‹คํ–‰")
with gr.Column():
output_gallery = gr.Gallery(label="ํŽธ์ง‘ ๊ฒฐ๊ณผ")
submit_btn.click(
fn=process_image_and_prompt,
inputs=[original_input, prompt_input, background_input, style_input],
outputs=output_gallery,
)
# --- ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ---
dummy_original = Image.new("RGBA", (100, 100), color="red")
dummy_background = Image.new("RGBA", (100, 100), color="green")
dummy_style = Image.new("RGBA", (100, 100), color="blue")
dummy_prompt = "์ด๋ฏธ์ง€ ํŽธ์ง‘: ์›๋ณธ์€ ๋นจ๊ฐ•, ๋ฐฐ๊ฒฝ์€ ์ดˆ๋ก, ์Šคํƒ€์ผ์€ ํŒŒ๋ž‘"
logger.info("process_image_and_prompt ํ•จ์ˆ˜๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค...")
result = process_image_and_prompt(dummy_original, dummy_prompt, dummy_background, dummy_style)
if result:
logger.info(f"์ง์ ‘ ํ˜ธ์ถœ ์„ฑ๊ณต. ๊ฒฐ๊ณผ: {result}")
else:
logger.error("์ง์ ‘ ํ˜ธ์ถœ ์‹คํŒจ.")
demo.launch(share=True)