Download app.py from ssboost/jddru8d: direct link, hf CLI and curl.
- Browser
- Download file 38.3 kB
-
https://huggingface.co/spaces/ssboost/jddru8d/resolve/main/app.py
- Command line
-
hf download hf://spaces/ssboost/jddru8d/app.py
-
curl -L -o app.py https://huggingface.co/spaces/ssboost/jddru8d/resolve/main/app.py
38.3 kB
| 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 | |
| import random | |
| 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 ํค ์ํ ์์คํ ------------------- | |
| # API ํค ์ค์ - ํ๊ฒฝ๋ณ์์์ API_KEYS = [...] ๊ตฌ๋ฌธ ๊ทธ๋๋ก ์คํ | |
| def load_api_configs_from_env(): | |
| env_configs = os.getenv('API_KEYS') | |
| if env_configs: | |
| try: | |
| # API_KEYS = [...] ๊ตฌ๋ฌธ์ ๊ทธ๋๋ก ์คํ | |
| exec(env_configs, globals()) | |
| return globals().get('API_KEYS', []) | |
| except Exception as e: | |
| logger.error(f"โ ๏ธ API_KEYS ํ๊ฒฝ๋ณ์ ์คํ ์ค๋ฅ: {e}") | |
| return [] | |
| return [] | |
| # ํ๊ฒฝ๋ณ์์์ ๋ก๋ํ๊ฑฐ๋ ๊ธฐ๋ณธ๊ฐ ์ฌ์ฉ | |
| loaded_configs = load_api_configs_from_env() | |
| API_KEYS = loaded_configs if loaded_configs else [ | |
| "", | |
| "", | |
| "", | |
| "", | |
| "", | |
| "", | |
| "", | |
| "", | |
| "", | |
| "", | |
| ] | |
| current_key_index = -1 # -1์ ์์ง ์ฌ์ฉํ์ง ์์์์ ์๋ฏธ | |
| def initialize_api_keys(): | |
| """API ํค ๋ชฉ๋ก์ ์ด๊ธฐํํ๋ ํจ์""" | |
| global API_KEYS | |
| # ๋น ๋ฌธ์์ด์ด๋ None์ธ ํค๋ค ์ ๊ฑฐ | |
| API_KEYS = [key for key in API_KEYS if key and key.strip() and not key.startswith("your_")] | |
| logger.info(f"API ํค {len(API_KEYS)}๊ฐ๊ฐ ๋ก๋๋์์ต๋๋ค.") | |
| if len(API_KEYS) == 0: | |
| logger.error("์ฌ์ฉ ๊ฐ๋ฅํ API ํค๊ฐ ์์ต๋๋ค. API_KEYS ๋ฆฌ์คํธ์ ์ค์ ํค๋ฅผ ์ ๋ ฅํด์ฃผ์ธ์.") | |
| def get_next_api_key(): | |
| """๋ค์ API ํค๋ฅผ ๊ฐ์ ธ์ค๋ ํจ์ (์ฒ์์๋ ๋๋ค, ์ดํ์๋ ์์ฐจ์ )""" | |
| global current_key_index, API_KEYS | |
| if not API_KEYS: | |
| logger.error("API ํค๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค.") | |
| return None | |
| if current_key_index == -1: | |
| # ์ฒซ ๋ฒ์งธ ์ฌ์ฉ์ ๋๋ค์ผ๋ก ์์์ ์ ํ | |
| current_key_index = random.randint(0, len(API_KEYS) - 1) | |
| logger.info(f"์ฒซ ๋ฒ์งธ API ํค ๋๋ค ์ ํ: ์ธ๋ฑ์ค {current_key_index}") | |
| else: | |
| # ์ดํ์๋ ์์ฐจ์ ์ผ๋ก ๋ค์ ํค ์ฌ์ฉ | |
| current_key_index = (current_key_index + 1) % len(API_KEYS) | |
| logger.info(f"๋ค์ API ํค ์์ฐจ ์ ํ: ์ธ๋ฑ์ค {current_key_index}") | |
| return API_KEYS[current_key_index] | |
| # ========== ์ด๋ฏธ์ง ์์ฑ๊ธฐ ๊ด๋ จ ํจ์ ========== | |
| 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 ํค๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค. API_KEYS ํ๊ฒฝ๋ณ์์ 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." | |
| ] | |
| 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: # ์ฌ๊ธฐ๊ฐ 254๋ฒ์งธ ์ค ๊ทผ์ฒ์ผ ๊ฒ | |
| if prompt and prompt.strip(): | |
| # ํ๋กฌํํธ๋ง ์๋ ๊ฒฝ์ฐ ํ ์คํธ ๊ธฐ๋ฐ ์์ฑ ์๋ | |
| logger.info("ํ ์คํธ ํ๋กฌํํธ๋ง์ผ๋ก ์ด๋ฏธ์ง ์์ฑ ์๋") | |
| try: | |
| # ๋น ์ด๋ฏธ์ง ๋ฆฌ์คํธ๋ก ์์ฑ ์๋ | |
| result_img, status = generate_with_images(prompt, [], variation_index) | |
| if result_img is not None: | |
| return result_img, status, prompt | |
| except: | |
| pass | |
| 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 = 2 | |
| 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) < 2: | |
| results.append(None) | |
| combined_status = "\n".join(statuses) | |
| combined_prompts = "\n".join(prompts) | |
| return results[0], results[1], combined_status, combined_prompts | |
| # ๋คํฌ๋ชจ๋ ์๋ ์ ์ฉ ์ปค์คํ CSS ์คํ์ผ | |
| custom_css = """ | |
| /* ============================================ | |
| ๋คํฌ๋ชจ๋ ์๋ ๋ณ๊ฒฝ ํ ํ๋ฆฟ CSS | |
| ============================================ */ | |
| /* 1. CSS ๋ณ์ ์ ์ (๋ผ์ดํธ๋ชจ๋ - ๊ธฐ๋ณธ๊ฐ) */ | |
| :root { | |
| /* ๋ฉ์ธ ์ปฌ๋ฌ */ | |
| --primary-color: #FB7F0D; | |
| --secondary-color: #ff9a8b; | |
| --accent-color: #FF6B6B; | |
| /* ๋ฐฐ๊ฒฝ ์ปฌ๋ฌ */ | |
| --background-color: #FFFFFF; | |
| --card-bg: #ffffff; | |
| --input-bg: #ffffff; | |
| /* ํ ์คํธ ์ปฌ๋ฌ */ | |
| --text-color: #334155; | |
| --text-secondary: #64748b; | |
| /* ๋ณด๋ ๋ฐ ๊ตฌ๋ถ์ */ | |
| --border-color: #dddddd; | |
| --border-light: #e5e5e5; | |
| /* ํ ์ด๋ธ ์ปฌ๋ฌ */ | |
| --table-even-bg: #f3f3f3; | |
| --table-hover-bg: #f0f0f0; | |
| /* ๊ทธ๋ฆผ์ */ | |
| --shadow: 0 8px 30px rgba(251, 127, 13, 0.08); | |
| --shadow-light: 0 2px 4px rgba(0, 0, 0, 0.1); | |
| /* ๊ธฐํ */ | |
| --border-radius: 18px; | |
| } | |
| /* 2. ๋คํฌ๋ชจ๋ ์์ ๋ณ์ (์๋ ๊ฐ์ง) */ | |
| @media (prefers-color-scheme: dark) { | |
| :root { | |
| /* ๋ฐฐ๊ฒฝ ์ปฌ๋ฌ */ | |
| --background-color: #1a1a1a; | |
| --card-bg: #2d2d2d; | |
| --input-bg: #2d2d2d; | |
| /* ํ ์คํธ ์ปฌ๋ฌ */ | |
| --text-color: #e5e5e5; | |
| --text-secondary: #a1a1aa; | |
| /* ๋ณด๋ ๋ฐ ๊ตฌ๋ถ์ */ | |
| --border-color: #404040; | |
| --border-light: #525252; | |
| /* ํ ์ด๋ธ ์ปฌ๋ฌ */ | |
| --table-even-bg: #333333; | |
| --table-hover-bg: #404040; | |
| /* ๊ทธ๋ฆผ์ */ | |
| --shadow: 0 8px 30px rgba(0, 0, 0, 0.3); | |
| --shadow-light: 0 2px 4px rgba(0, 0, 0, 0.2); | |
| } | |
| } | |
| /* 3. ์๋ ๋คํฌ๋ชจ๋ ํด๋์ค (Gradio ํ ๊ธ์ฉ) */ | |
| [data-theme="dark"], | |
| .dark, | |
| .gr-theme-dark { | |
| /* ๋ฐฐ๊ฒฝ ์ปฌ๋ฌ */ | |
| --background-color: #1a1a1a; | |
| --card-bg: #2d2d2d; | |
| --input-bg: #2d2d2d; | |
| /* ํ ์คํธ ์ปฌ๋ฌ */ | |
| --text-color: #e5e5e5; | |
| --text-secondary: #a1a1aa; | |
| /* ๋ณด๋ ๋ฐ ๊ตฌ๋ถ์ */ | |
| --border-color: #404040; | |
| --border-light: #525252; | |
| /* ํ ์ด๋ธ ์ปฌ๋ฌ */ | |
| --table-even-bg: #333333; | |
| --table-hover-bg: #404040; | |
| /* ๊ทธ๋ฆผ์ */ | |
| --shadow: 0 8px 30px rgba(0, 0, 0, 0.3); | |
| --shadow-light: 0 2px 4px rgba(0, 0, 0, 0.2); | |
| } | |
| /* 4. ๊ธฐ๋ณธ ์์ ๋คํฌ๋ชจ๋ ์ ์ฉ */ | |
| body { | |
| font-family: 'Pretendard', 'Noto Sans KR', -apple-system, BlinkMacSystemFont, sans-serif; | |
| background-color: var(--background-color) !important; | |
| color: var(--text-color) !important; | |
| line-height: 1.6; | |
| transition: background-color 0.3s ease, color 0.3s ease; | |
| } | |
| /* 5. Gradio ์ปจํ ์ด๋ ๊ฐ์ ์ ์ฉ */ | |
| .gradio-container, | |
| .gradio-container *, | |
| .gr-app, | |
| .gr-app *, | |
| .gr-interface { | |
| background-color: var(--background-color) !important; | |
| color: var(--text-color) !important; | |
| } | |
| /* Gradio ์ปจํ ์ด๋ ์ค๋ฒ๋ผ์ด๋ */ | |
| .gradio-container { | |
| max-width: 100% !important; | |
| width: 100% !important; | |
| 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; | |
| } | |
| /* 6. ์นด๋ ๋ฐ ํจ๋ ์คํ์ผ */ | |
| .gr-group, | |
| .gr-form, | |
| .gr-box, | |
| .gr-panel, | |
| .custom-frame, | |
| [class*="frame"], | |
| [class*="card"], | |
| [class*="panel"] { | |
| background-color: var(--card-bg) !important; | |
| border-radius: var(--border-radius) !important; | |
| box-shadow: var(--shadow) !important; | |
| padding: 1.5rem !important; | |
| margin-bottom: 1.5rem !important; | |
| border: 1px solid var(--border-color) !important; | |
| transition: transform 0.3s ease, background-color 0.3s ease; | |
| color: var(--text-color) !important; | |
| } | |
| .gr-group:hover { | |
| transform: translateY(-5px); | |
| } | |
| /* 7. ์ ๋ ฅ ํ๋ ์คํ์ผ */ | |
| input[type="text"], | |
| input[type="number"], | |
| input[type="email"], | |
| input[type="password"], | |
| textarea, | |
| select, | |
| .gr-input, | |
| .gr-text-input, | |
| .gr-textarea, | |
| .gr-dropdown { | |
| background-color: var(--input-bg) !important; | |
| color: var(--text-color) !important; | |
| border: 1px solid var(--border-color) !important; | |
| border-radius: var(--border-radius) !important; | |
| padding: 12px !important; | |
| box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05) !important; | |
| transition: all 0.3s ease !important; | |
| } | |
| input[type="text"]:focus, | |
| input[type="number"]:focus, | |
| input[type="email"]:focus, | |
| input[type="password"]:focus, | |
| textarea:focus, | |
| select:focus, | |
| .gr-input:focus, | |
| .gr-text-input:focus, | |
| .gr-textarea:focus, | |
| .gr-dropdown:focus { | |
| border-color: var(--primary-color) !important; | |
| outline: none !important; | |
| box-shadow: 0 0 0 2px rgba(251, 127, 13, 0.2) !important; | |
| } | |
| /* 8. ๋ผ๋ฒจ ๋ฐ ํ ์คํธ ์์ */ | |
| label, | |
| .gr-label, | |
| .gr-checkbox label, | |
| .gr-radio label, | |
| p, span, div { | |
| color: var(--text-color) !important; | |
| } | |
| /* 9. ์น์ ์ ๋ชฉ */ | |
| .section-title { | |
| font-size: 22px !important; | |
| font-weight: 700 !important; | |
| color: var(--text-color) !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); | |
| } | |
| /* 10. ๋ฒํผ ์คํ์ผ๋ง */ | |
| .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; | |
| } | |
| button:not([class*="custom"]):not([class*="primary"]):not([class*="secondary"]) { | |
| background-color: var(--card-bg) !important; | |
| color: var(--text-color) !important; | |
| border: 1px solid var(--border-color) !important; | |
| border-radius: var(--border-radius) !important; | |
| transition: all 0.3s ease !important; | |
| } | |
| /* 11. ์ด๋ฏธ์ง ์ปจํ ์ด๋ */ | |
| .image-container { | |
| border-radius: var(--border-radius); | |
| overflow: hidden; | |
| border: 1px solid var(--border-color); | |
| transition: all 0.3s ease; | |
| background-color: var(--card-bg); | |
| } | |
| .image-container:hover { | |
| box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1); | |
| } | |
| /* 12. ํ ์ด๋ธ ์คํ์ผ */ | |
| table { | |
| background-color: var(--card-bg) !important; | |
| color: var(--text-color) !important; | |
| border-color: var(--border-color) !important; | |
| } | |
| table th { | |
| background-color: var(--primary-color) !important; | |
| color: white !important; | |
| border-color: var(--border-color) !important; | |
| } | |
| table td { | |
| background-color: var(--card-bg) !important; | |
| color: var(--text-color) !important; | |
| border-color: var(--border-color) !important; | |
| } | |
| table tbody tr:nth-child(even) { | |
| background-color: var(--table-even-bg) !important; | |
| } | |
| table tbody tr:hover { | |
| background-color: var(--table-hover-bg) !important; | |
| } | |
| /* 13. ์ฒดํฌ๋ฐ์ค ๋ฐ ๋ผ๋์ค ๋ฒํผ */ | |
| input[type="checkbox"], | |
| input[type="radio"] { | |
| accent-color: var(--primary-color) !important; | |
| } | |
| /* 14. ๋ฒํผ ๊ทธ๋ฃน */ | |
| .button-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); | |
| gap: 0.8rem; | |
| margin-bottom: 1.2rem; | |
| } | |
| /* 15. ์คํฌ๋กค๋ฐ ์คํ์ผ */ | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: var(--secondary-color); | |
| } | |
| /* 16. ์์ฝ๋์ธ ๋ฐ ๋๋กญ๋ค์ด */ | |
| details { | |
| background-color: var(--card-bg) !important; | |
| border-color: var(--border-color) !important; | |
| color: var(--text-color) !important; | |
| } | |
| details summary { | |
| background-color: var(--card-bg) !important; | |
| color: var(--text-color) !important; | |
| } | |
| /* 17. ํดํ ๋ฐ ํ์ */ | |
| [data-tooltip]:hover::after, | |
| .tooltip, | |
| .popup { | |
| background-color: var(--card-bg) !important; | |
| color: var(--text-color) !important; | |
| border-color: var(--border-color) !important; | |
| box-shadow: var(--shadow-light) !important; | |
| } | |
| /* 18. ๋ชจ๋ฌ ๋ฐ ์ค๋ฒ๋ ์ด */ | |
| .modal, | |
| .overlay, | |
| [class*="modal"], | |
| [class*="overlay"] { | |
| background-color: var(--card-bg) !important; | |
| color: var(--text-color) !important; | |
| border-color: var(--border-color) !important; | |
| } | |
| /* 19. ์ถ๊ฐ Gradio ์ปดํฌ๋ํธ๋ค */ | |
| .gr-block, | |
| .gr-group, | |
| .gr-row, | |
| .gr-column { | |
| background-color: var(--background-color) !important; | |
| color: var(--text-color) !important; | |
| } | |
| /* 20. ์ฝ๋ ๋ธ๋ก ๋ฐ pre ํ๊ทธ */ | |
| code, | |
| pre, | |
| .code-block { | |
| background-color: var(--table-even-bg) !important; | |
| color: var(--text-color) !important; | |
| border-color: var(--border-color) !important; | |
| } | |
| /* 21. ์๋ฆผ ๋ฐ ๋ฉ์์ง */ | |
| .alert, | |
| .message, | |
| .notification, | |
| [class*="alert"], | |
| [class*="message"], | |
| [class*="notification"] { | |
| background-color: var(--card-bg) !important; | |
| color: var(--text-color) !important; | |
| border-color: var(--border-color) !important; | |
| } | |
| /* 22. ์ ๋๋ฉ์ด์ ์คํ์ผ */ | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .fade-in { | |
| animation: fadeIn 0.5s ease-out; | |
| } | |
| /* 23. Examples ์น์ ์คํ์ผ */ | |
| .examples-section { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); | |
| gap: 1.5rem; | |
| margin-top: 1rem; | |
| } | |
| .example-item { | |
| background-color: var(--card-bg); | |
| border-radius: var(--border-radius); | |
| overflow: hidden; | |
| box-shadow: var(--shadow); | |
| transition: transform 0.3s ease, background-color 0.3s ease; | |
| } | |
| .example-item:hover { | |
| transform: translateY(-5px); | |
| } | |
| /* 24. ์ ํ ์ ๋๋ฉ์ด์ */ | |
| * { | |
| transition: background-color 0.3s ease, | |
| color 0.3s ease, | |
| border-color 0.3s ease !important; | |
| } | |
| /* 25. ๋ฐ์ํ */ | |
| @media (max-width: 768px) { | |
| .button-grid { | |
| grid-template-columns: repeat(2, 1fr); | |
| } | |
| .examples-section { | |
| grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); | |
| } | |
| } | |
| """ | |
| # 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" /> | |
| """ | |
| # ์ ๋ชฉ๊ณผ ์ฌ์ฉ ๊ฐ์ด๋ ์ ๊ฑฐ | |
| header_html = "" | |
| image_generator_guide_html = "" | |
| # UI ๊ตฌ์ฑ | |
| 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) | |
| # ์ ๋ชฉ ์ ๊ฑฐ | |
| # gr.HTML(header_html) | |
| # ํญ ์ ๊ฑฐํ๊ณ ์ด๋ฏธ์ง ์์ฑ๊ธฐ ๋ด์ฉ์ ์ง์ ๋ฐฐ์น | |
| 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('โจ ์ด๋ฏธ์ง ์์ฑ (2์ฅ)', 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) | |
| with gr.Column(): | |
| output_image2 = gr.Image(label="์ด๋ฏธ์ง #2", 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) # ๋ชจ๋ ์์ ๋ฅผ ํ ํ์ด์ง์ ํ์ | |
| ) | |
| # ========== ์ด๋ฏธ์ง ์์ฑ๊ธฐ ์ด๋ฒคํธ ์ฐ๊ฒฐ ========== | |
| # ๋ฒํผ ์ด๋ฒคํธ ์ฐ๊ฒฐ | |
| 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 | |
| ) | |
| # ๋จ์ผ ์ด๋ฏธ์ง ์์ฑ ๋ฒํผ ์ด๋ฒคํธ ์ฐ๊ฒฐ - API ํค ์ ๋ ฅ๊ฐ ์ ๊ฑฐ | |
| 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], | |
| ) | |
| # 2์ฅ ์ด๋ฏธ์ง ์์ฑ ๋ฒํผ ์ด๋ฒคํธ ์ฐ๊ฒฐ - API ํค ์ ๋ ฅ๊ฐ ์ ๊ฑฐ | |
| submit_btn.click( | |
| fn=generate_multiple_images, | |
| inputs=[image1_input, image2_input, image3_input, prompt_input], | |
| outputs=[output_image1, output_image2, output_text, prompt_display], | |
| ) | |
| # API ํค ์ด๊ธฐํ ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์คํ | |
| initialize_api_keys() # API ํค ์ด๊ธฐํ ํจ์ ํธ์ถ | |
| demo.queue() | |
| demo.launch(share=False, inbrowser=True, width="100%") # width ํ๋ผ๋ฏธํฐ ์ถ๊ฐ |