test-100 / app.py
Kims12's picture
Update app.py
f2b06f7 verified
raw
history blame
60.5 kB
import os
import tempfile
from PIL import Image
import gradio as gr
import logging
import re
import time
from google import genai
from google.genai import types
from dotenv import load_dotenv
from transformers import pipeline # ์ƒˆ๋กœ ์ถ”๊ฐ€๋œ import
load_dotenv()
# ๊ธฐ์กด ์ฝ”๋“œ ์œ ์ง€ (๋กœ๊น…, ํ•จ์ˆ˜ ๋“ฑ ๋ชจ๋“  ๊ธฐ๋Šฅ ์ฝ”๋“œ)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ ๊ธฐ๋Šฅ ์ถ”๊ฐ€
def remove_background(image):
if image is None:
return None, "์ด๋ฏธ์ง€๊ฐ€ ์—…๋กœ๋“œ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."
try:
logger.info("๋ฐฐ๊ฒฝ ์ œ๊ฑฐ ์‹œ์ž‘")
# ๋ชจ๋ธ์„ ์ฒ˜์Œ ๋กœ๋“œํ•  ๋•Œ๋Š” ๋‹ค์šด๋กœ๋“œํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์Œ
pipe = pipeline("image-segmentation", model="briaai/RMBG-1.4", trust_remote_code=True)
# ๋ฐฐ๊ฒฝ์ด ์ œ๊ฑฐ๋œ ์ด๋ฏธ์ง€ ๊ฐ€์ ธ์˜ค๊ธฐ
output_image = pipe(image)
logger.info("๋ฐฐ๊ฒฝ ์ œ๊ฑฐ ์™„๋ฃŒ")
return output_image, "๋ฐฐ๊ฒฝ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ œ๊ฑฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."
except Exception as e:
logger.exception("๋ฐฐ๊ฒฝ ์ œ๊ฑฐ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
def save_binary_file(file_name, data):
with open(file_name, "wb") as f:
f.write(data)
# ์ด๋ฏธ์ง€ ํ•„ํ„ฐ ๊ธฐ๋Šฅ์„ ์œ„ํ•œ ์ถ”๊ฐ€ ์ฝ”๋“œ
import cv2
import numpy as np
from PIL import Image, ImageEnhance, ImageFilter
import tempfile
from datetime import datetime, timedelta
# ์ด๋ฏธ์ง€ ํ•„ํ„ฐ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜
def adjust_brightness(image, value):
"""์ด๋ฏธ์ง€ ๋ฐ๊ธฐ ์กฐ์ ˆ"""
value = float(value - 1) * 100 # 0-2 ๋ฒ”์œ„๋ฅผ -100์—์„œ +100์œผ๋กœ ๋ณ€ํ™˜
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)
v = cv2.add(v, value)
v = np.clip(v, 0, 255)
final_hsv = cv2.merge((h, s, v))
return cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
def adjust_contrast(image, value):
"""์ด๋ฏธ์ง€ ๋Œ€๋น„ ์กฐ์ ˆ"""
value = float(value)
return np.clip(image * value, 0, 255).astype(np.uint8)
def adjust_saturation(image, value):
"""์ด๋ฏธ์ง€ ์ฑ„๋„ ์กฐ์ ˆ"""
value = float(value - 1) * 100 # 0-2 ๋ฒ”์œ„๋ฅผ -100์—์„œ +100์œผ๋กœ ๋ณ€ํ™˜
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)
s = cv2.add(s, value)
s = np.clip(s, 0, 255)
final_hsv = cv2.merge((h, s, v))
return cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
def adjust_temperature(image, value):
"""์ด๋ฏธ์ง€ ์ƒ‰์˜จ๋„ ์กฐ์ ˆ (์ƒ‰์ƒ ๋ฐธ๋Ÿฐ์Šค)"""
value = float(value) * 30 # ํšจ๊ณผ ์Šค์ผ€์ผ ์กฐ์ ˆ
b, g, r = cv2.split(image)
if value > 0: # ๋”ฐ๋œปํ•˜๊ฒŒ
r = cv2.add(r, value)
b = cv2.subtract(b, value)
else: # ์ฐจ๊ฐ‘๊ฒŒ
r = cv2.add(r, value)
b = cv2.subtract(b, value)
r = np.clip(r, 0, 255)
b = np.clip(b, 0, 255)
return cv2.merge([b, g, r])
def adjust_tint(image, value):
"""์ด๋ฏธ์ง€ ์ƒ‰์กฐ ์กฐ์ ˆ"""
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv_image)
h = cv2.add(h, int(value))
h = np.clip(h, 0, 179) # Hue ๊ฐ’์€ 0-179 ๋ฒ”์œ„
final_hsv = cv2.merge((h, s, v))
return cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
def adjust_exposure(image, value):
"""์ด๋ฏธ์ง€ ๋…ธ์ถœ ์กฐ์ ˆ"""
enhancer = ImageEnhance.Brightness(Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)))
img_enhanced = enhancer.enhance(1 + float(value) / 5.0)
return cv2.cvtColor(np.array(img_enhanced), cv2.COLOR_RGB2BGR)
def adjust_vibrance(image, value):
"""์ด๋ฏธ์ง€ ํ™œ๊ธฐ ์กฐ์ ˆ"""
img = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
converter = ImageEnhance.Color(img)
factor = 1 + (float(value) / 100.0)
img = converter.enhance(factor)
return cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
def adjust_color_mixer_blues(image, value):
"""์ด๋ฏธ์ง€ ์ปฌ๋Ÿฌ ๋ฏน์„œ (๋ธ”๋ฃจ) ์กฐ์ ˆ"""
b, g, r = cv2.split(image)
b = cv2.add(b, float(value))
b = np.clip(b, 0, 255)
return cv2.merge([b, g, r])
def adjust_shadows(image, value):
"""์ด๋ฏธ์ง€ ๊ทธ๋ฆผ์ž ์กฐ์ ˆ"""
pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
enhancer = ImageEnhance.Brightness(pil_image)
factor = 1 + (float(value) / 100.0)
pil_image = enhancer.enhance(factor)
return cv2.cvtColor(np.array(pil_image), cv2.COLOR_BGR2RGB)
def process_image(image, brightness, contrast, saturation, temperature, tint, exposure, vibrance, color_mixer_blues, shadows):
"""๋ชจ๋“  ์กฐ์ • ์‚ฌํ•ญ์„ ์ด๋ฏธ์ง€์— ์ ์šฉ"""
if image is None:
return None
# PIL ์ด๋ฏธ์ง€๋ฅผ OpenCV ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜
image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
# ์กฐ์ • ์‚ฌํ•ญ ์ˆœ์ฐจ ์ ์šฉ
image = adjust_brightness(image, brightness)
image = adjust_contrast(image, contrast)
image = adjust_saturation(image, saturation)
image = adjust_temperature(image, temperature)
image = adjust_tint(image, tint)
image = adjust_exposure(image, exposure)
image = adjust_vibrance(image, vibrance)
image = adjust_color_mixer_blues(image, color_mixer_blues)
image = adjust_shadows(image, shadows)
# PIL ์ด๋ฏธ์ง€๋กœ ๋‹ค์‹œ ๋ณ€ํ™˜
return Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
def translate_prompt_to_english(prompt):
# ๊ธฐ์กด ํ•จ์ˆ˜ ์œ ์ง€
if not re.search("[๊ฐ€-ํžฃ]", prompt):
return prompt
prompt = prompt.replace("#1", "IMAGE_TAG_ONE")
prompt = prompt.replace("#2", "IMAGE_TAG_TWO")
prompt = prompt.replace("#3", "IMAGE_TAG_THREE")
try:
api_key = os.environ.get("GEMINI_API_KEY")
if not api_key:
logger.error("Gemini API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
prompt = prompt.replace("IMAGE_TAG_TWO", "#2")
prompt = prompt.replace("IMAGE_TAG_THREE", "#3")
return prompt
client = genai.Client(api_key=api_key)
translation_prompt = f"""
Translate the following Korean text to English:
{prompt}
IMPORTANT: The tokens IMAGE_TAG_ONE, IMAGE_TAG_TWO, and IMAGE_TAG_THREE are special tags
and must be preserved exactly as is in your translation. Do not translate these tokens.
"""
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():
translated_text = translated_text.replace("IMAGE_TAG_ONE", "#1")
translated_text = translated_text.replace("IMAGE_TAG_TWO", "#2")
translated_text = translated_text.replace("IMAGE_TAG_THREE", "#3")
logger.info(f"Translated text: {translated_text.strip()}")
return translated_text.strip()
else:
logger.warning("๋ฒˆ์—ญ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์›๋ณธ ํ”„๋กฌํ”„ํŠธ ์‚ฌ์šฉ")
prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
prompt = prompt.replace("IMAGE_TAG_TWO", "#2")
prompt = prompt.replace("IMAGE_TAG_THREE", "#3")
return prompt
except Exception as e:
logger.exception("๋ฒˆ์—ญ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
prompt = prompt.replace("IMAGE_TAG_TWO", "#2")
prompt = prompt.replace("IMAGE_TAG_THREE", "#3")
return prompt
def preprocess_prompt(prompt, image1, image2, image3):
# ๊ธฐ์กด ํ•จ์ˆ˜ ์œ ์ง€
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:
prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ์„ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ํ”ผ์‚ฌ์ฒด๋Š” ์œ ์ง€ํ•˜๊ณ , ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ๊ณผ ์กฐํ™”๋กญ๊ฒŒ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”."
elif "6. ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ(์ƒํ’ˆํฌํ•จ)" in prompt:
prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์™€ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(๋˜๋Š” ์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€)๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”. ๋ชจ๋“  ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ์š”์†Œ๋ฅผ ํฌํ•จํ•˜๊ณ , ํŠนํžˆ ์ƒํ’ˆ์ด ๋‹๋ณด์ด๋„๋ก ์กฐํ™”๋กญ๊ฒŒ ํ†ตํ•ฉํ•ด์ฃผ์„ธ์š”."
prompt += " ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”. ์ด๋ฏธ์ง€์— ํ…์ŠคํŠธ๋‚˜ ๊ธ€์ž๋ฅผ ํฌํ•จํ•˜์ง€ ๋งˆ์„ธ์š”."
return prompt
def generate_with_images(prompt, images, variation_index=0):
# ๊ธฐ์กด ํ•จ์ˆ˜ ์œ ์ง€
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}, ๋ณ€ํ˜• ์ธ๋ฑ์Šค: {variation_index}")
variation_suffixes = [
" Create this as the first variation. Do not add any text, watermarks, or labels to the image.",
" Create this as the second variation with more vivid colors. Do not add any text, watermarks, or labels to the image.",
" Create this as the third variation with a more creative style. Do not add any text, watermarks, or labels to the image.",
" Create this as the fourth variation with enhanced details. Do not add any text, watermarks, or labels to the image."
]
if variation_index < len(variation_suffixes):
prompt = prompt + variation_suffixes[variation_index]
else:
prompt = prompt + " Do not add any text, watermarks, or labels to the image."
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
)
)
# ์ž„์‹œ ํŒŒ์ผ์€ ํ•ญ์ƒ JPG ํ™•์žฅ์ž๋กœ ์ƒ์„ฑ
with tempfile.NamedTemporaryFile(suffix=".jpg", 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") # JPG๋Š” ํˆฌ๋ช…๋„๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ RGB๋กœ ๋ณ€ํ™˜
# ๋ณ€ํ™˜๋œ ์ด๋ฏธ์ง€๋ฅผ JPG๋กœ ์ €์žฅ
result_img.save(temp_path, format="JPEG", quality=95)
# ํŒŒ์ผ ๊ฒฝ๋กœ ๋ฐ˜ํ™˜
return temp_path, f"์ด๋ฏธ์ง€๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. {result_text}"
except Exception as e:
logger.exception("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
def process_images_with_prompt(image1, image2, image3, prompt, variation_index=0, max_retries=3):
# ๊ธฐ์กด ํ•จ์ˆ˜ ๋‚ด์šฉ ์œ ์ง€
retry_count = 0
last_error = None
while retry_count < max_retries:
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)
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. Do not include any text or watermarks in the generated image."
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. Do not include any text or watermarks in the generated 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. Do not include any text or watermarks in the generated image."
logger.info("Default prompt generated for three images")
result_img, status = generate_with_images(final_prompt, valid_images, variation_index)
if result_img is not None:
# ์ด๋ฏธ ํŒŒ์ผ ๊ฒฝ๋กœ๊ฐ€ ๋ฐ˜ํ™˜๋˜๋ฏ€๋กœ ์ถ”๊ฐ€ ์ฒ˜๋ฆฌ ์—†์ด ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜
return result_img, status, final_prompt
else:
last_error = status
retry_count += 1
logger.warning(f"์ด๋ฏธ์ง€ ์ƒ์„ฑ ์‹คํŒจ, ์žฌ์‹œ๋„ {retry_count}/{max_retries}: {status}")
time.sleep(1)
except Exception as e:
last_error = str(e)
retry_count += 1
logger.exception(f"์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ, ์žฌ์‹œ๋„ {retry_count}/{max_retries}:")
time.sleep(1)
return None, f"์ตœ๋Œ€ ์žฌ์‹œ๋„ ํšŸ์ˆ˜({max_retries}ํšŒ) ์ดˆ๊ณผ ํ›„ ์‹คํŒจ: {last_error}", prompt
def generate_multiple_images(image1, image2, image3, prompt, progress=gr.Progress()):
# ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€๋“ค์ด JPG๋กœ ๋ณ€ํ™˜๋˜๋„๋ก ์ˆ˜์ •
results = []
statuses = []
prompts = []
num_images = 4
max_retries = 3
progress(0, desc="์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค€๋น„ ์ค‘...")
for i in range(num_images):
progress((i / num_images), desc=f"{i+1}/{num_images} ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘...")
result_img, status, final_prompt = process_images_with_prompt(image1, image2, image3, prompt, i, max_retries)
if result_img is not None:
# ์ด๋ฏธ ํŒŒ์ผ ๊ฒฝ๋กœ์ด๋ฏ€๋กœ ์ถ”๊ฐ€ ์ฒ˜๋ฆฌ ์—†์ด ๊ทธ๋Œ€๋กœ ์ถ”๊ฐ€
results.append(result_img)
statuses.append(f"์ด๋ฏธ์ง€ #{i+1}: {status}")
prompts.append(f"์ด๋ฏธ์ง€ #{i+1}: {final_prompt}")
else:
results.append(None)
statuses.append(f"์ด๋ฏธ์ง€ #{i+1} ์ƒ์„ฑ ์‹คํŒจ: {status}")
prompts.append(f"์ด๋ฏธ์ง€ #{i+1}: {final_prompt}")
time.sleep(1)
progress(1.0, desc="์ด๋ฏธ์ง€ ์ƒ์„ฑ ์™„๋ฃŒ!")
while len(results) < 4:
results.append(None)
combined_status = "\n".join(statuses)
combined_prompts = "\n".join(prompts)
return results[0], results[1], results[2], results[3], combined_status, combined_prompts
# GFPGAN ๊ด€๋ จ ์ฝ”๋“œ ์œ ์ง€
import sys
from torchvision.transforms import functional
sys.modules["torchvision.transforms.functional_tensor"] = functional
from basicsr.archs.srvgg_arch import SRVGGNetCompact
from gfpgan.utils import GFPGANer
from realesrgan.utils import RealESRGANer
import torch
import cv2
# ํ•„์š”ํ•œ ๋ชจ๋ธ ๋‹ค์šด๋กœ๋“œ
if not os.path.exists('realesr-general-x4v3.pth'):
os.system("wget https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.5.0/realesr-general-x4v3.pth -P .")
if not os.path.exists('GFPGANv1.4.pth'):
os.system("wget https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.4.pth -P .")
if not os.path.exists('RestoreFormer.pth'):
os.system("wget https://github.com/TencentARC/GFPGAN/releases/download/v1.3.4/RestoreFormer.pth -P .")
model = SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=32, upscale=4, act_type='prelu')
model_path = 'realesr-general-x4v3.pth'
half = True if torch.cuda.is_available() else False
upsampler = RealESRGANer(scale=4, model_path=model_path, model=model, tile=0, tile_pad=10, pre_pad=0, half=half)
def upscaler(img, version, scale):
try:
img = cv2.imread(img, cv2.IMREAD_UNCHANGED)
if len(img.shape) == 3 and img.shape[2] == 4:
img_mode = 'RGBA'
elif len(img.shape) == 2:
img_mode = None
img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
else:
img_mode = None
h, w = img.shape[0:2]
if h < 300:
img = cv2.resize(img, (w * 2, h * 2), interpolation=cv2.INTER_LANCZOS4)
face_enhancer = GFPGANer(
model_path=f'{version}.pth',
upscale=2,
arch='RestoreFormer' if version=='RestoreFormer' else 'clean',
channel_multiplier=2,
bg_upsampler=upsampler
)
try:
_, _, output = face_enhancer.enhance(img, has_aligned=False, only_center_face=False, paste_back=True)
except RuntimeError as error:
print('์˜ค๋ฅ˜ ๋ฐœ์ƒ:', error)
try:
if scale != 2:
interpolation = cv2.INTER_AREA if scale < 2 else cv2.INTER_LANCZOS4
h, w = img.shape[0:2]
output = cv2.resize(output, (int(w * scale / 2), int(h * scale / 2)), interpolation=interpolation)
except Exception as error:
print('์ž˜๋ชป๋œ ์Šค์ผ€์ผ ์ž…๋ ฅ:', error)
output = cv2.cvtColor(output, cv2.COLOR_BGR2RGB)
return output
except Exception as error:
print('์ „์ฒด ์˜ˆ์™ธ ๋ฐœ์ƒ:', error)
return None
def upscaler_korean(img):
return upscaler(img, "GFPGANv1.4", 2)
# ์ปค์Šคํ…€ CSS ์Šคํƒ€์ผ
custom_css = """
:root {
--primary-color: #5561e9;
--secondary-color: #6c8aff;
--accent-color: #ff6b6b;
--background-color: #f0f5ff;
--card-bg: #ffffff;
--text-color: #334155;
--border-radius: 18px;
--shadow: 0 8px 30px rgba(0, 0, 0, 0.08);
}
body {
font-family: 'Pretendard', 'Noto Sans KR', -apple-system, BlinkMacSystemFont, sans-serif;
background-color: var(--background-color);
color: var(--text-color);
line-height: 1.6;
}
/* Gradio ์ปจํ…Œ์ด๋„ˆ ์˜ค๋ฒ„๋ผ์ด๋“œ */
.gradio-container {
max-width: 100% !important;
margin: 0 auto !important;
padding: 0 !important;
background-color: var(--background-color) !important;
}
/* ์ƒ๋‹จ ํ—ค๋” */
.app-header {
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
color: white;
padding: 2rem;
border-radius: var(--border-radius);
margin-bottom: 1.5rem;
box-shadow: var(--shadow);
text-align: center;
}
.app-header h1 {
margin: 0;
font-size: 2.5rem;
font-weight: 700;
letter-spacing: -0.5px;
}
.app-header p {
margin: 0.75rem 0 0;
font-size: 1.1rem;
opacity: 0.9;
}
/* ํŒจ๋„ ์Šคํƒ€์ผ๋ง */
.panel {
background-color: var(--card-bg);
border-radius: var(--border-radius);
box-shadow: var(--shadow);
padding: 1.5rem;
margin-bottom: 1.5rem;
border: 1px solid rgba(0, 0, 0, 0.04);
transition: transform 0.3s ease;
}
.panel:hover {
transform: translateY(-5px);
}
/* ์„น์…˜ ์ œ๋ชฉ */
.section-title {
font-size: 1.5rem;
font-weight: 700;
color: var(--primary-color);
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid var(--secondary-color);
display: flex;
align-items: center;
}
.section-title i {
margin-right: 0.5rem;
font-size: 1.4rem;
}
/* ๋ฒ„ํŠผ ์Šคํƒ€์ผ๋ง */
.custom-button {
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
color: white !important;
border: none !important;
border-radius: calc(var(--border-radius) - 5px) !important;
padding: 0.8rem 1.2rem !important;
font-weight: 600 !important;
cursor: pointer !important;
transition: all 0.3s ease !important;
box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
text-transform: none !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
}
.custom-button:hover {
transform: translateY(-2px) !important;
box-shadow: 0 7px 14px rgba(50, 50, 93, 0.1), 0 3px 6px rgba(0, 0, 0, 0.08) !important;
}
.custom-button.primary {
background: linear-gradient(135deg, var(--accent-color), #ff9a8b) !important;
}
.custom-button i {
margin-right: 0.5rem;
}
/* ์ด๋ฏธ์ง€ ์ปจํ…Œ์ด๋„ˆ */
.image-container {
border-radius: var(--border-radius);
overflow: hidden;
border: 1px solid rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
background-color: white;
}
.image-container:hover {
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
}
/* ํƒญ ์ปจํ…Œ์ด๋„ˆ */
.custom-tabs {
background-color: transparent !important;
border: none !important;
margin-bottom: 1rem;
}
.custom-tabs button {
background-color: rgba(255, 255, 255, 0.7) !important;
border: none !important;
border-radius: var(--border-radius) var(--border-radius) 0 0 !important;
padding: 0.8rem 1.5rem !important;
font-weight: 600 !important;
color: var(--text-color) !important;
transition: all 0.3s ease !important;
}
.custom-tabs button[aria-selected="true"] {
background-color: var(--primary-color) !important;
color: white !important;
}
/* ์ž…๋ ฅ ํ•„๋“œ */
.custom-input {
border-radius: calc(var(--border-radius) - 5px) !important;
border: 1px solid rgba(0, 0, 0, 0.1) !important;
padding: 0.8rem 1rem !important;
transition: all 0.3s ease !important;
}
.custom-input:focus {
border-color: var(--primary-color) !important;
box-shadow: 0 0 0 2px rgba(85, 97, 233, 0.2) !important;
}
/* ์‚ฌ์šฉ์ž ๋งค๋‰ด์–ผ */
.user-manual {
background-color: white;
padding: 2rem;
border-radius: var(--border-radius);
box-shadow: var(--shadow);
margin-top: 2rem;
}
.manual-title {
font-size: 1.8rem;
font-weight: 700;
color: var(--primary-color);
margin-bottom: 1.5rem;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
}
.manual-title i {
margin-right: 0.5rem;
font-size: 1.8rem;
}
.manual-section {
margin-bottom: 1.5rem;
padding: 1.2rem;
background-color: #f8faff;
border-radius: calc(var(--border-radius) - 5px);
}
.manual-section-title {
font-size: 1.3rem;
font-weight: 700;
margin-bottom: 1rem;
color: var(--primary-color);
display: flex;
align-items: center;
}
.manual-section-title i {
margin-right: 0.5rem;
font-size: 1.2rem;
}
.manual-text {
font-size: 1rem;
line-height: 1.7;
}
.manual-text strong {
color: var(--accent-color);
}
.tip-box {
background-color: rgba(255, 107, 107, 0.1);
border-left: 3px solid var(--accent-color);
padding: 1rem 1.2rem;
margin: 1rem 0;
border-radius: 8px;
}
/* ๋ฒ„ํŠผ ๊ทธ๋ฃน */
.button-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 0.8rem;
margin-bottom: 1.2rem;
}
/* ๋กœ๋”ฉ ์• ๋‹ˆ๋ฉ”์ด์…˜ */
.progress-container {
background-color: rgba(255, 255, 255, 0.9);
border-radius: var(--border-radius);
padding: 2rem;
box-shadow: var(--shadow);
text-align: center;
}
.progress-bar {
height: 8px;
border-radius: 4px;
background: linear-gradient(90deg, var(--primary-color), var(--secondary-color));
margin: 1rem 0;
}
/* ์˜ˆ์‹œ ๊ทธ๋ฆฌ๋“œ */
.examples-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
margin: 1.5rem 0;
}
.example-card {
background: white;
border-radius: var(--border-radius);
overflow: hidden;
box-shadow: var(--shadow);
transition: transform 0.3s ease;
}
.example-card:hover {
transform: translateY(-5px);
}
/* ๋ฐ˜์‘ํ˜• */
@media (max-width: 768px) {
.button-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* ์ „์ฒด ์ธํ„ฐํŽ˜์ด์Šค ๋‘ฅ๊ทผ ๋ชจ์„œ๋ฆฌ */
.block, .prose, .gr-prose, .gr-form, .gr-panel {
border-radius: var(--border-radius) !important;
}
/* ๋ฉ”์ธ ์ปจํ…์ธ  ์Šคํฌ๋กค๋ฐ” */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.05);
border-radius: 10px;
}
::-webkit-scrollbar-thumb {
background: var(--secondary-color);
border-radius: 10px;
}
"""
# FontAwesome ์•„์ด์ฝ˜ ํฌํ•จ
fontawesome_link = """
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" crossorigin="anonymous" referrerpolicy="no-referrer" />
"""
# ์•ฑ ํ—ค๋” HTML
header_html = """
<div class="app-header">
<h1>โœจ ์ด์ปค๋จธ์Šค ์ „์šฉ ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ(Ver1.1) โœจ</h1>
<p>์‰ฝ๊ณ  ๋น ๋ฅด๊ฒŒ ์ƒํ’ˆ ์ด๋ฏธ์ง€๋ฅผ ํŽธ์ง‘ํ•˜๊ณ  ์ƒ์„ฑํ•˜์„ธ์š”! ํ•œ ๋ฒˆ์˜ ํด๋ฆญ์œผ๋กœ ์ „๋ฌธ์ ์ธ ์ด๋ฏธ์ง€๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.</p>
</div>
"""
# ์‚ฌ์šฉ์ž ๋งค๋‰ด์–ผ HTML
user_manual_html = """
<div class="user-manual">
<div class="manual-title"><i class="fas fa-book"></i> ์‚ฌ์šฉ ์„ค๋ช…์„œ</div>
<div class="manual-section">
<div class="manual-section-title"><i class="fas fa-magic"></i> ์ด์ปค๋จธ์Šค ์ „์šฉ ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ</div>
<p class="manual-text">
<span style="font-size: 1.1rem; font-weight: 600; color: #5561e9;">1๏ธโƒฃ ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ</span><br>
์ตœ๋Œ€ 3๊ฐœ์˜ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.<br><br>
<span style="font-size: 1.1rem; font-weight: 600; color: #5561e9;">2๏ธโƒฃ ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ ๋˜๋Š” ๋ฒ„ํŠผ ์„ ํƒ</span><br>
๋ณ€ํ™˜ํ•˜๊ณ  ์‹ถ์€ ๋‚ด์šฉ์„ ์ง์ ‘ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜, ๋ฏธ๋ฆฌ ์ •์˜๋œ ๋ฒ„ํŠผ ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•˜์„ธ์š”.<br><br>
<span style="font-size: 1.1rem; font-weight: 600; color: #5561e9;">3๏ธโƒฃ ์ด๋ฏธ์ง€ ์ƒ์„ฑ</span><br>
'์ด๋ฏธ์ง€ ์ƒ์„ฑ' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด 4๊ฐ€์ง€ ๋ฒ„์ „์˜ ์ด๋ฏธ์ง€๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
</p>
</div>
<div class="manual-section">
<div class="manual-section-title"><i class="fas fa-hashtag"></i> ์ด๋ฏธ์ง€ ์ฐธ์กฐ ๋ฐฉ๋ฒ•</div>
<p class="manual-text">
ํ”„๋กฌํ”„ํŠธ์—์„œ ๋‹ค์Œ ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ ์ด๋ฏธ์ง€๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:
<ul>
<li><strong style="font-size: 1.05rem;">#1</strong> - ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€</li>
<li><strong style="font-size: 1.05rem;">#2</strong> - ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€</li>
<li><strong style="font-size: 1.05rem;">#3</strong> - ์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€</li>
</ul>
<div class="tip-box">
<i class="fas fa-lightbulb"></i> <strong>ํŒ:</strong> ์˜ˆ๋ฅผ ๋“ค์–ด, "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด (#2์˜ ์„ ๊ธ€๋ผ์Šค)๋ฅผ ์ฐฉ์šฉํ•œ ๋ชจ์Šต"๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
</div>
</p>
</div>
<div class="manual-section">
<div class="manual-section-title"><i class="fas fa-tools"></i> ์ฃผ์š” ๊ธฐ๋Šฅ</div>
<div class="manual-text">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1rem;">
<div>
<p style="font-size: 1.1rem; font-weight: 600; color: #ff6b6b;"><i class="fas fa-exchange-alt"></i> ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ</p>
ํฌ์ฆˆ๋‚˜ ์Šคํƒ€์ผ์„ ๋ณ€๊ฒฝํ•˜๋ฉด์„œ ์›๋ณธ์˜ ์ฃผ์š” ์š”์†Œ๋Š” ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.
</div>
<div>
<p style="font-size: 1.1rem; font-weight: 600; color: #ff6b6b;"><i class="fas fa-eraser"></i> ๊ธ€์ž ์ง€์šฐ๊ธฐ/๋ณ€๊ฒฝ</p>
์ด๋ฏธ์ง€์˜ ํ…์ŠคํŠธ๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ œ๊ฑฐํ•˜๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ํ…์ŠคํŠธ๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.
</div>
<div>
<p style="font-size: 1.1rem; font-weight: 600; color: #ff6b6b;"><i class="fas fa-tshirt"></i> ๊ฐ€์ƒ ์ƒํ’ˆ์ฐฉ์šฉ</p>
๋ชจ๋ธ์—๊ฒŒ ๋‹ค๋ฅธ ์ด๋ฏธ์ง€์˜ ์ƒํ’ˆ์„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ฐฉ์šฉ์‹œํ‚ต๋‹ˆ๋‹ค.
</div>
<div>
<p style="font-size: 1.1rem; font-weight: 600; color: #ff6b6b;"><i class="fas fa-image"></i> ๋ฐฐ๊ฒฝ ๋ฐ”๊พธ๊ธฐ</p>
์ฃผ์š” ํ”ผ์‚ฌ์ฒด๋Š” ์œ ์ง€ํ•˜๋ฉฐ ๋ฐฐ๊ฒฝ๋งŒ ๊ต์ฒดํ•ฉ๋‹ˆ๋‹ค.
</div>
</div>
</div>
</div>
<div class="manual-section">
<div class="manual-section-title"><i class="fas fa-search-plus"></i> ์ด๋ฏธ์ง€ ์—…์Šค์ผ€์ผ๋Ÿฌ</div>
<p class="manual-text">
<span style="font-size: 1.1rem; font-weight: 600; color: #5561e9;">์ด๋ฏธ์ง€ ๊ฐœ์„  ๊ธฐ๋Šฅ</span><br>
์ €ํ•ด์ƒ๋„ ์ด๋ฏธ์ง€๋ฅผ ๊ณ ํ•ด์ƒ๋„๋กœ ๋ณ€ํ™˜ํ•˜๊ณ , ์–ผ๊ตด์„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ณต์›ํ•ฉ๋‹ˆ๋‹ค.<br>
ํŠนํžˆ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฝ์šฐ์— ํšจ๊ณผ์ ์ž…๋‹ˆ๋‹ค:
<ul>
<li>ํ•ด์ƒ๋„๊ฐ€ ๋‚ฎ์€ ์ด๋ฏธ์ง€ ๊ฐœ์„ </li>
<li>ํ๋ฆฟํ•˜๊ฑฐ๋‚˜ ๋…ธ์ด์ฆˆ๊ฐ€ ์žˆ๋Š” ์–ผ๊ตด ๋ณต์›</li>
<li>์ด๋ฏธ์ง€ ์ „๋ฐ˜์ ์ธ ์„ ๋ช…๋„ ํ–ฅ์ƒ</li>
</ul>
<div class="tip-box">
<i class="fas fa-lightbulb"></i> <strong>ํŒ:</strong> ์ด๋ฏธ์ง€์˜ ํฌ๊ธฐ๊ฐ€ ์ž‘์„์ˆ˜๋ก ์ฒ˜๋ฆฌ ์†๋„๊ฐ€ ๋น ๋ฆ…๋‹ˆ๋‹ค. ์–ผ๊ตด์ด ํฌํ•จ๋œ ์ด๋ฏธ์ง€์—์„œ ๊ฐ€์žฅ ์ข‹์€ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
</div>
</p>
</div>
</div>
"""
# UI ๊ตฌ์„ฑ
with gr.Blocks(css=custom_css) as demo:
gr.HTML(fontawesome_link)
gr.HTML(header_html)
with gr.Tabs(elem_classes="custom-tabs") as tabs:
# ์‚ฌ์šฉ ์„ค๋ช…์„œ ํƒญ์„ ์ฒซ ๋ฒˆ์งธ๋กœ ๋ฐฐ์น˜
with gr.TabItem("๐Ÿ“š ์‚ฌ์šฉ ์„ค๋ช…์„œ", elem_classes="tab-content"):
gr.HTML("""
<div class="user-manual">
<div class="manual-title"><i class="fas fa-book"></i> ์‚ฌ์šฉ ์„ค๋ช…์„œ</div>
<div class="manual-section">
<div class="manual-section-title"><i class="fas fa-magic"></i> ์ด์ปค๋จธ์Šค์ „์šฉ ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ</div>
<p class="manual-text">
<span style="font-size: 1.1rem; font-weight: 600; color: #5561e9;">1๏ธโƒฃ ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ</span><br>
์ตœ๋Œ€ 3๊ฐœ์˜ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.<br><br>
<span style="font-size: 1.1rem; font-weight: 600; color: #5561e9;">2๏ธโƒฃ ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ ๋˜๋Š” ๋ฒ„ํŠผ ์„ ํƒ</span><br>
๋ณ€ํ™˜ํ•˜๊ณ  ์‹ถ์€ ๋‚ด์šฉ์„ ์ง์ ‘ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜, ๋ฏธ๋ฆฌ ์ •์˜๋œ ๋ฒ„ํŠผ ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•˜์„ธ์š”.<br><br>
<span style="font-size: 1.1rem; font-weight: 600; color: #5561e9;">3๏ธโƒฃ ์ด๋ฏธ์ง€ ์ƒ์„ฑ</span><br>
'์ด๋ฏธ์ง€ ์ƒ์„ฑ (1์žฅ)' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด 1๊ฐœ์˜ ์ด๋ฏธ์ง€๊ฐ€, '์ด๋ฏธ์ง€ ์ƒ์„ฑ (4์žฅ)' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด 4๊ฐ€์ง€ ๋ฒ„์ „์˜ ์ด๋ฏธ์ง€๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
</p>
</div>
<div class="manual-section">
<div class="manual-section-title"><i class="fas fa-hashtag"></i> ์ด๋ฏธ์ง€ ์ฐธ์กฐ ๋ฐฉ๋ฒ•</div>
<p class="manual-text">
ํ”„๋กฌํ”„ํŠธ์—์„œ ๋‹ค์Œ ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ ์ด๋ฏธ์ง€๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:
<ul>
<li><strong style="font-size: 1.05rem;">#1</strong> - ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€</li>
<li><strong style="font-size: 1.05rem;">#2</strong> - ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€</li>
<li><strong style="font-size: 1.05rem;">#3</strong> - ์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€</li>
</ul>
<div class="tip-box">
<i class="fas fa-lightbulb"></i> <strong>ํŒ:</strong> ์˜ˆ๋ฅผ ๋“ค์–ด, "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด (#2์˜ ์„ ๊ธ€๋ผ์Šค)๋ฅผ ์ฐฉ์šฉํ•œ ๋ชจ์Šต"๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
</div>
</p>
</div>
<div class="manual-section">
<div class="manual-section-title"><i class="fas fa-tools"></i> ์ฃผ์š” ๊ธฐ๋Šฅ</div>
<div class="manual-text">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1rem;">
<div>
<p style="font-size: 1.1rem; font-weight: 600; color: #ff6b6b;"><i class="fas fa-exchange-alt"></i> ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ</p>
ํฌ์ฆˆ๋‚˜ ์Šคํƒ€์ผ์„ ๋ณ€๊ฒฝํ•˜๋ฉด์„œ ์›๋ณธ์˜ ์ฃผ์š” ์š”์†Œ๋Š” ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.
</div>
<div>
<p style="font-size: 1.1rem; font-weight: 600; color: #ff6b6b;"><i class="fas fa-eraser"></i> ๊ธ€์ž ์ง€์šฐ๊ธฐ/๋ณ€๊ฒฝ</p>
์ด๋ฏธ์ง€์˜ ํ…์ŠคํŠธ๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ œ๊ฑฐํ•˜๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ํ…์ŠคํŠธ๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.
</div>
<div>
<p style="font-size: 1.1rem; font-weight: 600; color: #ff6b6b;"><i class="fas fa-tshirt"></i> ๊ฐ€์ƒ ์ƒํ’ˆ์ฐฉ์šฉ</p>
๋ชจ๋ธ์—๊ฒŒ ๋‹ค๋ฅธ ์ด๋ฏธ์ง€์˜ ์ƒํ’ˆ์„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ฐฉ์šฉ์‹œํ‚ต๋‹ˆ๋‹ค.
</div>
<div>
<p style="font-size: 1.1rem; font-weight: 600; color: #ff6b6b;"><i class="fas fa-image"></i> ๋ฐฐ๊ฒฝ ๋ฐ”๊พธ๊ธฐ</p>
์ฃผ์š” ํ”ผ์‚ฌ์ฒด๋Š” ์œ ์ง€ํ•˜๋ฉฐ ๋ฐฐ๊ฒฝ๋งŒ ๊ต์ฒดํ•ฉ๋‹ˆ๋‹ค.
</div>
<div>
<p style="font-size: 1.1rem; font-weight: 600; color: #ff6b6b;"><i class="fas fa-cut"></i> ๋ถ€๋ถ„ ์ง€์šฐ๊ธฐ</p>
์ด๋ฏธ์ง€์˜ ํŠน์ • ๋ถ€๋ถ„์„ ์ง€์šฐ๊ณ  ๋ฐฐ๊ฒฝ๊ณผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์กฐํ™”๋˜๋„๋ก ์ฑ„์›๋‹ˆ๋‹ค.
</div>
<div>
<p style="font-size: 1.1rem; font-weight: 600; color: #ff6b6b;"><i class="fas fa-wine-glass"></i> ์ƒํ’ˆ๋“ค๊ณ  ์žˆ๊ธฐ</p>
๋ชจ๋ธ์ด ์ƒํ’ˆ์„ ๋“ค๊ณ  ์žˆ๋Š” ์ž์—ฐ์Šค๋Ÿฌ์šด ํฌ์ฆˆ์˜ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
</div>
<div>
<p style="font-size: 1.1rem; font-weight: 600; color: #ff6b6b;"><i class="fas fa-expand-alt"></i> ์ด๋ฏธ์ง€ ํ™•์žฅ</p>
์›๋ณธ ์ด๋ฏธ์ง€ ์ฃผ๋ณ€์— ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ถ”๊ฐ€ ์˜์—ญ์„ ์ƒ์„ฑํ•˜์—ฌ ์ด๋ฏธ์ง€๋ฅผ ํ™•์žฅํ•ฉ๋‹ˆ๋‹ค.
</div>
</div>
</div>
</div>
<div class="manual-section">
<div class="manual-section-title"><i class="fas fa-search-plus"></i> ์ด๋ฏธ์ง€ ์—…์Šค์ผ€์ผ๋Ÿฌ</div>
<p class="manual-text">
<span style="font-size: 1.1rem; font-weight: 600; color: #5561e9;">์ด๋ฏธ์ง€ ๊ฐœ์„  ๊ธฐ๋Šฅ</span><br>
์ €ํ•ด์ƒ๋„ ์ด๋ฏธ์ง€๋ฅผ ๊ณ ํ•ด์ƒ๋„๋กœ ๋ณ€ํ™˜ํ•˜๊ณ , ์–ผ๊ตด์„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ณต์›ํ•ฉ๋‹ˆ๋‹ค.<br>
ํŠนํžˆ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฝ์šฐ์— ํšจ๊ณผ์ ์ž…๋‹ˆ๋‹ค:
<ul>
<li>ํ•ด์ƒ๋„๊ฐ€ ๋‚ฎ์€ ์ด๋ฏธ์ง€ ๊ฐœ์„ </li>
<li>ํ๋ฆฟํ•˜๊ฑฐ๋‚˜ ๋…ธ์ด์ฆˆ๊ฐ€ ์žˆ๋Š” ์–ผ๊ตด ๋ณต์›</li>
<li>์ด๋ฏธ์ง€ ์ „๋ฐ˜์ ์ธ ์„ ๋ช…๋„ ํ–ฅ์ƒ</li>
</ul>
<div class="tip-box">
<i class="fas fa-lightbulb"></i> <strong>ํŒ:</strong> ์ด๋ฏธ์ง€์˜ ํฌ๊ธฐ๊ฐ€ ์ž‘์„์ˆ˜๋ก ์ฒ˜๋ฆฌ ์†๋„๊ฐ€ ๋น ๋ฆ…๋‹ˆ๋‹ค. ์–ผ๊ตด์ด ํฌํ•จ๋œ ์ด๋ฏธ์ง€์—์„œ ๊ฐ€์žฅ ์ข‹์€ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
</div>
</p>
</div>
<div class="manual-section">
<div class="manual-section-title"><i class="fas fa-cut"></i> ๋ฐฐ๊ฒฝ ์ง€์šฐ๊ธฐ ๊ธฐ๋Šฅ</div>
<p class="manual-text">
<span style="font-size: 1.1rem; font-weight: 600; color: #5561e9;">์ž๋™ ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ</span><br>
์ด๋ฏธ์ง€์—์„œ ๋ฐฐ๊ฒฝ์„ ์ž๋™์œผ๋กœ ๊ฐ์ง€ํ•˜๊ณ  ์ œ๊ฑฐํ•˜์—ฌ ํˆฌ๋ช…ํ•œ PNG ์ด๋ฏธ์ง€๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.<br>
์ฃผ์š” ์‚ฌ์šฉ ์‚ฌ๋ก€:
<ul>
<li>์ œํ’ˆ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ</li>
<li>์ธ๋ฌผ ์‚ฌ์ง„์—์„œ ๋ฐฐ๊ฒฝ ๋ถ„๋ฆฌ</li>
<li>์—ฌ๋Ÿฌ ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ์„ ์œ„ํ•œ ์ค€๋น„</li>
</ul>
<div class="tip-box">
<i class="fas fa-lightbulb"></i> <strong>ํŒ:</strong> ๋ฐฐ๊ฒฝ๊ณผ ์ฃผ์š” ํ”ผ์‚ฌ์ฒด ๊ฐ„์˜ ๋Œ€๋น„๊ฐ€ ๋šœ๋ ทํ• ์ˆ˜๋ก ๋” ์ •ํ™•ํ•œ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณต์žกํ•œ ๋ฐฐ๊ฒฝ์˜ ๊ฒฝ์šฐ ๋ฏธ์„ธ ์กฐ์ •์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
</div>
</p>
</div>
<div class="manual-section">
<div class="manual-section-title"><i class="fas fa-sliders-h"></i> ์ด๋ฏธ์ง€ ํ•„ํ„ฐ ๊ธฐ๋Šฅ</div>
<p class="manual-text">
<span style="font-size: 1.1rem; font-weight: 600; color: #5561e9;">์ด๋ฏธ์ง€ ํŽธ์ง‘ ๋„๊ตฌ</span><br>
์ด๋ฏธ์ง€ ํ•„ํ„ฐ ํƒญ์—์„œ๋Š” ๋‹ค์–‘ํ•œ ์Šฌ๋ผ์ด๋”๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฏธ์ง€๋ฅผ ์‰ฝ๊ฒŒ ํŽธ์ง‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.<br>
์ฃผ์š” ๊ธฐ๋Šฅ:
<ul>
<li>๋ฐ๊ธฐ, ๋Œ€๋น„, ์ฑ„๋„, ์ƒ‰์˜จ๋„ ๋“ฑ ๋‹ค์–‘ํ•œ ์กฐ์ ˆ ์˜ต์…˜</li>
<li>๋ชจ๋“  ๋ณ€๊ฒฝ์‚ฌํ•ญ ์‹ค์‹œ๊ฐ„ ๋ฏธ๋ฆฌ๋ณด๊ธฐ</li>
<li>ํŽธ์ง‘ ์™„๋ฃŒ ํ›„ JPG ํ˜•์‹์œผ๋กœ ์ €์žฅ ๊ฐ€๋Šฅ</li>
</ul>
<div class="tip-box">
<i class="fas fa-lightbulb"></i> <strong>ํŒ:</strong> ๊ฐ ์Šฌ๋ผ์ด๋”๋ฅผ ์กฐ๊ธˆ์”ฉ ์กฐ์ ˆํ•˜๋ฉฐ ์›ํ•˜๋Š” ํšจ๊ณผ๋ฅผ ์ฐพ์•„๋ณด์„ธ์š”.
</div>
</p>
</div>
<div class="manual-section">
<div class="manual-section-title"><i class="fas fa-question-circle"></i> ์ž์ฃผ ๋ฌป๋Š” ์งˆ๋ฌธ</div>
<p class="manual-text">
<strong style="font-size: 1.1rem; color: #5561e9;">Q: ์–ด๋–ค ํ˜•์‹์˜ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋‚˜์š”?</strong><br>
A: JPG, PNG, WEBP ๋“ฑ ๋Œ€๋ถ€๋ถ„์˜ ์ผ๋ฐ˜์ ์ธ ์ด๋ฏธ์ง€ ํ˜•์‹์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.<br><br>
<strong style="font-size: 1.1rem; color: #5561e9;">Q: ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฐ ์ฒ˜๋ฆฌ์— ์–ผ๋งˆ๋‚˜ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฌ๋‚˜์š”?</strong><br>
A: ์ผ๋ฐ˜์ ์œผ๋กœ 10-30์ดˆ ์ •๋„ ์†Œ์š”๋˜๋ฉฐ, ์ด๋ฏธ์ง€ ํฌ๊ธฐ์™€ ๋ณต์žก๋„์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.<br><br>
<strong style="font-size: 1.1rem; color: #5561e9;">Q: ์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€๋ฅผ ์–ด๋–ป๊ฒŒ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‚˜์š”?</strong><br>
A: ์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€์— ๋งˆ์šฐ์Šค ์˜ค๋ฅธ์ชฝ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ '์ด๋ฏธ์ง€ ์ €์žฅ' ์˜ต์…˜์„ ์„ ํƒํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
</p>
</div>
</div>
""")
with gr.TabItem("โœจ ์ด์ปค๋จธ์Šค ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ", elem_classes="tab-content"):
with gr.Row(equal_height=True):
with gr.Column(scale=1):
with gr.Group(elem_classes="panel"):
gr.HTML('<div class="section-title"><i class="fas fa-upload"></i> ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ</div>')
with gr.Row():
image1_input = gr.Image(type="pil", label="#1", image_mode="RGB", elem_classes="image-container", height=400)
image2_input = gr.Image(type="pil", label="#2", image_mode="RGB", elem_classes="image-container", height=400)
image3_input = gr.Image(type="pil", label="#3", image_mode="RGB", elem_classes="image-container", height=400)
gr.HTML('<div class="section-title"><i class="fas fa-keyboard"></i> ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ</div>')
prompt_input = gr.Textbox(
lines=3,
placeholder="ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜ ๋น„์›Œ๋‘๋ฉด ์ž๋™ ํ•ฉ์„ฑ๋ฉ๋‹ˆ๋‹ค. '#1', '#2', '#3'์œผ๋กœ ๊ฐ ์ด๋ฏธ์ง€๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
label="ํ”„๋กฌํ”„ํŠธ (์„ ํƒ ์‚ฌํ•ญ)",
elem_classes="custom-input"
)
gr.HTML('<div class="section-title"><i class="fas fa-sliders-h"></i> ๋ณ€ํ™˜ ์˜ต์…˜</div>')
with gr.Column(elem_classes="button-grid"):
image_change_btn1 = gr.Button('๐Ÿ”„ ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ-1', elem_classes="custom-button")
image_change_btn2 = gr.Button('๐Ÿ”„ ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ-2', elem_classes="custom-button")
text_remove_btn = gr.Button('๐Ÿงน ๊ธ€์ž ์ง€์šฐ๊ธฐ', elem_classes="custom-button")
text_change_btn = gr.Button('๐Ÿ”ค ๊ธ€์ž ๋ณ€๊ฒฝ', elem_classes="custom-button")
clothes_change_btn1 = gr.Button('๐Ÿ‘• ์ƒํ’ˆ์ฐฉ์šฉ-1', elem_classes="custom-button")
clothes_change_btn2 = gr.Button('๐Ÿ‘“ ์ƒํ’ˆ์ฐฉ์šฉ-2', elem_classes="custom-button")
holding_product_btn = gr.Button('๐Ÿท ์ƒํ’ˆ๋“ค๊ณ  ์žˆ๊ธฐ', elem_classes="custom-button")
background_change_btn = gr.Button('๐Ÿ–ผ๏ธ ๋ฐฐ๊ฒฝ ๋ฐ”๊พธ๊ธฐ', elem_classes="custom-button")
composite_product_btn = gr.Button('โœ‚๏ธ ๋ถ€๋ถ„ ์ง€์šฐ๊ธฐ', elem_classes="custom-button")
outpainting_btn = gr.Button('๐Ÿ” ์ด๋ฏธ์ง€ ํ™•์žฅ', elem_classes="custom-button")
submit_single_btn = gr.Button('โœจ ์ด๋ฏธ์ง€ ์ƒ์„ฑ (1์žฅ)', elem_classes="custom-button primary")
submit_btn = gr.Button('โœจ ์ด๋ฏธ์ง€ ์ƒ์„ฑ (4์žฅ)', elem_classes="custom-button primary")
with gr.Column(scale=1):
with gr.Group(elem_classes="panel"):
gr.HTML('<div class="section-title"><i class="fas fa-images"></i> ์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€</div>')
with gr.Row():
with gr.Column():
output_image1 = gr.Image(label="์ด๋ฏธ์ง€ #1", elem_classes="image-container", height=400, type="filepath")
output_image3 = gr.Image(label="์ด๋ฏธ์ง€ #3", elem_classes="image-container", height=400, type="filepath")
with gr.Column():
output_image2 = gr.Image(label="์ด๋ฏธ์ง€ #2", elem_classes="image-container", height=400, type="filepath")
output_image4 = gr.Image(label="์ด๋ฏธ์ง€ #4", elem_classes="image-container", height=400, type="filepath")
gr.HTML('<div class="section-title"><i class="fas fa-info-circle"></i> ๊ฒฐ๊ณผ ์ •๋ณด</div>')
output_text = gr.Textbox(label="์ƒํƒœ ๋ฉ”์‹œ์ง€", lines=2, elem_classes="custom-input")
prompt_display = gr.Textbox(label="์‚ฌ์šฉ๋œ ํ”„๋กฌํ”„ํŠธ (์˜์–ด)", visible=True, lines=2, elem_classes="custom-input")
gr.HTML('<div class="section-title"><i class="fas fa-lightbulb"></i> ์˜ˆ์ œ ์ด๋ฏธ์ง€</div>')
with gr.Group(elem_classes="panel"):
examples = [
["down/๋ชจ๋ธ.jpg", None, None, "(#1์˜ ์—ฌ์„ฑ)์ด ์‚ด์ง ๋’ค๋กœ ๋Œ์•„๋ณด๋Š” ๋ชจ์Šต์œผ๋กœ ์ตœ๋Œ€ํ•œ ์ด์ „ seed๋ฅผ ์œ ์ง€ํ•œ์ฒด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ณ€๊ฒฝํ•˜๋ผ."],
["down/์ƒ์–ด๋ ˆ๊ณ ๋ชจํ˜•.png", None, None, "(#1 ๋ ˆ๋ชจ๋ชจํ˜•)์—์„œ ์ฒญ์ƒ‰์ƒ์–ด๋ ˆ๊ณ ๋งŒ ๊ฒ€์€์ƒ‰ ๊ณ ๋ž˜๋ ˆ๊ณ ๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์€ seed๋ฅผ ๋ณ€๊ฒฝ์„ ํ•˜์ง€๋งˆ๋ผ."],
["down/์ค‘๊ตญ์–ด.png", None, None, "(#1 ์ด๋ฏธ์ง€)์— ์žˆ๋Š” ์ค‘๊ตญ์–ด๋ฅผ ๋ชจ๋‘ ์ œ๊ฑฐํ•˜๋ผ."],
["down/ํ…์ŠคํŠธ.webp", None, None, '(#1์˜ ํ…์ŠคํŠธ)๋ฅผ ์Šคํƒ€์ผ์„ ์œ ์ง€ํ•œ์ฒด ํ…์ŠคํŠธ๋งŒ "Hello"๋กœ ๋ฐ”๊ฟ”๋ผ'],
["down/๋ชจ๋ธ.jpg", "down/์„ ๊ธ€๋ผ์Šค.png", "down/์ฒญ๋ฐ”์ง€.png", "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด ์‹ ์ฒด ๋น„์œจ๊ณผ ํฌ์ฆˆ๋Š” ์œ ์น˜ํ•œ ์ฒด (#2์˜ ์„ ๊ธ€๋ผ์Šค)์™€ (#3์˜ ์ฒญ๋ฐ”์ง€)๋ฅผ ์ง์ ‘ ๋ชจ๋ธ์ด ์ฐฉ์šฉํ•œ๊ฒƒ์ฒ˜๋Ÿผ ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ชจ์Šต"],
["down/๋ชจ๋ธ.jpg", "down/์„ ๊ธ€๋ผ์Šค.png", "down/์นดํŽ˜์ „๊ฒฝ.png", "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด (#2์˜ ์„ ๊ธ€๋ผ์Šค)์„ ์ฐฉ์šฉํ•˜๊ณ  (#3์˜ ๋’ท๋ฐฐ๊ฒฝ์˜ ์นดํŽ˜์ „์ฒด๊ฐ€ ๋ณด์ด๋ฉฐ) ์˜์ž์— ์•‰์•„ ์žˆ๋Š” ๋ชจ์Šต."],
["down/๋ชจ๋ธ.jpg", "down/์™€์ธ์ž”.png", None, "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด(#2์˜ ์™€์ธ์ž”)์„ ๋“ค๊ณ  ์žˆ๋Š” ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ชจ์Šต"],
["down/๋ชจ๋ธ.jpg", "down/์นดํŽ˜์ „๊ฒฝ.png", None, "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด (#2 ์นดํŽ˜)์—์„œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์žˆ๋Š” ๋ชจ์Šต"],
["down/์ƒ์–ด๋ ˆ๊ณ ๋ชจํ˜•.png", None, None, "(#1์˜ ๋ ˆ๊ณ ๋ชจํ˜•)์—์„œ ์ฒญ์ƒ‰์ƒ์–ด๋ ˆ๊ณ ๋ฅผ ์ œ๊ฑฐํ•œ ํ›„, ๊ทธ ์ž๋ฆฌ๋ฅผ ์ฃผ๋ณ€ ๋ฐฐ๊ฒฝ๊ณผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์–ด์šฐ๋Ÿฌ์ง€๋„๋ก ์ฑ„์›Œ์ฃผ์„ธ์š”. ๋‹จ, ์ด๋ฏธ์ง€์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„์˜ ์ฃผ์š” ์š”์†Œ๋Š” ๋™์ผํ•˜๊ฒŒ ์œ ์ง€ํ•ด ํ•ด์•ผํ•œ๋‹ค."],
["down/์นดํŽ˜์ „๊ฒฝ.png", None, None, "(#1 ์ด๋ฏธ์ง€)๋ฅผ ์›๋ณธ๊ทธ๋Œ€๋กœ ์ค‘์•™์— ๋‘๊ณ  ๋น„์œจ๋กœ ์œ ์ง€ํ•œ ์ฒด ์œ„์•„๋ž˜ ๋ฐ ์ขŒ์šฐ๋กœ ํฌ๊ฒŒ ํ™•์žฅํ•˜๋ผ."]
]
gr.Examples(
examples=examples,
inputs=[image1_input, image2_input, image3_input, prompt_input]
)
with gr.TabItem("๐Ÿ” ์ด๋ฏธ์ง€ ์—…์Šค์ผ€์ผ๋Ÿฌ", elem_classes="tab-content"):
with gr.Row():
with gr.Column(elem_classes="panel"):
gr.HTML('<div class="section-title"><i class="fas fa-search-plus"></i> ์ด๋ฏธ์ง€ ์—…์Šค์ผ€์ผ & ๋ณต์›</div>')
gr.HTML("""
<p style="margin-bottom: 1rem; font-size: 1.05rem;">
์ด๋ฏธ์ง€๋ฅผ ์—…์Šค์ผ€์ผ๋Ÿฌํ•ฉ๋‹ˆ๋‹ค.
</p>
<div class="tip-box">
<i class="fas fa-lightbulb"></i> <strong>ํŒ:</strong> ํ•ด์ƒ๋„๊ฐ€ ๋‚ฎ๊ฑฐ๋‚˜ ํ’ˆ์งˆ์ด ๋–จ์–ด์ง€๋Š” ์ด๋ฏธ์ง€, ํŠนํžˆ ์–ผ๊ตด ์ด๋ฏธ์ง€์— ํšจ๊ณผ์ ์ž…๋‹ˆ๋‹ค.
</div>
""")
with gr.Row():
with gr.Column():
gr.HTML('<div class="section-title"><i class="fas fa-upload"></i> ์ž…๋ ฅ ์ด๋ฏธ์ง€</div>')
gfpgan_input = gr.Image(type="filepath", label="์—…๋กœ๋“œ", elem_classes="image-container")
with gr.Column():
gr.HTML('<div class="section-title"><i class="fas fa-download"></i> ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€</div>')
gfpgan_output = gr.Image(type="numpy", label="์—…์Šค์ผ€์ผ ๊ฒฐ๊ณผ", elem_classes="image-container")
gfpgan_btn = gr.Button('โœจ ์—…์Šค์ผ€์ผ ๋ฐ ๋ณต์›', elem_classes="custom-button primary")
# ๋ฐฐ๊ฒฝ ์ง€์šฐ๊ธฐ ํƒญ
with gr.TabItem("โœ‚๏ธ ๋ฐฐ๊ฒฝ ์ง€์šฐ๊ธฐ", elem_classes="tab-content"):
with gr.Row():
with gr.Column(elem_classes="panel"):
gr.HTML('<div class="section-title"><i class="fas fa-cut"></i> ์ด๋ฏธ์ง€ ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ</div>')
gr.HTML("""
<p style="margin-bottom: 1rem; font-size: 1.05rem;">
์ด๋ฏธ์ง€์—์„œ ๋ฐฐ๊ฒฝ์„ ์ž๋™์œผ๋กœ ์ œ๊ฑฐํ•˜์—ฌ ํˆฌ๋ช…ํ•œ PNG ์ด๋ฏธ์ง€๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
</p>
<div class="tip-box">
<i class="fas fa-lightbulb"></i> <strong>ํŒ:</strong> ๋ฐฐ๊ฒฝ๊ณผ ์ฃผ์š” ํ”ผ์‚ฌ์ฒด ๊ฐ„์˜ ๋Œ€๋น„๊ฐ€ ๋šœ๋ ทํ• ์ˆ˜๋ก ๋” ๋‚˜์€ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
</div>
""")
with gr.Row():
with gr.Column():
gr.HTML('<div class="section-title"><i class="fas fa-upload"></i> ์ž…๋ ฅ ์ด๋ฏธ์ง€</div>')
bg_remove_input = gr.Image(type="pil", label="์—…๋กœ๋“œ", elem_classes="image-container")
with gr.Column():
gr.HTML('<div class="section-title"><i class="fas fa-download"></i> ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€</div>')
bg_remove_output = gr.Image(type="pil", label="๋ฐฐ๊ฒฝ ์ œ๊ฑฐ ๊ฒฐ๊ณผ", elem_classes="image-container", format="png")
with gr.Row():
bg_remove_status = gr.Textbox(label="์ƒํƒœ", lines=2, elem_classes="custom-input")
bg_remove_btn = gr.Button('โœ‚๏ธ ๋ฐฐ๊ฒฝ ์ œ๊ฑฐํ•˜๊ธฐ', elem_classes="custom-button primary")
# ์ด๋ฏธ์ง€ ํ•„ํ„ฐ ํƒญ UI ๊ฐœ์„ 
with gr.TabItem("๐Ÿ–Œ๏ธ ์ด๋ฏธ์ง€ ํ•„ํ„ฐ", elem_classes="tab-content"):
with gr.Row():
with gr.Column(elem_classes="panel"):
gr.HTML('<div class="section-title"><i class="fas fa-sliders-h"></i> ์ด๋ฏธ์ง€ ํ•„ํ„ฐ</div>')
gr.HTML("""
<p style="margin-bottom: 1rem; font-size: 1.05rem;">
์ด๋ฏธ์ง€ ํ•„ํ„ฐ๋ฅผ ์ ์šฉํ•˜์—ฌ ์ƒ‰์ƒ๊ณผ ํ†ค์„ ์กฐ์ ˆํ•ฉ๋‹ˆ๋‹ค.
</p>
<div class="tip-box">
<i class="fas fa-lightbulb"></i> <strong>ํŒ:</strong> ์Šฌ๋ผ์ด๋”๋ฅผ ์กฐ์ ˆํ•˜์—ฌ ์ด๋ฏธ์ง€์˜ ๋ฐ๊ธฐ, ๋Œ€๋น„, ์ฑ„๋„ ๋“ฑ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
</div>
""")
with gr.Row():
with gr.Column():
gr.HTML('<div class="section-title"><i class="fas fa-upload"></i> ์ž…๋ ฅ ์ด๋ฏธ์ง€</div>')
filter_input_image = gr.Image(type="pil", label="์—…๋กœ๋“œ", elem_classes="image-container")
with gr.Column():
gr.HTML('<div class="section-title"><i class="fas fa-image"></i> ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€</div>')
filter_output_image = gr.Image(type="pil", label="ํ•„ํ„ฐ ์ ์šฉ ๊ฒฐ๊ณผ", elem_classes="image-container")
gr.HTML('<div class="section-title"><i class="fas fa-sliders-h"></i> ํ•„ํ„ฐ ์กฐ์ ˆ</div>')
with gr.Group():
brightness_slider = gr.Slider(0.0, 2.0, value=1.0, step=0.1, label="๋ฐ๊ธฐ ์กฐ์ ˆ")
contrast_slider = gr.Slider(0.5, 1.5, value=1.0, step=0.1, label="๋Œ€๋น„ ์กฐ์ ˆ")
saturation_slider = gr.Slider(0.0, 2.0, value=1.0, step=0.1, label="์ฑ„๋„ ์กฐ์ ˆ")
temperature_slider = gr.Slider(-1.0, 1.0, value=0.0, step=0.1, label="์ƒ‰์˜จ๋„ ์กฐ์ ˆ")
tint_slider = gr.Slider(-100, 100, value=0, step=1, label="์ƒ‰์กฐ ์กฐ์ ˆ")
exposure_slider = gr.Slider(-5.0, 5.0, value=0.0, step=0.1, label="๋…ธ์ถœ ์กฐ์ ˆ")
vibrance_slider = gr.Slider(-100.0, 100.0, value=0.0, step=1.0, label="ํ™œ๊ธฐ ์กฐ์ ˆ")
color_mixer_blues_slider = gr.Slider(-100.0, 100.0, value=0.0, step=1.0, label="์ปฌ๋Ÿฌ ๋ฏน์„œ (๋ธ”๋ฃจ)")
shadows_slider = gr.Slider(-100.0, 100.0, value=0.0, step=1.0, label="๊ทธ๋ฆผ์ž ์กฐ์ ˆ")
# ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜ ์—ฐ๊ฒฐ
inputs = [
filter_input_image,
brightness_slider,
contrast_slider,
saturation_slider,
temperature_slider,
tint_slider,
exposure_slider,
vibrance_slider,
color_mixer_blues_slider,
shadows_slider
]
input_components = [
brightness_slider,
contrast_slider,
saturation_slider,
temperature_slider,
tint_slider,
exposure_slider,
vibrance_slider,
color_mixer_blues_slider,
shadows_slider
]
for input_component in input_components:
input_component.change(
fn=process_image,
inputs=inputs,
outputs=filter_output_image
)
filter_input_image.change(
fn=lambda x, *args: process_image(x, *args) if x is not None else None,
inputs=inputs,
outputs=filter_output_image
)
# ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
image_change_btn1.click(
fn=lambda: "(#1์˜ ์—ฌ์„ฑ)์ด ์‚ด์ง ๋’ค๋กœ ๋Œ์•„๋ณด๋Š” ๋ชจ์Šต์œผ๋กœ ์ตœ๋Œ€ํ•œ ์ด์ „ seed๋ฅผ ์œ ์ง€ํ•œํ…Œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ณ€๊ฒฝ.",
inputs=[],
outputs=prompt_input
)
image_change_btn2.click(
fn=lambda: "(#1 ๋ ˆ๋ชจ๋ชจํ˜•)์—์„œ ์ฒญ์ƒ‰์ƒ์–ด๋ ˆ๊ณ ๋งŒ ๊ฒ€์€์ƒ‰ ๊ณ ๋ž˜๋ ˆ๊ณ ๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์€ seed๋ฅผ ๋ณ€๊ฒฝ์„ ํ•˜์ง€๋งˆ๋ผ.",
inputs=[],
outputs=prompt_input
)
text_remove_btn.click(
fn=lambda: "(#1 ์ด๋ฏธ์ง€)์— ์žˆ๋Š” ์ค‘๊ตญ์–ด๋ฅผ ๋ชจ๋‘ ์ œ๊ฑฐํ•˜๋ผ.",
inputs=[],
outputs=prompt_input
)
text_change_btn.click(
fn=lambda: '(#1์˜ ํ…์ŠคํŠธ)๋ฅผ ์Šคํƒ€์ผ์„ ์œ ์ง€ํ•œ์ฒด ํ…์ŠคํŠธ๋งŒ "Hello"๋กœ ๋ฐ”๊ฟ”๋ผ',
inputs=[],
outputs=prompt_input
)
clothes_change_btn1.click(
fn=lambda: "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด ์‹ ์ฒด ๋น„์œจ๊ณผ ํฌ์ฆˆ๋Š” ์œ ์ง€ํ•œ ์ฒด (#2์˜ ์„ ๊ธ€๋ผ์Šค)์™€ (#3์˜ ์ฒญ๋ฐ”์ง€)๋ฅผ ์ง์ ‘ ๋ชจ๋ธ์ด ์ฐฉ์šฉํ•œ๊ฒƒ ์ฒ˜๋Ÿผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ชจ์Šต.",
inputs=[],
outputs=prompt_input
)
clothes_change_btn2.click(
fn=lambda: "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด (#2์˜ ์„ ๊ธ€๋ผ์Šค)์„ ์ฐฉ์šฉํ•˜๊ณ  (#3์˜ ๋’ท๋ฐฐ๊ฒฝ์˜ ์นดํŽ˜์ „์ฒด๊ฐ€ ๋ณด์ด๋ฉฐ) ์˜์ž์— ์•‰์•„ ์žˆ๋Š” ๋ชจ์Šต",
inputs=[],
outputs=prompt_input
)
holding_product_btn.click(
fn=lambda: "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด(#2์˜ ์™€์ธ์ž”)์„ ๋“ค๊ณ  ์žˆ๋Š” ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ชจ์Šต",
inputs=[],
outputs=prompt_input
)
background_change_btn.click(
fn=lambda: "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด (#2 ์นดํŽ˜)์—์„œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์žˆ๋Š” ๋ชจ์Šต",
inputs=[],
outputs=prompt_input
)
composite_product_btn.click(
fn=lambda: "(#1์˜ ๋ ˆ๊ณ ๋ชจํ˜•)์—์„œ ์ฒญ์ƒ‰์ƒ์–ด๋ ˆ๊ณ ๋ฅผ ์ œ๊ฑฐํ•œ ํ›„, ๊ทธ ์ž๋ฆฌ๋ฅผ ์ฃผ๋ณ€ ๋ฐฐ๊ฒฝ๊ณผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์–ด์šฐ๋Ÿฌ์ง€๋„๋ก ์ฑ„์›Œ์ฃผ์„ธ์š”. ๋‹จ, ์ด๋ฏธ์ง€์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„์˜ ์ฃผ์š” ์š”์†Œ๋Š” ๋™์ผํ•˜๊ฒŒ ์œ ์ง€ํ•ด ํ•ด์•ผํ•œ๋‹ค.",
inputs=[],
outputs=prompt_input
)
outpainting_btn.click(
fn=lambda: "(#1 ์ด๋ฏธ์ง€)๋ฅผ ์›๋ณธ๊ทธ๋Œ€๋กœ ์ค‘์•™์— ๋‘๊ณ  ๋น„์œจ๋กœ ์œ ์ง€ํ•œ์ฒด ์œ„์•„๋ž˜ ๋ฐ ์ขŒ์šฐ๋กœ ํฌ๊ฒŒ ํ™•์žฅํ•˜๋ผ.",
inputs=[],
outputs=prompt_input
)
# ๋‹จ์ผ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
submit_single_btn.click(
fn=lambda image1, image2, image3, prompt: process_images_with_prompt(image1, image2, image3, prompt, 0),
inputs=[image1_input, image2_input, image3_input, prompt_input],
outputs=[output_image1, output_text, prompt_display],
)
# 4์žฅ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
submit_btn.click(
fn=generate_multiple_images,
inputs=[image1_input, image2_input, image3_input, prompt_input],
outputs=[output_image1, output_image2, output_image3, output_image4, output_text, prompt_display],
)
gfpgan_btn.click(
fn=upscaler_korean,
inputs=gfpgan_input,
outputs=gfpgan_output
)
# ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
bg_remove_btn.click(
fn=remove_background,
inputs=bg_remove_input,
outputs=[bg_remove_output, bg_remove_status]
)
demo.queue()
demo.launch()