test-100 / app.py
Kims12's picture
Update app.py
ceef5c6 verified
raw
history blame
26.1 kB
import os
import tempfile
from PIL import Image
import gradio as gr
import logging
import re
from io import BytesIO
from google import genai
from google.genai import types
# ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋กœ๋“œ
from dotenv import load_dotenv
load_dotenv()
# ๋กœ๊น… ์„ค์ •
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
def save_binary_file(file_name, data):
with open(file_name, "wb") as f:
f.write(data)
def translate_prompt_to_english(prompt):
"""
์ž…๋ ฅ๋œ ํ”„๋กฌํ”„ํŠธ์— ํ•œ๊ธ€์ด ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉด Geminiโ€‘2.0โ€‘flash ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์˜์–ด๋กœ ๋ฒˆ์—ญํ•ฉ๋‹ˆ๋‹ค.
ํ•œ๊ธ€์ด ์—†์œผ๋ฉด ์›๋ณธ ํ”„๋กฌํ”„ํŠธ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
"""
if not re.search("[๊ฐ€-ํžฃ]", prompt):
return prompt
try:
api_key = os.environ.get("GEMINI_API_KEY")
if not api_key:
logger.error("Gemini API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
return prompt
client = genai.Client(api_key=api_key)
translation_prompt = f"Translate the following Korean text to English:\n\n{prompt}"
logger.info(f"Translation prompt: {translation_prompt}")
response = client.models.generate_content(
model="gemini-2.0-flash",
contents=[translation_prompt],
config=types.GenerateContentConfig(
response_modalities=['Text'],
temperature=0.2,
top_p=0.95,
top_k=40,
max_output_tokens=512
)
)
translated_text = ""
for part in response.candidates[0].content.parts:
if hasattr(part, 'text') and part.text:
translated_text += part.text
if translated_text.strip():
logger.info(f"Translated text: {translated_text.strip()}")
return translated_text.strip()
else:
logger.warning("๋ฒˆ์—ญ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์›๋ณธ ํ”„๋กฌํ”„ํŠธ ์‚ฌ์šฉ")
return prompt
except Exception as e:
logger.exception("๋ฒˆ์—ญ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
return prompt
def preprocess_prompt(prompt, image1, image2, image3, bg_options=None):
"""
ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ๊ธฐ๋Šฅ ๋ช…๋ น์„ ํ•ด์„
"""
has_img1 = image1 is not None
has_img2 = image2 is not None
has_img3 = image3 is not None
if "#1" in prompt and not has_img1:
prompt = prompt.replace("#1", "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
else:
prompt = prompt.replace("#1", "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
if "#2" in prompt and not has_img2:
prompt = prompt.replace("#2", "๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
else:
prompt = prompt.replace("#2", "๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
if "#3" in prompt and not has_img3:
prompt = prompt.replace("#3", "์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
else:
prompt = prompt.replace("#3", "์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
if "1. ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ" in prompt:
desc_match = re.search(r'#1์„ "(.*?)"์œผ๋กœ ๋ฐ”๊ฟ”๋ผ', prompt)
if desc_match:
description = desc_match.group(1)
prompt = f"์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ {description}์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. ์›๋ณธ ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ๋‚ด์šฉ์€ ์œ ์ง€ํ•˜๋˜ ์ƒˆ๋กœ์šด ์Šคํƒ€์ผ๊ณผ ๋ถ„์œ„๊ธฐ๋กœ ์žฌํ•ด์„ํ•ด์ฃผ์„ธ์š”."
else:
prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ์ฐฝ์˜์ ์œผ๋กœ ๋ณ€ํ˜•ํ•ด์ฃผ์„ธ์š”. ๋” ์ƒ์ƒํ•˜๊ณ  ์˜ˆ์ˆ ์ ์ธ ๋ฒ„์ „์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
elif "2. ๊ธ€์ž์ง€์šฐ๊ธฐ" in prompt:
text_match = re.search(r'#1์—์„œ "(.*?)"๋ฅผ ์ง€์›Œ๋ผ', prompt)
if text_match:
text_to_remove = text_match.group(1)
prompt = f"์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์—์„œ '{text_to_remove}' ํ…์ŠคํŠธ๋ฅผ ์ฐพ์•„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ œ๊ฑฐํ•ด์ฃผ์„ธ์š”. ํ…์ŠคํŠธ๊ฐ€ ์žˆ๋˜ ๋ถ€๋ถ„์„ ๋ฐฐ๊ฒฝ๊ณผ ์กฐํ™”๋กญ๊ฒŒ ์ฑ„์›Œ์ฃผ์„ธ์š”."
else:
prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์—์„œ ๋ชจ๋“  ํ…์ŠคํŠธ๋ฅผ ์ฐพ์•„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ œ๊ฑฐํ•ด์ฃผ์„ธ์š”. ๊น”๋”ํ•œ ์ด๋ฏธ์ง€๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
elif "4. ์˜ท๋ฐ”๊พธ๊ธฐ" in prompt:
prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์ธ๋ฌผ ์˜์ƒ์„ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์˜์ƒ์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. ์˜์ƒ์˜ ์Šคํƒ€์ผ๊ณผ ์ƒ‰์ƒ์€ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ๋”ฐ๋ฅด๋˜, ์‹ ์ฒด ๋น„์œจ๊ณผ ํฌ์ฆˆ๋Š” ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ์œ ์ง€ํ•ด์ฃผ์„ธ์š”."
elif "5. ๋ฐฐ๊ฒฝ๋ฐ”๊พธ๊ธฐ" in prompt or "๋ฐฐ๊ฒฝ ๋ณ€๊ฒฝ" in prompt:
# ๋ฐฐ๊ฒฝ ์˜ต์…˜์ด ์žˆ์œผ๋ฉด ๊ทธ๊ฒƒ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ
if bg_options and all([bg_options.get("product_name"), bg_options.get("background_type"), bg_options.get("background_style")]):
product_name = bg_options.get("product_name", "")
product_category = bg_options.get("product_category", "์ผ๋ฐ˜ ์ƒํ’ˆ")
background_type = bg_options.get("background_type", "์ž์—ฐ ๋ฐฐ๊ฒฝ")
background_style = bg_options.get("background_style", "์ˆฒ")
lighting = bg_options.get("lighting", "์ž์—ฐ๊ด‘")
texture = bg_options.get("texture", "")
color_tone = bg_options.get("color_tone", "")
mood = bg_options.get("mood", "")
# ์ƒ์„ธ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ
prompt = f"{product_name} ์ƒํ’ˆ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ์„ {background_type} ์ค‘ {background_style}์œผ๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. "
# ์ถ”๊ฐ€ ์„ธ๋ถ€ ์‚ฌํ•ญ ํฌํ•จ
if lighting:
prompt += f"{lighting} ์กฐ๋ช…์„ ์‚ฌ์šฉํ•˜๊ณ , "
if texture:
prompt += f"{texture} ์งˆ๊ฐ์„ ํ‘œํ˜„ํ•˜๋ฉฐ, "
if color_tone:
prompt += f"{color_tone} : ์ „์ฒด ์ƒ‰๊ฐ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ , "
if mood:
prompt += f"{mood}ํ•œ ๋ถ„์œ„๊ธฐ๋กœ "
prompt += f"์—ฐ์ถœํ•ด์ฃผ์„ธ์š”. {product_category} ์ƒํ’ˆ์˜ ์ƒ‰์ƒ, ์งˆ๊ฐ, ํ˜•ํƒœ๋Š” ์™„๋ฒฝํ•˜๊ฒŒ ๋ณด์กดํ•˜๊ณ  ๊ณ ํ’ˆ์งˆ ์ƒํ’ˆ ์‚ฌ์ง„ ์Šคํƒ€์ผ๋กœ ์ œ์ž‘ํ•ด์ฃผ์„ธ์š”."
else:
prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ์„ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ํ”ผ์‚ฌ์ฒด๋Š” ์œ ์ง€ํ•˜๊ณ , ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ๊ณผ ์กฐํ™”๋กญ๊ฒŒ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”."
elif "์ƒํ’ˆํ•ฉ์„ฑ" in prompt:
prompt = "#1 ์ƒํ’ˆ ์ด๋ฏธ์ง€์— #2 ์ด๋ฏธ์ง€ ๋˜๋Š” #3 ์ด๋ฏธ์ง€๋ฅผ [๋ชจ๋“  ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ์š”์†Œ๋ฅผ ํฌํ•จํ•˜๊ณ , ํŠนํžˆ ์ƒํ’ˆ์ด ๋‹๋ณด์ด๋„๋ก ์กฐํ™”๋กญ๊ฒŒ ํ†ตํ•ฉํ•˜์—ฌ]์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ•ฉ์„ฑํ•˜๋ผ."
prompt += " ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”."
return prompt
def generate_with_images(prompt, images):
"""
API ํ˜ธ์ถœ์„ ํ†ตํ•ด ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
"""
try:
api_key = os.environ.get("GEMINI_API_KEY")
if not api_key:
return None, "API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”."
client = genai.Client(api_key=api_key)
logger.info(f"Gemini API ์š”์ฒญ ์‹œ์ž‘ - ํ”„๋กฌํ”„ํŠธ: {prompt}")
contents = [prompt]
for idx, img in enumerate(images, 1):
if img is not None:
contents.append(img)
logger.info(f"์ด๋ฏธ์ง€ #{idx} ์ถ”๊ฐ€๋จ")
response = client.models.generate_content(
model="gemini-2.0-flash-exp-image-generation",
contents=contents,
config=types.GenerateContentConfig(
response_modalities=['Text', 'Image'],
temperature=1,
top_p=0.95,
top_k=40,
max_output_tokens=8192
)
)
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
temp_path = tmp.name
result_text = ""
image_found = False
for part in response.candidates[0].content.parts:
if hasattr(part, 'text') and part.text:
result_text += part.text
logger.info(f"์‘๋‹ต ํ…์ŠคํŠธ: {part.text}")
elif hasattr(part, 'inline_data') and part.inline_data:
save_binary_file(temp_path, part.inline_data.data)
image_found = True
logger.info("์‘๋‹ต์—์„œ ์ด๋ฏธ์ง€ ์ถ”์ถœ ์„ฑ๊ณต")
if not image_found:
return None, f"API์—์„œ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์‘๋‹ต ํ…์ŠคํŠธ: {result_text}"
result_img = Image.open(temp_path)
if result_img.mode == "RGBA":
result_img = result_img.convert("RGB")
return result_img, f"์ด๋ฏธ์ง€๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. {result_text}"
except Exception as e:
logger.exception("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
def process_images_with_prompt(image1, image2, image3, prompt, bg_options=None):
"""
3๊ฐœ์˜ ์ด๋ฏธ์ง€์™€ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜์—ฌ ์ตœ์ข… ์˜์–ด ํ”„๋กฌํ”„ํŠธ(final_prompt)๋ฅผ ์ƒ์„ฑํ•œ ํ›„,
API๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
"""
try:
images = [image1, image2, image3]
valid_images = [img for img in images if img is not None]
if not valid_images:
return None, "์ ์–ด๋„ ํ•˜๋‚˜์˜ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”.", ""
if prompt and prompt.strip():
processed_prompt = preprocess_prompt(prompt, image1, image2, image3, bg_options)
if re.search("[๊ฐ€-ํžฃ]", processed_prompt):
final_prompt = translate_prompt_to_english(processed_prompt)
else:
final_prompt = processed_prompt
else:
if len(valid_images) == 1:
final_prompt = "Please creatively transform this image into a more vivid and artistic version."
logger.info("Default prompt generated for single image")
elif len(valid_images) == 2:
final_prompt = "Please seamlessly composite these two images, integrating their key elements harmoniously into a single image."
logger.info("Default prompt generated for two images")
else:
final_prompt = "Please creatively composite these three images, combining their main elements into a cohesive and natural scene."
logger.info("Default prompt generated for three images")
result_img, status = generate_with_images(final_prompt, valid_images)
return result_img, status, final_prompt
except Exception as e:
logger.exception("์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}", prompt
def process_and_show_prompt(image1, image2, image3, prompt, product_name, product_category,
background_type, background_style, lighting, texture, color_tone, mood):
try:
# ๋ฐฐ๊ฒฝ ์˜ต์…˜์„ ๋”•์…”๋„ˆ๋ฆฌ๋กœ ๊ตฌ์„ฑ
bg_options = {
"product_name": product_name,
"product_category": product_category,
"background_type": background_type,
"background_style": background_style,
"lighting": lighting,
"texture": texture,
"color_tone": color_tone,
"mood": mood
}
result_img, status, final_prompt = process_images_with_prompt(image1, image2, image3, prompt, bg_options)
# ์„ ํƒ๋œ ์˜ต์…˜์„ ๋ฌธ์ž์—ด๋กœ ๊ตฌ์„ฑ
options_summary = f"์ƒํ’ˆ๋ช…: {product_name}\n"
options_summary += f"์ œํ’ˆ๊ตฐ: {product_category}\n"
options_summary += f"๋ฐฐ๊ฒฝ ์œ ํ˜•: {background_type}\n"
options_summary += f"์„ธ๋ถ€ ์Šคํƒ€์ผ: {background_style}\n"
if lighting:
options_summary += f"์กฐ๋ช…: {lighting}\n"
if texture:
options_summary += f"๋ฐฐ๊ฒฝ ์งˆ๊ฐ: {texture}\n"
if color_tone:
options_summary += f"์ƒ‰๊ฐ: {color_tone}\n"
if mood:
options_summary += f"๋ถ„์œ„๊ธฐ: {mood}\n"
# ํ•œ๊ตญ์–ด์™€ ์˜์–ด ํ”„๋กฌํ”„ํŠธ ๋น„๊ต๋ฅผ ์œ„ํ•ด ์›๋ณธ ํ”„๋กฌํ”„ํŠธ ํ™•์ธ
kr_prompt = preprocess_prompt(prompt, image1, image2, image3, bg_options)
return result_img, status, final_prompt, options_summary, kr_prompt
except Exception as e:
logger.exception("์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}", prompt, "", ""
# ์„ ํƒ ์˜ต์…˜ ํ‘œ์‹œ/์ˆจ๊น€ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜
def toggle_background_options(selection):
if selection == "๋ฐฐ๊ฒฝ๋ฐ”๊พธ๊ธฐ":
return (gr.update(visible=True), gr.update(visible=True), gr.update(visible=True),
gr.update(visible=True), gr.update(visible=True), gr.update(visible=True),
gr.update(visible=True), gr.update(visible=True), gr.update(visible=True),
gr.update(visible=True))
else:
return (gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
gr.update(visible=False))
# ๋ฐฐ๊ฒฝ ์œ ํ˜•์— ๋”ฐ๋ฅธ ์„ธ๋ถ€ ์Šคํƒ€์ผ ์˜ต์…˜ ์—…๋ฐ์ดํŠธ
def update_background_style_options(background_type):
if background_type == "์ž์—ฐ ๋ฐฐ๊ฒฝ":
return gr.update(choices=["์ˆฒ", "ํ•ด๋ณ€", "์‚ฐ", "๋“คํŒ", "๊ณ„๊ณก", "์—ด๋Œ€ ์ •์›", "์‚ฌ๋ง‰", "๋ˆˆ ํ’๊ฒฝ"], value="์ˆฒ")
elif background_type == "์‹ค๋‚ด ๋ฐฐ๊ฒฝ":
return gr.update(choices=["๊ฑฐ์‹ค", "์ฃผ๋ฐฉ", "์„œ์žฌ", "ํ™”์žฅ๋Œ€", "ํ˜ธํ…” ๋กœ๋น„", "์นดํŽ˜", "๋ฏธ๋‹ˆ๋ฉ€ ๊ณต๊ฐ„"], value="๊ฑฐ์‹ค")
elif background_type == "ํŠน์ˆ˜ ๋ฐฐ๊ฒฝ":
return gr.update(choices=["๋„์‹œ ํ’๊ฒฝ", "๋ฃจํ”„ํƒ‘", "ํ•ด์•ˆ๊ฐ€", "๋ชจ๋˜ ์ŠคํŠœ๋””์˜ค", "๋นˆํ‹ฐ์ง€ ๊ณต๊ฐ„", "๋ฏธ๋‹ˆ์–ด์ฒ˜ ๊ณต๊ฐ„"], value="๋„์‹œ ํ’๊ฒฝ")
else:
return gr.update(choices=["์„ ํƒํ•˜์„ธ์š”"], value="์„ ํƒํ•˜์„ธ์š”")
# ์„ ํƒ ๋‚ด์šฉ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์—…๋ฐ์ดํŠธ ํ•จ์ˆ˜
def update_preview(product_name, product_category, background_type, background_style, lighting, texture, color_tone, mood):
preview = f"์ƒํ’ˆ๋ช…: {product_name}\n"
preview += f"์ œํ’ˆ๊ตฐ: {product_category}\n"
preview += f"๋ฐฐ๊ฒฝ ์œ ํ˜•: {background_type}\n"
preview += f"์„ธ๋ถ€ ์Šคํƒ€์ผ: {background_style}\n"
if lighting:
preview += f"์กฐ๋ช…: {lighting}\n"
if texture:
preview += f"๋ฐฐ๊ฒฝ ์งˆ๊ฐ: {texture}\n"
if color_tone:
preview += f"์ƒ‰๊ฐ: {color_tone}\n"
if mood:
preview += f"๋ถ„์œ„๊ธฐ: {mood}\n"
return preview
# Gradio ์ธํ„ฐํŽ˜์ด์Šค
with gr.Blocks() as demo:
gr.HTML(
"""
<div style="text-align: center; margin-bottom: 1rem;">
<h1>์ด์ปค๋จธ์Šค ์ƒํ’ˆ ์ด๋ฏธ์ง€ ๋ฐฐ๊ฒฝ ์ƒ์„ฑ๊ธฐ</h1>
<p>์ƒํ’ˆ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๊ณ  ์›ํ•˜๋Š” ๋ฐฐ๊ฒฝ ์˜ต์…˜์„ ์„ ํƒํ•˜์—ฌ ์ƒˆ๋กœ์šด ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜์„ธ์š”.</p>
</div>
"""
)
current_selection = gr.State("")
with gr.Row():
with gr.Column(scale=1):
with gr.Row():
image1_input = gr.Image(type="pil", label="#1 ์ƒํ’ˆ ์ด๋ฏธ์ง€", image_mode="RGB")
image2_input = gr.Image(type="pil", label="#2 (์„ ํƒ์‚ฌํ•ญ)", image_mode="RGB")
image3_input = gr.Image(type="pil", label="#3 (์„ ํƒ์‚ฌํ•ญ)", image_mode="RGB")
prompt_input = gr.Textbox(
lines=3,
placeholder="ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜ ๋น„์›Œ๋‘๋ฉด ์ž๋™ ํ•ฉ์„ฑ๋ฉ๋‹ˆ๋‹ค.",
label="ํ”„๋กฌํ”„ํŠธ (์„ ํƒ ์‚ฌํ•ญ)"
)
with gr.Row():
image_change_btn = gr.Button("์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ")
text_remove_btn = gr.Button("๊ธ€์ž์ง€์šฐ๊ธฐ")
clothes_change_btn = gr.Button("์˜ท๋ฐ”๊พธ๊ธฐ")
background_change_btn = gr.Button("๋ฐฐ๊ฒฝ๋ฐ”๊พธ๊ธฐ", variant="primary")
composite_product_btn = gr.Button("์ƒํ’ˆํ•ฉ์„ฑ")
# ๋ฐฐ๊ฒฝ ๋ณ€๊ฒฝ ๊ด€๋ จ ์„น์…˜ (๊ธฐ๋ณธ์ ์œผ๋กœ ์ˆจ๊น€ ์ƒํƒœ)
with gr.Row(visible=False) as background_header:
gr.HTML("<h3 style='margin-top: 20px; margin-bottom: 10px; width: 100%; text-align: center;'>๋ฐฐ๊ฒฝ ๋ณ€๊ฒฝ ์˜ต์…˜</h3>")
# ์ƒํ’ˆ๋ช… ์ž…๋ ฅ
product_name = gr.Textbox(
label="์ƒํ’ˆ๋ช… ์ž…๋ ฅ",
placeholder="์ƒํ’ˆ๋ช…์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”",
visible=False
)
# ์ œํ’ˆ๊ตฐ ์นดํ…Œ๊ณ ๋ฆฌ ๋“œ๋กญ๋‹ค์šด
product_category = gr.Dropdown(
choices=["ํ™”์žฅํ’ˆ", "๊ฐ€๋ฐฉ", "์•ก์„ธ์„œ๋ฆฌ", "๋ณด์„๋ฅ˜", "ํ™ˆ ๋ฐ์ฝ”", "์‹ ๋ฐœ", "์˜๋ฅ˜", "์ฃผ๋ฐฉ์šฉํ’ˆ", "์ „์ž๊ธฐ๊ธฐ", "๊ธฐํƒ€"],
label="์ œํ’ˆ๊ตฐ ์„ ํƒ",
value="๊ธฐํƒ€",
visible=False
)
# ๋ฐฐ๊ฒฝ ์œ ํ˜• ๋“œ๋กญ๋‹ค์šด
background_type = gr.Dropdown(
choices=["์ž์—ฐ ๋ฐฐ๊ฒฝ", "์‹ค๋‚ด ๋ฐฐ๊ฒฝ", "ํŠน์ˆ˜ ๋ฐฐ๊ฒฝ"],
label="๋ฐฐ๊ฒฝ ์œ ํ˜• ์„ ํƒ",
value="์ž์—ฐ ๋ฐฐ๊ฒฝ",
visible=False
)
# ๋ฐฐ๊ฒฝ ์„ธ๋ถ€ ์Šคํƒ€์ผ ๋“œ๋กญ๋‹ค์šด (๋ฐฐ๊ฒฝ ์œ ํ˜•์— ๋”ฐ๋ผ ๋™์  ๋ณ€๊ฒฝ)
background_style = gr.Dropdown(
choices=["์ˆฒ", "ํ•ด๋ณ€", "์‚ฐ", "๋“คํŒ", "๊ณ„๊ณก", "์—ด๋Œ€ ์ •์›", "์‚ฌ๋ง‰", "๋ˆˆ ํ’๊ฒฝ"],
label="๋ฐฐ๊ฒฝ ์„ธ๋ถ€ ์Šคํƒ€์ผ ์„ ํƒ",
value="์ˆฒ",
visible=False
)
# ์ถ”๊ฐ€ ์„ ํƒ ์˜ต์…˜ ํ—ค๋”
with gr.Row(visible=False) as additional_header:
gr.HTML("<h4 style='margin-top: 15px; margin-bottom: 10px; width: 100%; text-align: center;'>์ถ”๊ฐ€ ์„ ํƒ ์˜ต์…˜</h4>")
# ์กฐ๋ช… ๋“œ๋กญ๋‹ค์šด
lighting = gr.Dropdown(
choices=["์ž์—ฐ๊ด‘", "๋”ฐ๋œปํ•œ ์กฐ๋ช…", "๊ทธ๋ฆผ์ž ์žˆ๋Š” ์กฐ๋ช…", "๋ฏธ๋‹ˆ๋ฉ€ ์กฐ๋ช…"],
label="์กฐ๋ช… ์„ ํƒ",
value="์ž์—ฐ๊ด‘",
visible=False
)
# ๋ฐฐ๊ฒฝ ์งˆ๊ฐ ๋“œ๋กญ๋‹ค์šด
texture = gr.Dropdown(
choices=["ํฐ ๋ฒฝ", "๋Œ€๋ฆฌ์„", "๋‚˜๋ฌด", "๋ฒฝ๋Œ", "ํฐ ์ฒœ", "์‹œ๋ฉ˜ํŠธ"],
label="๋ฐฐ๊ฒฝ ์งˆ๊ฐ ์„ ํƒ",
value="",
visible=False
)
# ์ƒ‰๊ฐ ๋“œ๋กญ๋‹ค์šด
color_tone = gr.Dropdown(
choices=["ํ™”์ดํŠธ ํ†ค", "๋ธ”๋ฃจ ํ†ค", "๋”ฐ๋œปํ•œ ์ƒ‰์ƒ", "๋ชจ๋…ธํ†ค", "ํŒŒ์Šคํ…”"],
label="์ƒ‰๊ฐ ์„ ํƒ",
value="",
visible=False
)
# ๋ถ„์œ„๊ธฐ ๋“œ๋กญ๋‹ค์šด
mood = gr.Dropdown(
choices=["ํฌ๊ทผํ•œ", "์‹ฌํ”Œํ•œ", "๋ชจ๋˜ํ•œ", "๋‚ด์ถ”๋Ÿด", "๋ฏธ๋‹ˆ๋ฉ€"],
label="๋ถ„์œ„๊ธฐ ์„ ํƒ",
value="",
visible=False
)
# ์„ ํƒ ๋‚ด์šฉ ๋ฏธ๋ฆฌ๋ณด๊ธฐ
selection_preview = gr.Textbox(
label="์„ ํƒ ๋‚ด์šฉ ๋ฏธ๋ฆฌ๋ณด๊ธฐ",
lines=8,
readonly=True,
visible=False
)
submit_btn = gr.Button("์ด๋ฏธ์ง€ ์ƒ์„ฑ", variant="primary")
with gr.Column(scale=1):
output_image = gr.Image(label="์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€")
output_text = gr.Textbox(label="์ƒํƒœ ๋ฉ”์‹œ์ง€")
prompt_display = gr.Textbox(label="์‚ฌ์šฉ๋œ ํ”„๋กฌํ”„ํŠธ (์˜์–ด)", visible=True)
kr_prompt_display = gr.Textbox(label="์‚ฌ์šฉ๋œ ํ”„๋กฌํ”„ํŠธ (ํ•œ๊ตญ์–ด)", visible=True)
options_summary = gr.Textbox(label="์„ ํƒ๋œ ์˜ต์…˜ ์š”์•ฝ", lines=8, visible=True)
# ์„ ํƒ ์˜ต์…˜ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ๋ž€ ์—…๋ฐ์ดํŠธ (ํ•œ๊ตญ์–ด ๋ฌธ๊ตฌ)
image_change_btn.click(
fn=lambda: "#1 ์ด๋ฏธ์ง€์˜ [๋‹ค๋ฅธ ๋ชจ์Šต]์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋ผ.",
inputs=[],
outputs=prompt_input
).then(
fn=lambda: "์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ",
inputs=[],
outputs=current_selection
).then(
fn=toggle_background_options,
inputs=[current_selection],
outputs=[background_header, product_name, product_category, background_type,
background_style, additional_header, lighting, texture, color_tone,
mood, selection_preview]
)
text_remove_btn.click(
fn=lambda: "#1 ์ด๋ฏธ์ง€์˜ [์ค‘๊ตญ์–ด๋ฅผ ๋ชจ๋‘]๋ฅผ ์ œ๊ฑฐํ•˜๋ผ.",
inputs=[],
outputs=prompt_input
).then(
fn=lambda: "๊ธ€์ž์ง€์šฐ๊ธฐ",
inputs=[],
outputs=current_selection
).then(
fn=toggle_background_options,
inputs=[current_selection],
outputs=[background_header, product_name, product_category, background_type,
background_style, additional_header, lighting, texture, color_tone,
mood, selection_preview]
)
clothes_change_btn.click(
fn=lambda: "#1์ด๋ฏธ์ง€์—์„œ [์‹ ์ฒด ๋น„์œจ๊ณผ ํฌ์ฆˆ๋Š” ์œ ์ง€ํ•œ ์ฒด] ์˜์ƒ[์Šคํƒ€์ผ๊ณผ ์ƒ‰์ƒ์„]#2, #3์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”.",
inputs=[],
outputs=prompt_input
).then(
fn=lambda: "์˜ท๋ฐ”๊พธ๊ธฐ",
inputs=[],
outputs=current_selection
).then(
fn=toggle_background_options,
inputs=[current_selection],
outputs=[background_header, product_name, product_category, background_type,
background_style, additional_header, lighting, texture, color_tone,
mood, selection_preview]
)
background_change_btn.click(
fn=lambda: "๋ฐฐ๊ฒฝ ๋ณ€๊ฒฝ",
inputs=[],
outputs=prompt_input
).then(
fn=lambda: "๋ฐฐ๊ฒฝ๋ฐ”๊พธ๊ธฐ",
inputs=[],
outputs=current_selection
).then(
fn=toggle_background_options,
inputs=[current_selection],
outputs=[background_header, product_name, product_category, background_type,
background_style, additional_header, lighting, texture, color_tone,
mood, selection_preview]
)
composite_product_btn.click(
fn=lambda: "์ด๋ฏธ์ง€ #1์˜ ๋ชจ๋ธ์ด ํ•„์š”์— ๋”ฐ๋ผ ์ž์„ธ๋‚˜ ํ‘œ์ •์„ ์œ ๋™์ ์œผ๋กœ ๋ณ€ํ˜•ํ•œ ํ›„, ์ด๋ฏธ์ง€ #2(๋˜๋Š” #3)์˜ ์ƒํ’ˆ์„ ์ตœ๋Œ€ํ•œ ์›๋ž˜ ํ˜•ํƒœ์™€ ๋””ํ…Œ์ผ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ฐฉ์šฉํ•œ ๋ชจ์Šต์„ ํ•ฉ์„ฑํ•˜๋ผ.",
inputs=[],
outputs=prompt_input
).then(
fn=lambda: "์ƒํ’ˆํ•ฉ์„ฑ",
inputs=[],
outputs=current_selection
).then(
fn=toggle_background_options,
inputs=[current_selection],
outputs=[background_header, product_name, product_category, background_type,
background_style, additional_header, lighting, texture, color_tone,
mood, selection_preview]
)
# ๋ฐฐ๊ฒฝ ์œ ํ˜•์— ๋”ฐ๋ผ ์„ธ๋ถ€ ์Šคํƒ€์ผ ์˜ต์…˜ ์—…๋ฐ์ดํŠธ
background_type.change(
fn=update_background_style_options,
inputs=[background_type],
outputs=[background_style]
)
# ์„ ํƒ ๋‚ด์šฉ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์—…๋ฐ์ดํŠธ (๋ชจ๋“  ์„ ํƒ ์ปดํฌ๋„ŒํŠธ์˜ ๋ณ€๊ฒฝ ์ด๋ฒคํŠธ์— ์—ฐ๊ฒฐ)
product_name.change(
fn=update_preview,
inputs=[product_name, product_category, background_type, background_style, lighting, texture, color_tone, mood],
outputs=[selection_preview]
)
product_category.change(
fn=update_preview,
inputs=[product_name, product_category, background_type, background_style, lighting, texture, color_tone, mood],
outputs=[selection_preview]
)
background_type.change(
fn=update_preview,
inputs=[product_name, product_category, background_type, background_style, lighting, texture, color_tone, mood],
outputs=[selection_preview]
)
background_style.change(
fn=update_preview,
inputs=[product_name, product_category, background_type, background_style, lighting, texture, color_tone, mood],
outputs=[selection_preview]
)
lighting.change(
fn=update_preview,
inputs=[product_name, product_category, background_type, background_style, lighting, texture, color_tone, mood],
outputs=[selection_preview]
)
texture.change(
fn=update_preview,
inputs=[product_name, product_category, background_type, background_style, lighting, texture, color_tone, mood],
outputs=[selection_preview]
)
color_tone.change(
fn=update_preview,
inputs=[product_name, product_category, background_type, background_style, lighting, texture, color_tone, mood],
outputs=[selection_preview]
)
mood.change(
fn=update_preview,
inputs=[product_name, product_category, background_type, background_style, lighting, texture, color_tone, mood],
outputs=[selection_preview]
)
# ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
submit_btn.click(
fn=process_and_show_prompt,
inputs=[image1_input, image2_input, image3_input, prompt_input,
product_name, product_category, background_type, background_style,
lighting, texture, color_tone, mood],
outputs=[output_image, output_text, prompt_display, options_summary, kr_prompt_display],
)
gr.Markdown(
"""
### ์‚ฌ์šฉ ๋ฐฉ๋ฒ•:
1. **์ž๋™ ํ•ฉ์„ฑ**: ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๊ณ  ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋น„์›Œ๋‘๋ฉด ์ž๋™์œผ๋กœ ํ•ฉ์„ฑ๋ฉ๋‹ˆ๋‹ค.
2. **์ด๋ฏธ์ง€ ์ฐธ์กฐ**: #1, #2, #3์œผ๋กœ ๊ฐ ์ด๋ฏธ์ง€๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
3. **์„ ํƒ ์˜ต์…˜**: ์œ„์˜ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ๋ž€์— ํ•œ๊ตญ์–ด ๋ฌธ๊ตฌ๋กœ ์ž…๋ ฅํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.
4. **๋ฐฐ๊ฒฝ ๋ณ€๊ฒฝ**: ๋ฐฐ๊ฒฝ๋ฐ”๊พธ๊ธฐ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ์ถ”๊ฐ€ ์˜ต์…˜์ด ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค.
> **ํŒ**: ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
"""
)
if __name__ == "__main__":
demo.launch(share=True)