|
import os |
|
import tempfile |
|
from PIL import Image, ImageEnhance, ImageFilter |
|
import gradio as gr |
|
import logging |
|
import re |
|
import time |
|
import cv2 |
|
import numpy as np |
|
from io import BytesIO |
|
from datetime import datetime, timedelta |
|
|
|
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__) |
|
|
|
|
|
API_KEYS = [] |
|
current_key_index = 0 |
|
|
|
def initialize_api_keys(): |
|
"""API ํค ๋ชฉ๋ก์ ์ด๊ธฐํํ๋ ํจ์""" |
|
global API_KEYS |
|
|
|
key1 = os.environ.get("GEMINI_API_KEY_1", "") |
|
key2 = os.environ.get("GEMINI_API_KEY_2", "") |
|
key3 = os.environ.get("GEMINI_API_KEY_3", "") |
|
key4 = os.environ.get("GEMINI_API_KEY_4", "") |
|
key5 = os.environ.get("GEMINI_API_KEY_5", "") |
|
|
|
|
|
if key1: |
|
API_KEYS.append(key1) |
|
if key2: |
|
API_KEYS.append(key2) |
|
if key3: |
|
API_KEYS.append(key3) |
|
if key4: |
|
API_KEYS.append(key4) |
|
if key5: |
|
API_KEYS.append(key5) |
|
|
|
|
|
default_key = os.environ.get("GEMINI_API_KEY", "") |
|
if default_key and default_key not in API_KEYS: |
|
API_KEYS.append(default_key) |
|
|
|
logger.info(f"API ํค {len(API_KEYS)}๊ฐ๊ฐ ๋ก๋๋์์ต๋๋ค.") |
|
|
|
def get_next_api_key(): |
|
"""๋ค์ API ํค๋ฅผ ๊ฐ์ ธ์ค๋ ํจ์""" |
|
global current_key_index |
|
|
|
if not API_KEYS: |
|
return None |
|
|
|
|
|
api_key = API_KEYS[current_key_index] |
|
|
|
|
|
current_key_index = (current_key_index + 1) % len(API_KEYS) |
|
|
|
return api_key |
|
|
|
|
|
def save_binary_file(file_name, data): |
|
with open(file_name, "wb") as f: |
|
f.write(data) |
|
|
|
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 = get_next_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 = get_next_api_key() |
|
if not api_key: |
|
return None, "API ํค๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค. ํ๊ฒฝ ๋ณ์์ GEMINI_API_KEY_1, GEMINI_API_KEY_2, GEMINI_API_KEY_3, GEMINI_API_KEY_4, GEMINI_API_KEY_5 ์ค ํ๋ ์ด์์ ์ค์ ํด์ฃผ์ธ์." |
|
|
|
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 |
|
) |
|
) |
|
|
|
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, 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()): |
|
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 |
|
|
|
|
|
def adjust_brightness(image, value): |
|
"""์ด๋ฏธ์ง ๋ฐ๊ธฐ ์กฐ์ """ |
|
value = float(value - 1) * 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 |
|
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) |
|
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 |
|
|
|
|
|
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) |
|
|
|
|
|
return Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) |
|
|
|
def download_edited_image(image, input_image_name): |
|
"""์ด๋ฏธ์ง๋ฅผ JPG ํ์์ผ๋ก ์ ์ฅํ๊ณ ๊ฒฝ๋ก ๋ฐํ""" |
|
if image is None: |
|
return None |
|
|
|
|
|
def get_korean_timestamp(): |
|
korea_time = datetime.utcnow() + timedelta(hours=9) |
|
return korea_time.strftime('%Y%m%d_%H%M%S') |
|
|
|
timestamp = get_korean_timestamp() |
|
if input_image_name and hasattr(input_image_name, 'name'): |
|
base_name = input_image_name.name.split('.')[0] |
|
else: |
|
base_name = "์ด๋ฏธ์ง" |
|
|
|
file_name = f"[๋์ฅAI]๋์ฅํํฐ_{base_name}_{timestamp}.jpg" |
|
|
|
|
|
temp_file_path = tempfile.gettempdir() + "/" + file_name |
|
image.save(temp_file_path, format="JPEG") |
|
return temp_file_path |
|
|
|
|
|
custom_css = """ |
|
:root { |
|
--primary-color: #FB7F0D; |
|
--secondary-color: #ff9a8b; |
|
--accent-color: #FF6B6B; |
|
--background-color: #FFF3E9; |
|
--card-bg: #ffffff; |
|
--text-color: #334155; |
|
--border-radius: 18px; |
|
--shadow: 0 8px 30px rgba(251, 127, 13, 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; /* 200%์์ 100%๋ก ๋ณ๊ฒฝ */ |
|
width: 100% !important; /* ์ถ๊ฐ: ๋๋น 100% ์ง์ */ |
|
margin: 0 auto !important; |
|
padding: 0 !important; |
|
background-color: var(--background-color) !important; |
|
box-sizing: border-box !important; /* ์ถ๊ฐ: ํจ๋ฉ์ด ๋๋น์ ํฌํจ๋๋๋ก ์ค์ */ |
|
} |
|
|
|
/* ์ถ๊ฐ: ๋ด๋ถ ์ปจํ
์ด๋๋ 100% ๋๋น๋ก ์ค์ */ |
|
.contain { |
|
max-width: 100% !important; |
|
width: 100% !important; |
|
} |
|
|
|
/* ์ถ๊ฐ: ๊ฐ ํ(Row)๋ 100% ๋๋น๋ก ์ค์ */ |
|
.gr-padded { |
|
padding: 0 !important; |
|
width: 100% !important; |
|
max-width: 100% !important; |
|
} |
|
|
|
/* ํจ๋ ์คํ์ผ๋ง */ |
|
.gr-group { |
|
background-color: var(--card-bg); |
|
border-radius: var(--border-radius) !important; |
|
box-shadow: var(--shadow) !important; |
|
padding: 1.5rem !important; |
|
margin-bottom: 1.5rem !important; |
|
border: 1px solid rgba(0, 0, 0, 0.04) !important; |
|
transition: transform 0.3s ease; |
|
} |
|
|
|
.gr-group:hover { |
|
transform: translateY(-5px); |
|
} |
|
|
|
/* ์น์
์ ๋ชฉ */ |
|
.section-title { |
|
font-size: 22px !important; |
|
font-weight: 700 !important; |
|
color: #333333 !important; |
|
margin-bottom: 1rem !important; |
|
padding-bottom: 0.5rem !important; |
|
border-bottom: 2px solid var(--primary-color) !important; |
|
display: flex; |
|
align-items: center; |
|
} |
|
|
|
.section-title span { |
|
color: var(--primary-color); |
|
} |
|
|
|
/* ๋ฒํผ ์คํ์ผ๋ง */ |
|
.custom-button { |
|
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)) !important; |
|
color: white !important; |
|
font-weight: 600 !important; |
|
border: none !important; |
|
border-radius: 30px !important; |
|
padding: 12px 24px !important; |
|
box-shadow: 0 4px 8px rgba(251, 127, 13, 0.25) !important; |
|
transition: all 0.3s ease !important; |
|
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 6px 12px rgba(251, 127, 13, 0.3) !important; |
|
} |
|
|
|
.custom-button.primary { |
|
background: linear-gradient(135deg, var(--accent-color), #ff9a8b) !important; |
|
} |
|
|
|
/* ์ด๋ฏธ์ง ์ปจํ
์ด๋ */ |
|
.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); |
|
} |
|
|
|
/* ํญ ์คํ์ผ ๊ฐ์ */ |
|
.tabs { |
|
border-bottom: none !important; |
|
} |
|
|
|
.tab-nav { |
|
background-color: transparent !important; |
|
border-bottom: 1px solid #eeeeee !important; |
|
padding: 0 !important; |
|
} |
|
|
|
.tab-nav button { |
|
border-radius: var(--border-radius) var(--border-radius) 0 0 !important; |
|
margin-right: 5px !important; |
|
padding: 12px 20px !important; |
|
font-size: 18px !important; |
|
font-weight: 600 !important; |
|
border: 1px solid #eeeeee !important; |
|
border-bottom: none !important; |
|
background-color: rgba(255, 255, 255, 0.7) !important; |
|
color: var(--text-color) !important; |
|
transition: all 0.3s ease !important; |
|
min-width: 150px !important; |
|
text-align: center !important; |
|
} |
|
|
|
.tab-nav button.selected { |
|
background-color: var(--primary-color) !important; |
|
color: white !important; |
|
border-color: var(--primary-color) !important; |
|
box-shadow: 0 -2px 6px rgba(251, 127, 13, 0.2) !important; |
|
} |
|
|
|
.tab-nav button:hover:not(.selected) { |
|
background-color: var(--background-color) !important; |
|
border-bottom: none !important; |
|
} |
|
|
|
/* ์
๋ ฅ ํ๋ ์คํ์ผ */ |
|
.gr-input, .gr-text-input, .gr-sample-inputs { |
|
border-radius: var(--border-radius) !important; |
|
border: 1px solid #dddddd !important; |
|
padding: 12px !important; |
|
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05) !important; |
|
transition: all 0.3s ease !important; |
|
} |
|
|
|
.gr-input:focus, .gr-text-input:focus { |
|
border-color: var(--primary-color) !important; |
|
outline: none !important; |
|
box-shadow: 0 0 0 2px rgba(251, 127, 13, 0.2) !important; |
|
} |
|
|
|
/* ๋ฒํผ ๊ทธ๋ฃน */ |
|
.button-grid { |
|
display: grid; |
|
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); |
|
gap: 0.8rem; |
|
margin-bottom: 1.2rem; |
|
} |
|
|
|
/* ๋ฉ์ธ ์ปจํ
์ธ ์คํฌ๋กค๋ฐ */ |
|
::-webkit-scrollbar { |
|
width: 8px; |
|
height: 8px; |
|
} |
|
|
|
::-webkit-scrollbar-track { |
|
background: rgba(0, 0, 0, 0.05); |
|
border-radius: 10px; |
|
} |
|
|
|
::-webkit-scrollbar-thumb { |
|
background: var(--primary-color); |
|
border-radius: 10px; |
|
} |
|
|
|
/* ํธ์ง๊ธฐ ํน๋ณ ์คํ์ผ */ |
|
.editor-section { |
|
background-color: var(--card-bg); |
|
border-radius: var(--border-radius); |
|
box-shadow: var(--shadow); |
|
padding: 1.5rem; |
|
margin-bottom: 1.5rem; |
|
} |
|
|
|
.editor-title { |
|
font-size: 1.5rem; |
|
font-weight: 700; |
|
color: var(--primary-color); |
|
margin-bottom: 1rem; |
|
display: flex; |
|
align-items: center; |
|
} |
|
|
|
.editor-title i { |
|
margin-right: 0.5rem; |
|
} |
|
|
|
.download-button { |
|
background-color: var(--accent-color) !important; |
|
color: white !important; |
|
border: none !important; |
|
padding: 10px !important; |
|
font-size: 12px !important; |
|
border-radius: var(--border-radius) !important; |
|
font-weight: 600 !important; |
|
transition: all 0.3s ease !important; |
|
} |
|
|
|
.download-button:hover { |
|
transform: translateY(-2px) !important; |
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15) !important; |
|
} |
|
|
|
.download-container { |
|
display: flex; |
|
flex-direction: column; |
|
align-items: center; |
|
width: 100%; |
|
} |
|
|
|
.download-output { |
|
width: 100%; |
|
margin-top: 1rem; |
|
} |
|
|
|
/* ์ ๋๋ฉ์ด์
์คํ์ผ */ |
|
@keyframes fadeIn { |
|
from { opacity: 0; transform: translateY(10px); } |
|
to { opacity: 1; transform: translateY(0); } |
|
} |
|
|
|
.fade-in { |
|
animation: fadeIn 0.5s ease-out; |
|
} |
|
|
|
/* Examples ์น์
์คํ์ผ */ |
|
.examples-section { |
|
display: grid; |
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); |
|
gap: 1.5rem; |
|
margin-top: 1rem; |
|
} |
|
|
|
.example-item { |
|
background-color: white; |
|
border-radius: var(--border-radius); |
|
overflow: hidden; |
|
box-shadow: var(--shadow); |
|
transition: transform 0.3s ease; |
|
} |
|
|
|
.example-item:hover { |
|
transform: translateY(-5px); |
|
} |
|
|
|
/* ๋ฐ์ํ */ |
|
@media (max-width: 768px) { |
|
.button-grid { |
|
grid-template-columns: repeat(2, 1fr); |
|
} |
|
|
|
.examples-section { |
|
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); |
|
} |
|
} |
|
""" |
|
|
|
|
|
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" /> |
|
""" |
|
|
|
|
|
header_html = "" |
|
image_generator_guide_html = "" |
|
image_editor_guide_html = "" |
|
|
|
|
|
with gr.Blocks(css=custom_css, theme=gr.themes.Default( |
|
primary_hue="orange", |
|
secondary_hue="orange", |
|
font=[gr.themes.GoogleFont("Noto Sans KR"), "ui-sans-serif", "system-ui"] |
|
)) as demo: |
|
gr.HTML(fontawesome_link) |
|
|
|
|
|
|
|
with gr.Tabs(elem_classes="tabs") as tabs: |
|
|
|
with gr.Tab("โจ ์ด๋ฏธ์ง ์์ฑ๊ธฐ", elem_classes="tab-content"): |
|
|
|
|
|
with gr.Row(equal_height=True): |
|
with gr.Column(scale=1): |
|
|
|
|
|
|
|
with gr.Group(): |
|
gr.HTML('<div class="section-title"><i class="fas fa-upload"></i> <span>์ด๋ฏธ์ง ์
๋ก๋ ๋ฐ ์ค์ </span></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) |
|
|
|
|
|
prompt_input = gr.Textbox( |
|
lines=3, |
|
placeholder="ํ๋กฌํํธ๋ฅผ ์
๋ ฅํ๊ฑฐ๋ ๋น์๋๋ฉด ์๋ ํฉ์ฑ๋ฉ๋๋ค. '#1', '#2', '#3'์ผ๋ก ๊ฐ ์ด๋ฏธ์ง๋ฅผ ์ฐธ์กฐํ ์ ์์ต๋๋ค.", |
|
label="ํ๋กฌํํธ (์ ํ ์ฌํญ)", |
|
elem_classes="gr-text-input" |
|
) |
|
|
|
|
|
with gr.Group(): |
|
gr.HTML('<div class="section-title"><i class="fas fa-sliders-h"></i> <span>ํ๋กฌํํธ ํ
ํ๋ฆฟ</span></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") |
|
image_change_btn3= gr.Button('๐ ๋ถ๋ถ๋ณ๊ฒฝ-3', 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") |
|
food_btn_1 = gr.Button('๐ฝ๏ธ ํ๋ ์ดํ
-1', elem_classes="custom-button") |
|
food_btn_2 = gr.Button('๐ฝ๏ธ ํ๋ ์ดํ
-2', elem_classes="custom-button") |
|
food_btn_3 = gr.Button('๐ฝ๏ธ ํ๋ ์ดํ
-3', elem_classes="custom-button") |
|
|
|
|
|
with gr.Group(): |
|
gr.HTML('<div class="section-title"><i class="fas fa-image"></i> <span>์ด๋ฏธ์ง ์์ฑ</span></div>') |
|
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(): |
|
gr.HTML('<div class="section-title"><i class="fas fa-images"></i> <span>์์ฑ๋ ์ด๋ฏธ์ง</span></div>') |
|
with gr.Row(): |
|
with gr.Column(): |
|
output_image1 = gr.Image(label="์ด๋ฏธ์ง #1", elem_classes="image-container", height=400) |
|
output_image3 = gr.Image(label="์ด๋ฏธ์ง #3", elem_classes="image-container", height=400) |
|
with gr.Column(): |
|
output_image2 = gr.Image(label="์ด๋ฏธ์ง #2", elem_classes="image-container", height=400) |
|
output_image4 = gr.Image(label="์ด๋ฏธ์ง #4", elem_classes="image-container", height=400) |
|
|
|
|
|
with gr.Group(): |
|
gr.HTML('<div class="section-title"><i class="fas fa-info-circle"></i> <span>๊ฒฐ๊ณผ ์ ๋ณด</span></div>') |
|
output_text = gr.Textbox(label="์ํ ๋ฉ์์ง", lines=2, elem_classes="gr-text-input") |
|
prompt_display = gr.Textbox(label="์ฌ์ฉ๋ ํ๋กฌํํธ (์์ด)", visible=True, lines=2, elem_classes="gr-text-input") |
|
|
|
|
|
gr.HTML('<div class="section-title"><i class="fas fa-lightbulb"></i> <span>์์ ์ด๋ฏธ์ง</span></div>') |
|
|
|
|
|
examples = [ |
|
["down/๋ชจ๋ธ.jpg", None, None, "(#1์ ์ฌ์ฑ)์ด ์ด์ง ๋ค๋ก ๋์๋ณด๋ ๋ชจ์ต์ผ๋ก ์ต๋ํ ์ด์ seed๋ฅผ ์ ์งํ์ฒด ์์ฐ์ค๋ฝ๊ฒ ๋ณ๊ฒฝํ๋ผ."], |
|
["down/์์ด๋ ๊ณ ๋ชจํ.png", None, None, "(#1 ๋ ๋ชจ๋ชจํ)์์ ์ฒญ์์์ด๋ ๊ณ ๋ง ๊ฒ์์ ๊ณ ๋๋ ๊ณ ๋ก ๋ณ๊ฒฝํ๊ณ ๋๋จธ์ง ๋ถ๋ถ์ seed๋ฅผ ๋ณ๊ฒฝ์ ํ์ง๋ง๋ผ."], |
|
["down/์ผ์๊ฐ๋ฐฉ.png", None, None, "(#1 ์ฌํ์ฉ ์ผ์๋ฐ์ค)์์ ์ผ์์ด ๋ด๊ธด 3์์ ์ฝ๋ผ๊ฐ ๋์ฌ์๋ ์ด๋ฏธ์ง๋ฅผ ์์ฑํ๋ผ."], |
|
["down/์ค๊ตญ์ด.png", None, None, "(#1 ์ด๋ฏธ์ง)์ ์๋ ์ค๊ตญ์ด๋ฅผ ๋ชจ๋ ์ ๊ฑฐํ๋ผ."], |
|
["down/ํ
์คํธ.webp", None, None, '(#1์ ํ
์คํธ)๋ฅผ ์คํ์ผ์ ์ ์งํ์ฒด ํ
์คํธ๋ง "Hello"๋ก ๋ฐ๊ฟ๋ผ'], |
|
["down/๋ชจ๋ธ2.png", "down/์ ๊ธ๋ผ์ค.png", "down/์ฒญ๋ฐ์ง.png", "(#1์ ์ฌ์ฑ๋ชจ๋ธ)์ด ์ ์ฒด ๋น์จ๊ณผ ํฌ์ฆ๋ ์ ์งํ ์ฒด (#2์ ์ ๊ธ๋ผ์ค)์ (#3์ ์ฒญ๋ฐ์ง)๋ฅผ ์ง์ ๋ชจ๋ธ์ด ์ฐฉ์ฉํ๊ฒ ์ฒ๋ผ ์์ฐ์ค๋ฝ๊ฒ ๋ชจ์ต์ ์์ฑํ๋ผ."], |
|
["down/๋ชจ๋ธ2.png", "down/์ ๊ธ๋ผ์ค.png", "down/์นดํ์ ๊ฒฝ.png", "(#1์ ์ฌ์ฑ๋ชจ๋ธ)์ด (#2์ ์ ๊ธ๋ผ์ค)์ ์ฐฉ์ฉํ๊ณ (#3์ ๋ท๋ฐฐ๊ฒฝ์ ์นดํ์ ์ฒด๊ฐ ๋ณด์ด๋ฉฐ) ์์์ ์์ ์๋ ๋ชจ์ต์ ์์ฑํ๋ผ."], |
|
["down/๋ชจ๋ธ2.png", "down/์์ธ์.png", None, "(#1์ ์ฌ์ฑ๋ชจ๋ธ)์ด(#2์ ์์ธ์)์ ๋ค๊ณ ์๋ ์์ฐ์ค๋ฌ์ด ๋ชจ์ต์ ์์ฑํ๋ผ."], |
|
["down/๋ชจ๋ธ2.png", "down/์นดํ์ ๊ฒฝ.png", None, "(#1์ ์ฌ์ฑ๋ชจ๋ธ)์ด (#2 ์นดํ)์์ ์์ฐ์ค๋ฝ๊ฒ ์๋ ๋ชจ์ต์ ์์ฑํ๋ผ."], |
|
["down/์์ด๋ ๊ณ ๋ชจํ.png", None, None, "(#1์ ๋ ๊ณ ๋ชจํ)์์ ์ฒญ์์์ด๋ ๊ณ ๋ฅผ ์ ๊ฑฐํ ํ, ๊ทธ ์๋ฆฌ๋ฅผ ์ฃผ๋ณ ๋ฐฐ๊ฒฝ๊ณผ ์์ฐ์ค๋ฝ๊ฒ ์ด์ฐ๋ฌ์ง๋๋ก ์ฑ์์ฃผ์ธ์. ๋จ, ์ด๋ฏธ์ง์ ๋ค๋ฅธ ๋ถ๋ถ์ ์ฃผ์ ์์๋ ๋์ผํ๊ฒ ์ ์งํด์ผํ๋ค."], |
|
["down/์นดํ์ ๊ฒฝ.png", None, None, "(#1 ์ด๋ฏธ์ง)๋ฅผ ์๋ณธ๊ทธ๋๋ก ์ค์์ ๋๊ณ ๋น์จ๋ก ์ ์งํ ์ฒด ์์๋ ๋ฐ ์ข์ฐ๋ก ํฌ๊ฒ ํ์ฅํ๋ผ."], |
|
["down/์๋ฌ๋.png", None, None, "(#1์
๋ฌ๋)์ ๋ด์ ์ฉ๊ธฐ๋ ๋ฒ๋ฆฌ๊ณ ๋๊ณ ํฐ ์์ ์ ์์ (#1์
๋ฌ๋)์์๋ง ๊ฐ๋ ์ฑ์์ ์์
์ ์ธ ๊ฐ๋๋ก ์ด์ธ๋ฆฌ๋ ์ํ๊ณผ ํจ๊ป ํ๋ ์ดํ
ํ ๋ชจ์ต์ ์ด๋ฏธ์ง๋ก ์์ฑํ๋ผ. "], |
|
["down/์๋ฌ๋.png", "down/ํ๋ ์ดํ
.png", None, "(#2 ํ๋ ์ดํ
ํ ์ด๋ฏธ์ง)์ ๋ด๊ธด ์์์ (#1 ์๋ฌ๋)๋ก ๋ฐ๊พธ๊ณ ๋๋จธ์ง๋ ์๋๋ฅผ ์ ์งํ ์ฒด ์ด๋ฏธ์ง๋ฅผ ์์ฑํ๋ผ."], |
|
["down/์ปต.png", None, None, "(#1์ปต)์ ๋ธ๊ธฐ, ๋ฐ๋๋ผ, ์ด์ฝ ์์ด์คํฌ๋ฆผ์ ๋ด๊ณ ๊ทธ ์์ ์ด์ฝ ์๋ฝ์ด ํ๋ฅด๊ฒ ์ด๋ฏธ์ง๋ฅผ ์์ฑํ๋ผ."] |
|
] |
|
|
|
|
|
gr.Examples( |
|
examples=examples, |
|
inputs=[image1_input, image2_input, image3_input, prompt_input], |
|
examples_per_page=len(examples) |
|
) |
|
|
|
|
|
with gr.Tab("๐จ ์ด๋ฏธ์ง ํธ์ง๊ธฐ", elem_classes="tab-content"): |
|
|
|
|
|
|
|
with gr.Row(): |
|
|
|
with gr.Column(scale=1): |
|
|
|
with gr.Group(): |
|
gr.HTML('<div class="section-title"><i class="fas fa-upload"></i> <span>์ด๋ฏธ์ง ์
๋ก๋</span></div>') |
|
edit_input_image = gr.Image(type="pil", label="ํธ์งํ ์ด๋ฏธ์ง", elem_classes="image-container") |
|
|
|
|
|
with gr.Group(): |
|
gr.HTML('<div class="section-title"><i class="fas fa-sliders-h"></i> <span>์ด๋ฏธ์ง ์กฐ์ </span></div>') |
|
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="๊ทธ๋ฆผ์ ์กฐ์ ") |
|
|
|
|
|
with gr.Column(scale=1): |
|
|
|
with gr.Group(): |
|
gr.HTML('<div class="section-title"><i class="fas fa-images"></i> <span>ํธ์ง๋ ์ด๋ฏธ์ง</span></div>') |
|
edit_output_image = gr.Image(type="pil", label="ํธ์ง๋ ์ด๋ฏธ์ง", elem_classes="image-container") |
|
|
|
|
|
with gr.Group(): |
|
gr.HTML('<div class="section-title"><i class="fas fa-download"></i> <span>์ ์ฅ</span></div>') |
|
with gr.Row(elem_classes="download-container"): |
|
download_button = gr.Button("JPG๋ก ๋ณํํ๊ธฐ", elem_classes="download-button") |
|
with gr.Row(elem_classes="download-container"): |
|
download_output = gr.File(label="JPG ์ด๋ฏธ์ง ๋ค์ด๋ก๋", elem_classes="download-output") |
|
|
|
|
|
|
|
image_change_btn1.click( |
|
fn=lambda: "(#1์ ์ฌ์ฑ)์ด ์ด์ง ๋ค๋ก ๋์๋ณด๋ ๋ชจ์ต์ผ๋ก ์ต๋ํ ์ด์ seed๋ฅผ ์ ์งํํ
์์ฐ์ค๋ฝ๊ฒ ๋ณ๊ฒฝํ๋ผ.", |
|
inputs=[], |
|
outputs=prompt_input |
|
) |
|
image_change_btn2.click( |
|
fn=lambda: "(#1 ๋ ๋ชจ๋ชจํ)์์ ์ฒญ์์์ด๋ ๊ณ ๋ง ๊ฒ์์ ๊ณ ๋๋ ๊ณ ๋ก ๋ณ๊ฒฝํ๊ณ ๋๋จธ์ง ๋ถ๋ถ์ seed๋ฅผ ๋ณ๊ฒฝ์ ํ์ง๋ง๋ผ.", |
|
inputs=[], |
|
outputs=prompt_input |
|
) |
|
image_change_btn3.click( |
|
fn=lambda: "(#1 ์ฌํ์ฉ ์ผ์๋ฐ์ค)์์ ์ผ์์ด ๋ด๊ธด 3์์ ์ฝ๋ผ๊ฐ ๋์ฌ์๋ ์ด๋ฏธ์ง๋ฅผ ์์ฑํ๋ผ.", |
|
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 |
|
|
|
) |
|
food_btn_1.click( |
|
fn=lambda: "(#1์
๋ฌ๋)์ ๋ด์ ์ฉ๊ธฐ๋ ๋ฒ๋ฆฌ๊ณ ๋๊ณ ํฐ ์์ ์ ์์ (#1์
๋ฌ๋)์์๋ง ๊ฐ๋ ์ฑ์์ ์์
์ ์ธ ๊ฐ๋๋ก ์ด์ธ๋ฆฌ๋ ์ํ๊ณผ ํจ๊ป ํ๋ ์ดํ
ํ ๋ชจ์ต์ ์ด๋ฏธ์ง๋ก ์์ฑํ๋ผ. ", |
|
inputs=[], |
|
outputs=prompt_input |
|
) |
|
food_btn_2.click( |
|
fn=lambda: "(#2 ํ๋ ์ดํ
ํ ์ด๋ฏธ์ง)์ ๋ด๊ธด ์์์ (#1 ์๋ฌ๋)๋ก ๋ฐ๊พธ๊ณ ๋๋จธ์ง๋ ์๋๋ฅผ ์ ์งํ ์ฒด ์ด๋ฏธ์ง๋ฅผ ์์ฑํ๋ผ.", |
|
inputs=[], |
|
outputs=prompt_input |
|
) |
|
|
|
food_btn_3.click( |
|
fn=lambda: "(#1์ปต)์ ๋ธ๊ธฐ, ๋ฐ๋๋ผ, ์ด์ฝ ์์ด์คํฌ๋ฆผ์ ๋ด๊ณ ๊ทธ ์์ ์ด์ฝ ์๋ฝ์ด ํ๋ฅด๊ฒ ์ด๋ฏธ์ง๋ฅผ ์์ฑํ๋ผ.", |
|
inputs=[], |
|
outputs=prompt_input |
|
) |
|
|
|
|
|
def generate_single_image(image1, image2, image3, prompt): |
|
return process_images_with_prompt(image1, image2, image3, prompt, 0, 3) |
|
|
|
submit_single_btn.click( |
|
fn=generate_single_image, |
|
inputs=[image1_input, image2_input, image3_input, prompt_input], |
|
outputs=[output_image1, output_text, prompt_display], |
|
) |
|
|
|
|
|
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], |
|
) |
|
|
|
|
|
|
|
edit_inputs = [ |
|
edit_input_image, |
|
brightness_slider, |
|
contrast_slider, |
|
saturation_slider, |
|
temperature_slider, |
|
tint_slider, |
|
exposure_slider, |
|
vibrance_slider, |
|
color_mixer_blues_slider, |
|
shadows_slider |
|
] |
|
|
|
edit_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 edit_input_components: |
|
input_component.change( |
|
fn=process_image, |
|
inputs=edit_inputs, |
|
outputs=edit_output_image |
|
) |
|
|
|
|
|
edit_input_image.change( |
|
fn=process_image, |
|
inputs=edit_inputs, |
|
outputs=edit_output_image |
|
) |
|
|
|
|
|
download_button.click( |
|
fn=download_edited_image, |
|
inputs=[edit_output_image, edit_input_image], |
|
outputs=download_output |
|
) |
|
|
|
|
|
initialize_api_keys() |
|
demo.queue() |
|
demo.launch(share=False, inbrowser=True, width="100%") |