ttopse / app.py
whispersound's picture
Update app.py
32f62d1 verified
import os
import logging
import re
import json
import gradio as gr
from google import genai
from google.genai import types
import google.generativeai as genai_generative
from dotenv import load_dotenv
load_dotenv()
# ------------------- ๋กœ๊น… ์„ค์ • -------------------
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# ------------------- ๋ฐฐ๊ฒฝ ๋””๋ ‰ํ† ๋ฆฌ ์„ค์ • -------------------
BACKGROUNDS_DIR = "./background"
if not os.path.exists(BACKGROUNDS_DIR):
os.makedirs(BACKGROUNDS_DIR)
logger.info(f"๋ฐฐ๊ฒฝ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์ƒ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค: {BACKGROUNDS_DIR}")
# ------------------- ์ „์—ญ ๋ณ€์ˆ˜ ์„ค์ • -------------------
SIMPLE_BACKGROUNDS = {}
STUDIO_BACKGROUNDS = {}
NATURE_BACKGROUNDS = {}
INDOOR_BACKGROUNDS = {}
TECHNOLOGY_BACKGROUNDS = {}
COLORFUL_PATTERN_BACKGROUNDS = {}
ABSTRACT_BACKGROUNDS = {}
JEWELRY_BACKGROUNDS = {} # ์ฅฌ์–ผ๋ฆฌ ๋ฐฐ๊ฒฝ ์ „์—ญ ๋ณ€์ˆ˜ ์ถ”๊ฐ€
# ------------------- ๋ฐฐ๊ฒฝ JSON ํŒŒ์ผ ๋กœ๋“œ ํ•จ์ˆ˜ -------------------
def load_background_json(filename):
"""๋ฐฐ๊ฒฝ JSON ํŒŒ์ผ ๋กœ๋“œ ํ•จ์ˆ˜"""
file_path = os.path.join(BACKGROUNDS_DIR, filename)
try:
with open(file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
logger.info(f"{filename} ํŒŒ์ผ์„ ์„ฑ๊ณต์ ์œผ๋กœ ๋กœ๋“œํ–ˆ์Šต๋‹ˆ๋‹ค. {len(data)} ํ•ญ๋ชฉ ํฌํ•จ.")
return data
except FileNotFoundError:
logger.info(f"{filename} ํŒŒ์ผ์ด ์—†์Šต๋‹ˆ๋‹ค.")
return {}
except Exception as e:
logger.warning(f"{filename} ํŒŒ์ผ ๋กœ๋“œ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}.")
return {}
# ------------------- ๋ฐฐ๊ฒฝ ์˜ต์…˜ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜ -------------------
def initialize_backgrounds():
"""๋ชจ๋“  ๋ฐฐ๊ฒฝ ์˜ต์…˜ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜"""
global SIMPLE_BACKGROUNDS, STUDIO_BACKGROUNDS, NATURE_BACKGROUNDS, INDOOR_BACKGROUNDS
global TECHNOLOGY_BACKGROUNDS, COLORFUL_PATTERN_BACKGROUNDS, ABSTRACT_BACKGROUNDS
global JEWELRY_BACKGROUNDS # ์ฅฌ์–ผ๋ฆฌ ๋ฐฐ๊ฒฝ ์ถ”๊ฐ€
# ๋””๋ ‰ํ† ๋ฆฌ ๋‚ด ๋ชจ๋“  ํŒŒ์ผ ๋กœ๊น…
logger.info(f"Backgrounds ๋””๋ ‰ํ† ๋ฆฌ ๊ฒฝ๋กœ: {BACKGROUNDS_DIR}")
logger.info(f"๋””๋ ‰ํ† ๋ฆฌ ๋‚ด ํŒŒ์ผ ๋ชฉ๋ก: {os.listdir(BACKGROUNDS_DIR)}")
# ๊ฐ ๋ฐฐ๊ฒฝ ํŒŒ์ผ ๋กœ๋“œ
SIMPLE_BACKGROUNDS = load_background_json("simple_backgrounds.json")
STUDIO_BACKGROUNDS = load_background_json("studio_backgrounds.json")
NATURE_BACKGROUNDS = load_background_json("nature_backgrounds.json")
INDOOR_BACKGROUNDS = load_background_json("indoor_backgrounds.json")
TECHNOLOGY_BACKGROUNDS = load_background_json("tech-backgrounds-final.json") # ํŒŒ์ผ ์ด๋ฆ„ ์ˆ˜์ •
COLORFUL_PATTERN_BACKGROUNDS = load_background_json("colorful-pattern-backgrounds.json") # ํŒŒ์ผ ์ด๋ฆ„ ์ˆ˜์ •
ABSTRACT_BACKGROUNDS = load_background_json("abstract_backgrounds.json")
JEWELRY_BACKGROUNDS = load_background_json("jewelry_backgrounds.json")
# ๊ธฐ๋ณธ๊ฐ’ ์„ค์ • (ํŒŒ์ผ์ด ์—†๊ฑฐ๋‚˜ ๋น„์–ด์žˆ๋Š” ๊ฒฝ์šฐ)
if not SIMPLE_BACKGROUNDS:
SIMPLE_BACKGROUNDS = {"ํด๋ž˜์‹ ํ™”์ดํŠธ": "clean white background with soft even lighting"}
if not STUDIO_BACKGROUNDS:
STUDIO_BACKGROUNDS = {"๋ฏธ๋‹ˆ๋ฉ€ ํ”Œ๋žซ๋ ˆ์ด": "minimalist flat lay with clean white background"}
if not NATURE_BACKGROUNDS:
NATURE_BACKGROUNDS = {"์—ด๋Œ€ ํ•ด๋ณ€": "tropical beach with crystal clear water"}
if not INDOOR_BACKGROUNDS:
INDOOR_BACKGROUNDS = {"๋ฏธ๋‹ˆ๋ฉ€ ์Šค์นธ๋””๋‚˜๋น„์•ˆ ๊ฑฐ์‹ค": "minimalist Scandinavian living room"}
if not TECHNOLOGY_BACKGROUNDS:
TECHNOLOGY_BACKGROUNDS = {"๋‹ค์ด๋‚˜๋ฏน ์Šคํ”Œ๋ž˜์‹œ": "dynamic water splash interaction with product"}
if not COLORFUL_PATTERN_BACKGROUNDS:
COLORFUL_PATTERN_BACKGROUNDS = {"ํ™”๋ คํ•œ ๊ฝƒ ํŒจํ„ด": "vibrant floral pattern backdrop"}
if not ABSTRACT_BACKGROUNDS:
ABSTRACT_BACKGROUNDS = {"๋„ค์˜จ ๋ผ์ดํŠธ": "neon light abstract background with vibrant glowing elements"}
if not JEWELRY_BACKGROUNDS:
JEWELRY_BACKGROUNDS = {"ํด๋ž˜์‹ ํ™”์ดํŠธ ์‹คํฌ": "pristine white silk fabric backdrop"}
logger.info("๋ชจ๋“  ๋ฐฐ๊ฒฝ ์˜ต์…˜ ์ดˆ๊ธฐํ™” ์™„๋ฃŒ")
# ๋ฐฐ๊ฒฝ ๋“œ๋กญ๋‹ค์šด ์ดˆ๊ธฐํ™”๋ฅผ ์œ„ํ•œ ํ•จ์ˆ˜ ์ถ”๊ฐ€
def initialize_dropdowns():
"""๋“œ๋กญ๋‹ค์šด ๋ฉ”๋‰ด ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜"""
# ๊ฐ ๋ฐฐ๊ฒฝ ์œ ํ˜•๋ณ„ ๋“œ๋กญ๋‹ค์šด ์„ ํƒ ๋ชฉ๋ก ์ƒ์„ฑ
simple_choices = list(SIMPLE_BACKGROUNDS.keys())
studio_choices = list(STUDIO_BACKGROUNDS.keys())
nature_choices = list(NATURE_BACKGROUNDS.keys())
indoor_choices = list(INDOOR_BACKGROUNDS.keys())
tech_choices = list(TECHNOLOGY_BACKGROUNDS.keys())
colorful_choices = list(COLORFUL_PATTERN_BACKGROUNDS.keys())
abstract_choices = list(ABSTRACT_BACKGROUNDS.keys())
jewelry_choices = list(JEWELRY_BACKGROUNDS.keys())
return {
"simple": simple_choices,
"studio": studio_choices,
"nature": nature_choices,
"indoor": indoor_choices,
"tech": tech_choices,
"colorful": colorful_choices,
"abstract": abstract_choices,
"jewelry": jewelry_choices, # ์ƒˆ๋กœ ์ถ”๊ฐ€
}
# ------------------- ํ”„๋กฌํ”„ํŠธ ๊ด€๋ จ ํ•จ์ˆ˜ -------------------
def filter_prompt_only(prompt):
"""Gemini์˜ ์„ค๋ช… ๋ฐ ๋ถˆํ•„์š”ํ•œ ๋ฉ”์‹œ์ง€๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ์‹ค์ œ ํ”„๋กฌํ”„ํŠธ๋งŒ ์ถ”์ถœํ•˜๋Š” ํ•จ์ˆ˜"""
# ์ฝ”๋“œ ๋ธ”๋ก ๋‚ด๋ถ€์˜ ํ”„๋กฌํ”„ํŠธ ์ฐพ๊ธฐ
code_block_pattern = r"```\s*(.*?)```"
code_match = re.search(code_block_pattern, prompt, re.DOTALL)
if code_match:
return code_match.group(1).strip()
# Midjourney ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํฌํ•จํ•˜๋Š” ํ”„๋กฌํ”„ํŠธ ๋ถ€๋ถ„ ์ฐพ๊ธฐ
if "--ar 1:1" in prompt:
lines = prompt.split('\n')
prompt_lines = []
in_prompt = False
for line in lines:
# ํ”„๋กฌํ”„ํŠธ ์‹œ์ž‘ ๋ถ€๋ถ„ ์ธ์‹ (์ผ๋ฐ˜์ ์ธ ์ œํ’ˆ ์„ค๋ช…์ด๋‚˜ 'Magazine-worthy' ๊ฐ™์€ ํ‚ค์›Œ๋“œ๋กœ ์‹œ์ž‘)
if (not in_prompt and
("product" in line.lower() or
"magazine" in line.lower() or
"commercial" in line.lower() or
"photography" in line.lower())):
in_prompt = True
prompt_lines.append(line)
# ์ด๋ฏธ ํ”„๋กฌํ”„ํŠธ ์˜์—ญ์— ์žˆ๋Š” ๊ฒฝ์šฐ ๊ณ„์† ์ถ”๊ฐ€
elif in_prompt:
# ์„ค๋ช…์ด๋‚˜ ๋ฉ”ํƒ€ ํ…์ŠคํŠธ๊ฐ€ ์‹œ์ž‘๋˜๋ฉด ์ค‘๋‹จ
if "explanation" in line.lower() or "let me know" in line.lower():
break
prompt_lines.append(line)
# ํ”„๋กฌํ”„ํŠธ ๋ผ์ธ ํ•ฉ์น˜๊ธฐ
if prompt_lines:
return '\n'.join(prompt_lines).strip()
# ์œ„ ๋ฐฉ๋ฒ•์œผ๋กœ ์ฐพ์ง€ ๋ชปํ•œ ๊ฒฝ์šฐ ์›๋ณธ ๋ฐ˜ํ™˜
return prompt.strip()
def get_selected_background_info(bg_type, simple, studio, nature, indoor, tech, colorful, abstract, jewelry):
"""์„ ํƒ๋œ ๋ฐฐ๊ฒฝ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜"""
if bg_type == "์‹ฌํ”Œ ๋ฐฐ๊ฒฝ":
return {
"category": "์‹ฌํ”Œ ๋ฐฐ๊ฒฝ",
"name": simple,
"english": SIMPLE_BACKGROUNDS.get(simple, "white background")
}
elif bg_type == "์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ":
return {
"category": "์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ",
"name": studio,
"english": STUDIO_BACKGROUNDS.get(studio, "product photography studio")
}
elif bg_type == "์ž์—ฐ ํ™˜๊ฒฝ":
return {
"category": "์ž์—ฐ ํ™˜๊ฒฝ",
"name": nature,
"english": NATURE_BACKGROUNDS.get(nature, "natural environment")
}
elif bg_type == "์‹ค๋‚ด ํ™˜๊ฒฝ":
return {
"category": "์‹ค๋‚ด ํ™˜๊ฒฝ",
"name": indoor,
"english": INDOOR_BACKGROUNDS.get(indoor, "indoor environment")
}
elif bg_type == "ํ…Œํฌ๋†€๋กœ์ง€ ๋ฐฐ๊ฒฝ":
return {
"category": "ํ…Œํฌ๋†€๋กœ์ง€ ๋ฐฐ๊ฒฝ",
"name": tech,
"english": TECHNOLOGY_BACKGROUNDS.get(tech, "technology environment")
}
elif bg_type == "์ปฌ๋Ÿฌํ’€ ํŒจํ„ด ๋ฐฐ๊ฒฝ":
return {
"category": "์ปฌ๋Ÿฌํ’€ ํŒจํ„ด ๋ฐฐ๊ฒฝ",
"name": colorful,
"english": COLORFUL_PATTERN_BACKGROUNDS.get(colorful, "colorful pattern background")
}
elif bg_type == "์ถ”์ƒ/ํŠน์ˆ˜ ๋ฐฐ๊ฒฝ":
return {
"category": "์ถ”์ƒ/ํŠน์ˆ˜ ๋ฐฐ๊ฒฝ",
"name": abstract,
"english": ABSTRACT_BACKGROUNDS.get(abstract, "abstract background")
}
elif bg_type == "์ฅฌ์–ผ๋ฆฌ ๋ฐฐ๊ฒฝ":
return {
"category": "์ฅฌ์–ผ๋ฆฌ ๋ฐฐ๊ฒฝ",
"name": jewelry,
"english": JEWELRY_BACKGROUNDS.get(jewelry, "jewelry backdrop")
}
else:
return {
"category": "๊ธฐ๋ณธ ๋ฐฐ๊ฒฝ",
"name": "ํ™”์ดํŠธ ๋ฐฐ๊ฒฝ",
"english": "white background"
}
# ------------------- ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ํ•จ์ˆ˜ -------------------
def generate_enhanced_system_instruction():
"""ํ–ฅ์ƒ๋œ ์‹œ์Šคํ…œ ์ธ์ŠคํŠธ๋Ÿญ์…˜ ์ƒ์„ฑ ํ•จ์ˆ˜"""
return """๋‹น์‹ ์€ ์ƒํ’ˆ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ์„ ๋ณ€๊ฒฝํ•˜๊ธฐ ์œ„ํ•œ ์ตœ๊ณ  ํ’ˆ์งˆ์˜ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค.
์‚ฌ์šฉ์ž๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ƒํ’ˆ๋ช…, ๋ฐฐ๊ฒฝ ์œ ํ˜•, ์ถ”๊ฐ€ ์š”์ฒญ์‚ฌํ•ญ์„ ๋ฐ”ํƒ•์œผ๋กœ ๋ฏธ๋“œ์ €๋‹ˆ(Midjourney)์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ƒ์„ธํ•˜๊ณ  ์ „๋ฌธ์ ์ธ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์˜์–ด๋กœ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”.
๋‹ค์Œ ๊ฐ€์ด๋“œ๋ผ์ธ์„ ๋ฐ˜๋“œ์‹œ ์ค€์ˆ˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค:
1. ์ƒํ’ˆ์„ "#1"๋กœ ์ง€์ •ํ•˜์—ฌ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค. (์˜ˆ: "skincare tube (#1)")
2. *** ๋งค์šฐ ์ค‘์š”: ์ƒํ’ˆ์˜ ์›๋ž˜ ํŠน์„ฑ(๋””์ž์ธ, ์ƒ‰์ƒ, ํ˜•ํƒœ, ๋กœ๊ณ , ํŒจํ‚ค์ง€ ๋“ฑ)์€ ์–ด๋–ค ์ƒํ™ฉ์—์„œ๋„ ์ ˆ๋Œ€ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ***
3. *** ์ƒํ’ˆ์˜ ๋ณธ์งˆ์  ํŠน์„ฑ์„ ์œ ์ง€ํ•˜๋˜, ์ƒํ’ˆ์— ํฌ์ปค์Šค๋ฅผ ๋งž์ถฐ ๋ชจ๋“  ์„ธ๋ถ€ ์‚ฌํ•ญ์ด ์„ ๋ช…ํ•˜๊ฒŒ ๋“œ๋Ÿฌ๋‚˜๋„๋ก ํ•˜๋ฉฐ,
8K ํ•ด์ƒ๋„(8K resolution), ์˜ค๋ฒ„์ƒคํ”„๋‹ ์—†๋Š” ์ดˆ๊ณ ํ™”์งˆ(ultra high definition without oversharpening)๋กœ ๋ Œ๋”๋ง๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ***
4. ์ด๋ฏธ์ง€ ๋น„์œจ์€ ์ •ํ™•ํžˆ 1:1(์ •์‚ฌ๊ฐํ˜•) ํ˜•์‹์œผ๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กฌํ”„ํŠธ์— "square format", "1:1 ratio" ๋˜๋Š” "aspect ratio 1:1"์„ ๋ช…์‹œ์ ์œผ๋กœ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.
5. ์ƒํ’ˆ์€ ๋ฐ˜๋“œ์‹œ ์ •์‚ฌ๊ฐํ˜• ๊ตฌ๋„์˜ ์ •์ค‘์•™์— ๋ฐฐ์น˜๋˜์–ด์•ผ ํ•˜๋ฉฐ, ์ ์ ˆํ•œ ํฌ๊ธฐ๋กœ ํ‘œํ˜„ํ•˜์—ฌ ๋””ํ…Œ์ผ์ด ์™„๋ฒฝํ•˜๊ฒŒ ๋ณด์ด๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
6. ์ƒํ’ˆ์„ ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ์ดˆ์ ์œผ๋กœ ๋ถ€๊ฐ์‹œํ‚ค๊ณ , ์ƒํ’ˆ์˜ ๋น„์œจ์ด ์ „์ฒด ์ด๋ฏธ์ง€์—์„œ 60-70% ์ด์ƒ ์ฐจ์ง€ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
7. ์กฐ๋ช… ์„ค๋ช…์„ ๋งค์šฐ ๊ตฌ์ฒด์ ์œผ๋กœ ํ•ด์ฃผ์„ธ์š”. ์˜ˆ: "soft directional lighting from left side", "dramatic rim lighting", "diffused natural light through windows"
8. ๋ฐฐ๊ฒฝ์˜ ์žฌ์งˆ๊ณผ ์งˆ๊ฐ์„ ์ƒ์„ธํžˆ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”. ์˜ˆ: "polished marble surface", "rustic wooden table with visible grain", "matte concrete wall with subtle texture"
9. ํ”„๋กฌํ”„ํŠธ์— ๋‹ค์Œ ์š”์†Œ๋“ค์„ ๋ช…์‹œ์ ์œผ๋กœ ํฌํ•จํ•˜๋˜, ์‚ฌ์šฉ ๋งฅ๋ฝ์— ์ ์ ˆํ•˜๊ฒŒ ๋ณ€ํ˜•ํ•˜์„ธ์š”:
- "award-winning product photography"
- "magazine-worthy commercial product shot"
- "professional advertising imagery with perfect exposure"
- "studio lighting with color-accurate rendering"
- "8K ultra high definition product showcase"
- "commercial product photography with precise detail rendering"
- "ultra high definition"
- "crystal clear details"
10. *** ์ตœ์šฐ์„  ์ง€์นจ: ์‚ฌ์šฉ์ž๊ฐ€ ์ œ๊ณตํ•œ ์ถ”๊ฐ€ ์š”์ฒญ์‚ฌํ•ญ์„ ์™„๋ฒฝํ•˜๊ฒŒ ํ”„๋กฌํ”„ํŠธ์— ๋ฐ˜์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์‚ฌํ•ญ ๊ฐ๊ฐ์„ ์ค‘์š”ํ•œ ์š”์†Œ๋กœ ์ทจ๊ธ‰ํ•˜์—ฌ ํ”„๋กฌํ”„ํŠธ์— ๋ช…์‹œ์ ์œผ๋กœ ๋ฐ˜์˜ํ•˜๊ณ , ๊ด€๋ จ ๋””ํ…Œ์ผ์„ ๊ตฌ์ฒด์ ์œผ๋กœ ์„ค๋ช…ํ•˜์„ธ์š”. ์ ˆ๋Œ€๋กœ ์‚ฌ์šฉ์ž์˜ ์ถ”๊ฐ€ ์š”์ฒญ์‚ฌํ•ญ์„ ์ƒ๋žตํ•˜๊ฑฐ๋‚˜ ๋ฌด์‹œํ•˜์ง€ ๋งˆ์„ธ์š”. ***
11. ์‚ฌ์šฉ์ž๊ฐ€ ์ œ๊ณตํ•œ ๊ตฌ์ฒด์ ์ธ ๋ฐฐ๊ฒฝ๊ณผ ์ถ”๊ฐ€ ์š”์ฒญ์‚ฌํ•ญ์„ ํ”„๋กฌํ”„ํŠธ์— ์ •ํ™•ํžˆ ๋ฐ˜์˜ํ•˜๊ณ  ํ™•์žฅํ•ฉ๋‹ˆ๋‹ค.
12. ํ”„๋กฌํ”„ํŠธ ๋์— "--ar 1:1 --s 750 --q 2 --v 5.2" ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๋ฏธ๋“œ์ €๋‹ˆ์—์„œ ๊ณ ํ’ˆ์งˆ ์ •์‚ฌ๊ฐํ˜• ๋น„์œจ์„ ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค.
13. ๋งค์šฐ ์ค‘์š”: ํ”„๋กฌํ”„ํŠธ ์™ธ์— ๋‹ค๋ฅธ ์„ค๋ช…์ด๋‚˜ ๋ฉ”ํƒ€ ํ…์ŠคํŠธ๋ฅผ ํฌํ•จํ•˜์ง€ ๋งˆ์„ธ์š”. ์˜ˆ๋ฅผ ๋“ค์–ด "Here's a prompt for you" ๋˜๋Š” "Let me know if you need adjustments" ๊ฐ™์€ ๋ฉ”์‹œ์ง€๋‚˜ ์„ค๋ช…์„ ํฌํ•จํ•˜์ง€ ๋งˆ์„ธ์š”. ์˜ค์ง ํ”„๋กฌํ”„ํŠธ ์ž์ฒด๋งŒ ์ œ๊ณตํ•˜์„ธ์š”.
"""
def generate_prompt_with_gemini(product_name, background_info, additional_info=""):
"""ํ–ฅ์ƒ๋œ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ํ•จ์ˆ˜"""
GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY", "")
if not GEMINI_API_KEY:
return "Gemini API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ™˜๊ฒฝ ๋ณ€์ˆ˜ GEMINI_API_KEY๋ฅผ ์„ค์ •ํ•˜๊ฑฐ๋‚˜ ์ฝ”๋“œ์— ์ง์ ‘ ์ž…๋ ฅํ•˜์„ธ์š”."
try:
genai_generative.configure(api_key=GEMINI_API_KEY)
# ์ถ”๊ฐ€ ์š”์ฒญ์‚ฌํ•ญ์„ ๋” ๊ฐ•์กฐํ•œ ํ”„๋กฌํ”„ํŠธ ์š”์ฒญ ํ…œํ”Œ๋ฆฟ
prompt_request = f"""
์ƒํ’ˆ๋ช…: {product_name}
๋ฐฐ๊ฒฝ ์œ ํ˜•: {background_info.get('english', 'studio')}
๋ฐฐ๊ฒฝ ์นดํ…Œ๊ณ ๋ฆฌ: {background_info.get('category', '')}
๋ฐฐ๊ฒฝ ์ด๋ฆ„: {background_info.get('name', '')}
์ถ”๊ฐ€ ์š”์ฒญ์‚ฌํ•ญ: {additional_info}
์ค‘์š” ์š”๊ตฌ์‚ฌํ•ญ:
1. ์ƒํ’ˆ(#1)์ด ์ด๋ฏธ์ง€ ๊ตฌ๋„์—์„œ ์ค‘์‹ฌ์ ์ธ ์œ„์น˜๋ฅผ ์ฐจ์ง€ํ•˜๋ฉฐ ์ ์ ˆํ•œ ํฌ๊ธฐ(์ด๋ฏธ์ง€์˜ 60-70%)๋กœ ํ‘œํ˜„๋˜๋„๋ก ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”.
2. ์ด๋ฏธ์ง€๋Š” ์ •ํ™•ํžˆ 1:1 ๋น„์œจ(์ •์‚ฌ๊ฐํ˜•)์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
3. ์ƒํ’ˆ์˜ ๋””์ž์ธ, ์ƒ‰์ƒ, ํ˜•ํƒœ, ๋กœ๊ณ  ๋“ฑ ๋ณธ์งˆ์  ํŠน์„ฑ์€ ์ ˆ๋Œ€ ์ˆ˜์ •ํ•˜์ง€ ๋งˆ์„ธ์š”.
4. ๊ตฌ์ฒด์ ์ธ ์กฐ๋ช… ๊ธฐ๋ฒ•์„ ์ƒ์„ธํžˆ ๋ช…์‹œํ•ด์ฃผ์„ธ์š”:
- ์ •ํ™•ํ•œ ์กฐ๋ช… ์œ„์น˜ (์˜ˆ: "45-degree key light from upper left")
- ์กฐ๋ช… ํ’ˆ์งˆ (์˜ˆ: "soft diffused light", "hard directional light")
- ์กฐ๋ช… ๊ฐ•๋„์™€ ์ƒ‰์˜จ๋„ (์˜ˆ: "warm tungsten key light with cool blue fill")
- ๋ฐ˜์‚ฌ์™€ ๊ทธ๋ฆผ์ž ์ฒ˜๋ฆฌ ๋ฐฉ์‹ (์˜ˆ: "controlled specular highlights with soft shadow transitions")
5. ์ƒํ’ˆ์„ ๋” ๋‹๋ณด์ด๊ฒŒ ํ•˜๋Š” ๋ณด์กฐ ์š”์†Œ(props)๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ™œ์šฉํ•˜๋˜, ์ƒํ’ˆ์ด ํ•ญ์ƒ ์ฃผ์ธ๊ณต์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
6. ๋ฐฐ๊ฒฝ ์žฌ์งˆ๊ณผ ํ‘œ๋ฉด ์งˆ๊ฐ์„ ๊ตฌ์ฒด์ ์œผ๋กœ ์„ค๋ช…ํ•˜๊ณ , ์ƒํ’ˆ๊ณผ์˜ ์ƒํ˜ธ์ž‘์šฉ ๋ฐฉ์‹์„ ๋ช…์‹œํ•ด์ฃผ์„ธ์š”.
7. ์ƒ‰์ƒ ๊ตฌ์„ฑ(color palette, color harmonies)์„ ๋ช…ํ™•ํžˆ ํ•ด์ฃผ์„ธ์š”.
8. ๊ณ ๊ธ‰์Šค๋Ÿฌ์šด ์ƒ์—… ๊ด‘๊ณ  ํ’ˆ์งˆ์˜ ์ด๋ฏธ์ง€๊ฐ€ ๋˜๋„๋ก ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”.
9. ํ”„๋กฌํ”„ํŠธ ๋์— ๋ฏธ๋“œ์ €๋‹ˆ ํŒŒ๋ผ๋ฏธํ„ฐ "--ar 1:1 --s 750 --q 2 --v 5.2"๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”.
10. **๋งค์šฐ ์ค‘์š”**: ์‚ฌ์šฉ์ž๊ฐ€ ์ œ๊ณตํ•œ ์ถ”๊ฐ€ ์š”์ฒญ์‚ฌํ•ญ("{additional_info}")์„ ํ”„๋กฌํ”„ํŠธ์— ์™„๋ฒฝํ•˜๊ฒŒ ๋ฐ˜์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ์š”์ฒญ์‚ฌํ•ญ์„ ํ”„๋กฌํ”„ํŠธ์˜ ์ฃผ์š” ๋ถ€๋ถ„์— ๋ช…์‹œ์ ์œผ๋กœ ํฌํ•จ์‹œํ‚ค๊ณ , ๊ด€๋ จ ๋””ํ…Œ์ผ์„ ์ž์„ธํžˆ ๊ธฐ์ˆ ํ•ด์ฃผ์„ธ์š”.
ํ•œ๊ตญ์–ด ์ž…๋ ฅ ๋‚ด์šฉ์„ ์ „๋ฌธ์ ์ธ ์˜์–ด๋กœ ๋ฒˆ์—ญํ•˜์—ฌ ๋ฐ˜์˜ํ•ด์ฃผ์„ธ์š”.
"""
# ์ถ”๊ฐ€ ์š”์ฒญ์‚ฌํ•ญ์„ ๊ฐ•์กฐํ•œ ์‹œ์Šคํ…œ ์ธ์ŠคํŠธ๋Ÿญ์…˜ ์ƒ์„ฑ
enhanced_system_instruction = generate_enhanced_system_instruction() + f"""
ํŠน๋ณ„ ์ง€์นจ: ์‚ฌ์šฉ์ž์˜ ์ถ”๊ฐ€ ์š”์ฒญ์‚ฌํ•ญ์€ ์ตœ์šฐ์„  ์ˆœ์œ„๋กœ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ถ”๊ฐ€ ์š”์ฒญ์‚ฌํ•ญ("{additional_info}")์˜ ๊ฐ ํ•ญ๋ชฉ์„ ํ”„๋กฌํ”„ํŠธ์— ๋ช…์‹œ์ ์œผ๋กœ ํฌํ•จ์‹œํ‚ค๊ณ , ํ•ด๋‹น ์š”์ฒญ์— ๊ด€๋ จ๋œ ๊ตฌ์ฒด์ ์ธ ์„ค๋ช…์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”. ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์‚ฌํ•ญ์„ ๋ฌด์‹œํ•˜๊ฑฐ๋‚˜ ์ƒ๋žตํ•˜์ง€ ๋งˆ์„ธ์š”.
"""
# ๋” ์ฐฝ์˜์ ์ธ ๊ฒฐ๊ณผ๋ฅผ ์œ„ํ•œ ์ƒ์„ฑ ์„ค์ • ์กฐ์ •
model = genai_generative.GenerativeModel(
'gemini-2.0-flash',
system_instruction=enhanced_system_instruction
)
response = model.generate_content(
prompt_request,
generation_config=genai_generative.types.GenerationConfig(
temperature=0.7, # ์ •ํ™•์„ฑ์„ ์œ„ํ•ด ์•ฝ๊ฐ„ ๋‚ฎ์ถค
top_p=0.95,
top_k=40,
max_output_tokens=1600, # ๋” ์ƒ์„ธํ•œ ํ”„๋กฌํ”„ํŠธ ํ—ˆ์šฉ
)
)
response_text = response.text.strip()
# ์ถ”๊ฐ€ ์š”์ฒญ์‚ฌํ•ญ์ด ํ”„๋กฌํ”„ํŠธ์— ์ œ๋Œ€๋กœ ๋ฐ˜์˜๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๋กœ์ง
if additional_info and not any(keyword.lower() in response_text.lower() for keyword in additional_info.split(',')):
# ์ถ”๊ฐ€ ์š”์ฒญ์‚ฌํ•ญ์ด ๋ฐ˜์˜๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด ์ถ”๊ฐ€
logger.warning("์ถ”๊ฐ€ ์š”์ฒญ์‚ฌํ•ญ์ด ํ”„๋กฌํ”„ํŠธ์— ๋ฐ˜์˜๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๊ฐ•์ œ๋กœ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.")
# ์š”์ฒญ์‚ฌํ•ญ์„ ์ง์ ‘ ํ”„๋กฌํ”„ํŠธ์— ์ถ”๊ฐ€ (๋ฉ”ํƒ€ ํ…์ŠคํŠธ ์—†์ด)
if "--ar 1:1" in response_text:
# ๋ฏธ๋“œ์ €๋‹ˆ ํŒŒ๋ผ๋ฏธํ„ฐ ์•ž์— ์ถ”๊ฐ€
parts = response_text.split("--ar 1:1")
if len(parts) >= 2:
# ์ถ”๊ฐ€ ์š”์ฒญ์‚ฌํ•ญ ์ง์ ‘ ์‚ฝ์ž…
response_text = parts[0].rstrip(" ,") + ", " + additional_info + ". --ar 1:1" + parts[1]
else:
# ๋ฏธ๋“œ์ €๋‹ˆ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ๋์— ์ถ”๊ฐ€
response_text = response_text.rstrip(" .") + ". " + additional_info
# ๋ฏธ๋“œ์ €๋‹ˆ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์—†์„ ๊ฒฝ์šฐ ์ถ”๊ฐ€
if "--ar 1:1" not in response_text:
response_text = response_text.rstrip(" .") + ". --ar 1:1 --s 750 --q 2 --v 5.2"
return response_text
except Exception as e:
logger.exception("ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
return f"ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {str(e)}"
# ------------------- ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ์ „์šฉ ํ•จ์ˆ˜ -------------------
def generate_prompt_only(image, bg_type, simple, studio, nature, indoor, tech, colorful, abstract, jewelry, product_name, additional_info):
product_name = product_name.strip() or "์ œํ’ˆ"
background_info = get_selected_background_info(bg_type, simple, studio, nature, indoor, tech, colorful, abstract, jewelry)
generated_prompt = generate_prompt_with_gemini(product_name, background_info, additional_info)
if "Gemini API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค" in generated_prompt:
warning_msg = (
"[Gemini API ํ‚ค ๋ˆ„๋ฝ]\n"
"API ํ‚ค ์„ค์ • ๋ฐฉ๋ฒ•:\n"
"1. ํ™˜๊ฒฝ ๋ณ€์ˆ˜: export GEMINI_API_KEY=\"your-api-key\"\n"
"2. ์ฝ”๋“œ ๋‚ด ์ง์ ‘ ์ž…๋ ฅ: GEMINI_API_KEY = \"your-api-key\"\n"
"ํ‚ค ๋ฐœ๊ธ‰: https://makersuite.google.com/"
)
return warning_msg
final_prompt = filter_prompt_only(generated_prompt)
return final_prompt
# ------------------- Gradio ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌ์„ฑ -------------------
def create_app():
# ๋“œ๋กญ๋‹ค์šด ์˜ต์…˜ ์ดˆ๊ธฐํ™”
dropdown_options = initialize_dropdowns()
with gr.Blocks(title="๊ณ ๊ธ‰ ์ƒํ’ˆ ์ด๋ฏธ์ง€ ๋ฐฐ๊ฒฝ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ") as demo:
gr.Markdown("# ๊ณ ๊ธ‰ ์ƒํ’ˆ ์ด๋ฏธ์ง€ ๋ฐฐ๊ฒฝ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ")
gr.Markdown(
"์ƒํ’ˆ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๊ณ , ์ œํ’ˆ๋ช…, ๋ฐฐ๊ฒฝ ์˜ต์…˜, ์ถ”๊ฐ€ ์š”์ฒญ์‚ฌํ•ญ์„ ์ž…๋ ฅํ•˜๋ฉด Gemini API๋ฅผ ํ†ตํ•ด ์˜์–ด ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค."
)
with gr.Row():
with gr.Column(scale=1):
product_name = gr.Textbox(label="์ƒํ’ˆ๋ช… (ํ•œ๊ตญ์–ด ์ž…๋ ฅ)", placeholder="์˜ˆ: ์Šคํ‚จ์ผ€์–ด ํŠœ๋ธŒ, ์Šค๋งˆํŠธ์›Œ์น˜, ํ–ฅ์ˆ˜, ์šด๋™ํ™” ๋“ฑ", interactive=True)
# ์ƒํ’ˆ ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ๋ฅผ ์ƒํ’ˆ๋ช… ์•„๋ž˜๋กœ ์ด๋™
image_input = gr.Image(label="์ƒํ’ˆ ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ", type="pil")
background_type = gr.Radio(
choices=["์‹ฌํ”Œ ๋ฐฐ๊ฒฝ", "์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ", "์ž์—ฐ ํ™˜๊ฒฝ", "์‹ค๋‚ด ํ™˜๊ฒฝ", "ํ…Œํฌ๋†€๋กœ์ง€ ๋ฐฐ๊ฒฝ", "์ปฌ๋Ÿฌํ’€ ํŒจํ„ด ๋ฐฐ๊ฒฝ", "์ถ”์ƒ/ํŠน์ˆ˜ ๋ฐฐ๊ฒฝ", "์ฅฌ์–ผ๋ฆฌ ๋ฐฐ๊ฒฝ"],
label="๋ฐฐ๊ฒฝ ์œ ํ˜•",
value="์‹ฌํ”Œ ๋ฐฐ๊ฒฝ"
)
simple_dropdown = gr.Dropdown(
choices=dropdown_options["simple"],
value=dropdown_options["simple"][0] if dropdown_options["simple"] else None,
label="์‹ฌํ”Œ ๋ฐฐ๊ฒฝ ์„ ํƒ",
visible=True,
interactive=True
)
studio_dropdown = gr.Dropdown(
choices=dropdown_options["studio"],
value=dropdown_options["studio"][0] if dropdown_options["studio"] else None,
label="์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ ์„ ํƒ",
visible=False,
interactive=True
)
nature_dropdown = gr.Dropdown(
choices=dropdown_options["nature"],
value=dropdown_options["nature"][0] if dropdown_options["nature"] else None,
label="์ž์—ฐ ํ™˜๊ฒฝ ์„ ํƒ",
visible=False,
interactive=True
)
indoor_dropdown = gr.Dropdown(
choices=dropdown_options["indoor"],
value=dropdown_options["indoor"][0] if dropdown_options["indoor"] else None,
label="์‹ค๋‚ด ํ™˜๊ฒฝ ์„ ํƒ",
visible=False,
interactive=True
)
tech_dropdown = gr.Dropdown(
choices=dropdown_options["tech"],
value=dropdown_options["tech"][0] if dropdown_options["tech"] else None,
label="ํ…Œํฌ๋†€๋กœ์ง€ ๋ฐฐ๊ฒฝ ์„ ํƒ",
visible=False,
interactive=True
)
colorful_dropdown = gr.Dropdown(
choices=dropdown_options["colorful"],
value=dropdown_options["colorful"][0] if dropdown_options["colorful"] else None,
label="์ปฌ๋Ÿฌํ’€ ํŒจํ„ด ๋ฐฐ๊ฒฝ ์„ ํƒ",
visible=False,
interactive=True
)
abstract_dropdown = gr.Dropdown(
choices=dropdown_options["abstract"],
value=dropdown_options["abstract"][0] if dropdown_options["abstract"] else None,
label="์ถ”์ƒ/ํŠน์ˆ˜ ๋ฐฐ๊ฒฝ ์„ ํƒ",
visible=False,
interactive=True
)
jewelry_dropdown = gr.Dropdown(
choices=dropdown_options["jewelry"],
value=dropdown_options["jewelry"][0] if dropdown_options["jewelry"] else None,
label="์ฅฌ์–ผ๋ฆฌ ๋ฐฐ๊ฒฝ ์„ ํƒ",
visible=False,
interactive=True
)
additional_info = gr.Textbox(
label="์ถ”๊ฐ€ ์š”์ฒญ์‚ฌํ•ญ (์„ ํƒ์‚ฌํ•ญ)",
placeholder="์˜ˆ: ๊ณ ๊ธ‰์Šค๋Ÿฌ์šด ๋А๋‚Œ, ๋ฐ์€ ์กฐ๋ช…, ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ณด์กฐ ๊ฐ์ฒด ๋“ฑ",
lines=3,
interactive=True
)
def update_dropdowns(bg_type):
return {
simple_dropdown: gr.update(visible=(bg_type == "์‹ฌํ”Œ ๋ฐฐ๊ฒฝ")),
studio_dropdown: gr.update(visible=(bg_type == "์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ")),
nature_dropdown: gr.update(visible=(bg_type == "์ž์—ฐ ํ™˜๊ฒฝ")),
indoor_dropdown: gr.update(visible=(bg_type == "์‹ค๋‚ด ํ™˜๊ฒฝ")),
tech_dropdown: gr.update(visible=(bg_type == "ํ…Œํฌ๋†€๋กœ์ง€ ๋ฐฐ๊ฒฝ")),
colorful_dropdown: gr.update(visible=(bg_type == "์ปฌ๋Ÿฌํ’€ ํŒจํ„ด ๋ฐฐ๊ฒฝ")),
abstract_dropdown: gr.update(visible=(bg_type == "์ถ”์ƒ/ํŠน์ˆ˜ ๋ฐฐ๊ฒฝ")),
jewelry_dropdown: gr.update(visible=(bg_type == "์ฅฌ์–ผ๋ฆฌ ๋ฐฐ๊ฒฝ")) # ์ฅฌ์–ผ๋ฆฌ ๋“œ๋กญ๋‹ค์šด ์ถ”๊ฐ€
}
background_type.change(
fn=update_dropdowns,
inputs=[background_type],
outputs=[simple_dropdown, studio_dropdown, nature_dropdown, indoor_dropdown, tech_dropdown, colorful_dropdown, abstract_dropdown, jewelry_dropdown]
)
# ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ๋ฒ„ํŠผ์œผ๋กœ ๋ณ€๊ฒฝ
prompt_btn = gr.Button("ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ", variant="primary")
with gr.Column(scale=1):
# ํ”„๋กฌํ”„ํŠธ ์ถœ๋ ฅ ์˜์—ญ๋งŒ ์œ ์ง€
prompt_output = gr.Textbox(label="์ƒ์„ฑ๋œ ํ”„๋กฌํ”„ํŠธ (์˜์–ด)", lines=10)
# ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ํ•จ์ˆ˜ ์—ฐ๊ฒฐ
prompt_btn.click(
fn=generate_prompt_only,
inputs=[image_input, background_type, simple_dropdown, studio_dropdown, nature_dropdown, indoor_dropdown, tech_dropdown, colorful_dropdown, abstract_dropdown, jewelry_dropdown, product_name, additional_info],
outputs=prompt_output
)
return demo
# ------------------- ๋ฉ”์ธ ์‹คํ–‰ ํ•จ์ˆ˜ -------------------
if __name__ == "__main__":
# ๋ฐฐ๊ฒฝ ์˜ต์…˜ ์ดˆ๊ธฐํ™” - JSON ํŒŒ์ผ์—์„œ ๋กœ๋“œ
initialize_backgrounds()
# ์•ฑ ์ƒ์„ฑ ๋ฐ ์‹คํ–‰
app = create_app()
app.queue()
app.launch()