|
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 dotenv import load_dotenv |
|
from gradio_client import Client, handle_file |
|
|
|
load_dotenv() |
|
|
|
|
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') |
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
|
|
API_ENDPOINT = os.environ.get("API_ENDPOINT", "") |
|
|
|
if not API_ENDPOINT: |
|
raise ValueError("API_ENDPOINT ํ๊ฒฝ๋ณ์๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค.") |
|
|
|
|
|
try: |
|
client = Client(API_ENDPOINT) |
|
logger.info("ํด๋ผ์ด์ธํธ ์ด๊ธฐํ ์๋ฃ") |
|
except Exception as e: |
|
logger.error(f"ํด๋ผ์ด์ธํธ ์ด๊ธฐํ ์คํจ: {e}") |
|
raise |
|
|
|
def safe_client_call(api_name, **kwargs): |
|
"""์์ ํ ํด๋ผ์ด์ธํธ ํธ์ถ ํจ์""" |
|
try: |
|
return client.predict(api_name=api_name, **kwargs) |
|
except Exception as e: |
|
logger.error(f"API ํธ์ถ ์คํจ ({api_name}): {e}") |
|
return None |
|
|
|
def convert_image_to_file(image): |
|
"""PIL Image ๊ฐ์ฒด๋ฅผ ์์ ํ์ผ๋ก ๋ณํ""" |
|
if image is None: |
|
return None |
|
|
|
try: |
|
|
|
if hasattr(image, 'save'): |
|
|
|
temp_file = tempfile.NamedTemporaryFile(suffix='.png', delete=False) |
|
image.save(temp_file.name, format='PNG') |
|
temp_file.close() |
|
return temp_file.name |
|
|
|
elif isinstance(image, str): |
|
return image |
|
|
|
else: |
|
return image |
|
except Exception as e: |
|
logger.error(f"์ด๋ฏธ์ง ํ์ผ ๋ณํ ์คํจ: {e}") |
|
return None |
|
|
|
|
|
def generate_single_image(image1, image2, image3, prompt): |
|
"""๋จ์ผ ์ด๋ฏธ์ง ์์ฑ - ์๋ณธ ํจ์๋ช
์ ์ง""" |
|
try: |
|
|
|
image1_path = convert_image_to_file(image1) |
|
image2_path = convert_image_to_file(image2) |
|
image3_path = convert_image_to_file(image3) |
|
|
|
|
|
image1_file = handle_file(image1_path) if image1_path else None |
|
image2_file = handle_file(image2_path) if image2_path else None |
|
image3_file = handle_file(image3_path) if image3_path else None |
|
|
|
result = safe_client_call( |
|
"/generate_single_image", |
|
image1=image1_file, |
|
image2=image2_file, |
|
image3=image3_file, |
|
prompt=prompt |
|
) |
|
|
|
if result: |
|
return result[0], result[1], result[2] |
|
else: |
|
return None, "API ํธ์ถ ์คํจ", "" |
|
|
|
except Exception as e: |
|
logger.error(f"๋จ์ผ ์ด๋ฏธ์ง ์์ฑ ์คํจ: {e}") |
|
return None, f"์ค๋ฅ ๋ฐ์: {str(e)}", "" |
|
|
|
def generate_multiple_images(image1, image2, image3, prompt, progress=gr.Progress()): |
|
"""๋ค์ค ์ด๋ฏธ์ง ์์ฑ - ์๋ณธ ํจ์๋ช
์ ์ง""" |
|
try: |
|
progress(0, desc="์ด๋ฏธ์ง ์์ฑ ์ค๋น ์ค...") |
|
|
|
|
|
image1_path = convert_image_to_file(image1) |
|
image2_path = convert_image_to_file(image2) |
|
image3_path = convert_image_to_file(image3) |
|
|
|
|
|
image1_file = handle_file(image1_path) if image1_path else None |
|
image2_file = handle_file(image2_path) if image2_path else None |
|
image3_file = handle_file(image3_path) if image3_path else None |
|
|
|
progress(0.5, desc="์ด๋ฏธ์ง ์์ฑ ์ค...") |
|
|
|
result = safe_client_call( |
|
"/generate_multiple_images", |
|
image1=image1_file, |
|
image2=image2_file, |
|
image3=image3_file, |
|
prompt=prompt |
|
) |
|
|
|
progress(1.0, desc="์ด๋ฏธ์ง ์์ฑ ์๋ฃ!") |
|
|
|
if result: |
|
return result[0], result[1], result[2], result[3] |
|
else: |
|
return None, None, "API ํธ์ถ ์คํจ", "" |
|
|
|
except Exception as e: |
|
logger.error(f"๋ค์ค ์ด๋ฏธ์ง ์์ฑ ์คํจ: {e}") |
|
return None, None, f"์ค๋ฅ ๋ฐ์: {str(e)}", "" |
|
|
|
|
|
def get_prompt_template_1(): |
|
"""ํ๋กฌํํธ ํ
ํ๋ฆฟ 1 - ๋ถ๋ถ๋ณ๊ฒฝ-1""" |
|
return "(#1์ ์ฌ์ฑ)์ด ์ด์ง ๋ค๋ก ๋์๋ณด๋ ๋ชจ์ต์ผ๋ก ์ต๋ํ ์ด์ seed๋ฅผ ์ ์งํํ
์์ฐ์ค๋ฝ๊ฒ ๋ณ๊ฒฝํ๋ผ." |
|
|
|
def get_prompt_template_2(): |
|
"""ํ๋กฌํํธ ํ
ํ๋ฆฟ 2 - ๋ถ๋ถ๋ณ๊ฒฝ-2""" |
|
return "(#1 ๋ ๋ชจ๋ชจํ)์์ ์ฒญ์์์ด๋ ๊ณ ๋ง ๊ฒ์์ ๊ณ ๋๋ ๊ณ ๋ก ๋ณ๊ฒฝํ๊ณ ๋๋จธ์ง ๋ถ๋ถ์ seed๋ฅผ ๋ณ๊ฒฝ์ ํ์ง๋ง๋ผ." |
|
|
|
def get_prompt_template_3(): |
|
"""ํ๋กฌํํธ ํ
ํ๋ฆฟ 3 - ๋ถ๋ถ๋ณ๊ฒฝ-3""" |
|
return "(#1 ์ฌํ์ฉ ์ผ์๋ฐ์ค)์์ ์ผ์์ด ๋ด๊ธด 3์์ ์ฝ๋ผ๊ฐ ๋์ฌ์๋ ์ด๋ฏธ์ง๋ฅผ ์์ฑํ๋ผ." |
|
|
|
def get_prompt_template_4(): |
|
"""ํ๋กฌํํธ ํ
ํ๋ฆฟ 4 - ๊ธ์์ง์ฐ๊ธฐ""" |
|
return "(#1 ์ด๋ฏธ์ง)์ ์๋ ์ค๊ตญ์ด๋ฅผ ๋ชจ๋ ์ ๊ฑฐํ๋ผ." |
|
|
|
def get_prompt_template_5(): |
|
"""ํ๋กฌํํธ ํ
ํ๋ฆฟ 5 - ๊ธ์๋ณ๊ฒฝ""" |
|
return '(#1์ ํ
์คํธ)๋ฅผ ์คํ์ผ์ ์ ์งํ์ฒด ํ
์คํธ๋ง "Hello"๋ก ๋ฐ๊ฟ๋ผ' |
|
|
|
def get_prompt_template_6(): |
|
"""ํ๋กฌํํธ ํ
ํ๋ฆฟ 6 - ์ํ์ฐฉ์ฉ-1""" |
|
return "(#1์ ์ฌ์ฑ๋ชจ๋ธ)์ด ์ ์ฒด ๋น์จ๊ณผ ํฌ์ฆ๋ ์ ์งํ ์ฒด (#2์ ์ ๊ธ๋ผ์ค)์ (#3์ ์ฒญ๋ฐ์ง)๋ฅผ ์ง์ ๋ชจ๋ธ์ด ์ฐฉ์ฉํ๊ฒ ์ฒ๋ผ ์์ฐ์ค๋ฝ๊ฒ ๋ชจ์ต์ ์์ฑํ๋ผ." |
|
|
|
def get_prompt_template_7(): |
|
"""ํ๋กฌํํธ ํ
ํ๋ฆฟ 7 - ์ํ์ฐฉ์ฉ-2""" |
|
return "(#1์ ์ฌ์ฑ๋ชจ๋ธ)์ด (#2์ ์ ๊ธ๋ผ์ค)์ ์ฐฉ์ฉํ๊ณ (#3์ ๋ท๋ฐฐ๊ฒฝ์ ์นดํ์ ์ฒด๊ฐ ๋ณด์ด๋ฉฐ) ์์์ ์์ ์๋ ๋ชจ์ต์ ์์ฑํ๋ผ." |
|
|
|
def get_prompt_template_8(): |
|
"""ํ๋กฌํํธ ํ
ํ๋ฆฟ 8 - ์ํ๋ค๊ณ """ |
|
return "(#1์ ์ฌ์ฑ๋ชจ๋ธ)์ด(#2์ ์์ธ์)์ ๋ค๊ณ ์๋ ์์ฐ์ค๋ฌ์ด ๋ชจ์ต์ ์์ฑํ๋ผ." |
|
|
|
def get_prompt_template_9(): |
|
"""ํ๋กฌํํธ ํ
ํ๋ฆฟ 9 - ๋ฐฐ๊ฒฝ๋ฐ๊พธ๊ธฐ""" |
|
return "(#1์ ์ฌ์ฑ๋ชจ๋ธ)์ด (#2 ์นดํ)์์ ์์ฐ์ค๋ฝ๊ฒ ์๋ ๋ชจ์ต์ ์์ฑํ๋ผ." |
|
|
|
def get_prompt_template_10(): |
|
"""ํ๋กฌํํธ ํ
ํ๋ฆฟ 10 - ๋ถ๋ถ์ง์ฐ๊ธฐ""" |
|
return "(#1์ ๋ ๊ณ ๋ชจํ)์์ ์ฒญ์์์ด๋ ๊ณ ๋ฅผ ์ ๊ฑฐํ ํ, ๊ทธ ์๋ฆฌ๋ฅผ ์ฃผ๋ณ ๋ฐฐ๊ฒฝ๊ณผ ์์ฐ์ค๋ฝ๊ฒ ์ด์ฐ๋ฌ์ง๋๋ก ์ฑ์์ฃผ์ธ์. ๋จ, ์ด๋ฏธ์ง์ ๋ค๋ฅธ ๋ถ๋ถ์ ์ฃผ์ ์์๋ ๋์ผํ๊ฒ ์ ์ง ํด์ผํ๋ค." |
|
|
|
def get_prompt_template_11(): |
|
"""ํ๋กฌํํธ ํ
ํ๋ฆฟ 11 - ์ด๋ฏธ์งํ์ฅ""" |
|
return "(#1 ์ด๋ฏธ์ง)๋ฅผ ์๋ณธ๊ทธ๋๋ก ์ค์์ ๋๊ณ ๋น์จ๋ก ์ ์งํ ์ฒด ์์๋ ๋ฐ ์ข์ฐ๋ก ํฌ๊ฒ ํ์ฅํ๋ผ." |
|
|
|
def get_prompt_template_12(): |
|
"""ํ๋กฌํํธ ํ
ํ๋ฆฟ 12 - ํ๋ ์ดํ
-1""" |
|
return "(#1์
๋ฌ๋)์ ๋ด์ ์ฉ๊ธฐ๋ ๋ฒ๋ฆฌ๊ณ ๋๊ณ ํฐ ์์ ์ ์์ (#1์
๋ฌ๋)์์๋ง ๊ฐ๋ ์ฑ์์ ์์
์ ์ธ ๊ฐ๋๋ก ์ด์ธ๋ฆฌ๋ ์ํ๊ณผ ํจ๊ป ํ๋ ์ดํ
ํ ๋ชจ์ต์ ์ด๋ฏธ์ง๋ก ์์ฑํ๋ผ. " |
|
|
|
def get_prompt_template_13(): |
|
"""ํ๋กฌํํธ ํ
ํ๋ฆฟ 13 - ํ๋ ์ดํ
-2""" |
|
return "(#2 ํ๋ ์ดํ
ํ ์ด๋ฏธ์ง)์ ๋ด๊ธด ์์์ (#1 ์๋ฌ๋)๋ก ๋ฐ๊พธ๊ณ ๋๋จธ์ง๋ ์๋๋ฅผ ์ ์งํ ์ฒด ์ด๋ฏธ์ง๋ฅผ ์์ฑํ๋ผ." |
|
|
|
def get_prompt_template_14(): |
|
"""ํ๋กฌํํธ ํ
ํ๋ฆฟ 14 - ํ๋ ์ดํ
-3""" |
|
return "(#1์ปต)์ ๋ธ๊ธฐ, ๋ฐ๋๋ผ, ์ด์ฝ ์์ด์คํฌ๋ฆผ์ ๋ด๊ณ ๊ทธ ์์ ์ด์ฝ ์๋ฝ์ด ํ๋ฅด๊ฒ ์ด๋ฏธ์ง๋ฅผ ์์ฑํ๋ผ." |
|
|
|
|
|
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_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 = "" |
|
|
|
|
|
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.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=get_prompt_template_1, outputs=prompt_input) |
|
image_change_btn2.click(fn=get_prompt_template_2, outputs=prompt_input) |
|
image_change_btn3.click(fn=get_prompt_template_3, outputs=prompt_input) |
|
text_remove_btn.click(fn=get_prompt_template_4, outputs=prompt_input) |
|
text_change_btn.click(fn=get_prompt_template_5, outputs=prompt_input) |
|
clothes_change_btn1.click(fn=get_prompt_template_6, outputs=prompt_input) |
|
clothes_change_btn2.click(fn=get_prompt_template_7, outputs=prompt_input) |
|
holding_product_btn.click(fn=get_prompt_template_8, outputs=prompt_input) |
|
background_change_btn.click(fn=get_prompt_template_9, outputs=prompt_input) |
|
composite_product_btn.click(fn=get_prompt_template_10, outputs=prompt_input) |
|
outpainting_btn.click(fn=get_prompt_template_11, outputs=prompt_input) |
|
food_btn_1.click(fn=get_prompt_template_12, outputs=prompt_input) |
|
food_btn_2.click(fn=get_prompt_template_13, outputs=prompt_input) |
|
food_btn_3.click(fn=get_prompt_template_14, outputs=prompt_input) |
|
|
|
|
|
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_text, prompt_display], |
|
) |
|
|
|
|
|
demo.queue() |
|
demo.launch(share=False, inbrowser=True, width="100%") |