gg235d / src /app.py
ssboost's picture
Update src/app.py
265c64d verified
raw
history blame
32.2 kB
# app.py
import gradio as gr
import pillow_heif
import spaces
import torch
from PIL import Image, ImageEnhance, ImageFilter # ImageFilter ์ถ”๊ฐ€
from refiners.fluxion.utils import manual_seed, no_grad
from utils import load_ic_light, resize_modulo_8
import zipfile
from io import BytesIO
import tempfile
import cv2 # OpenCV ์ถ”๊ฐ€
import numpy as np # NumPy ์ถ”๊ฐ€
# HEIF/AVIF ์ด๋ฏธ์ง€ ์ง€์› ๋“ฑ๋ก
pillow_heif.register_heif_opener()
pillow_heif.register_avif_opener()
# GPU ์„ค์ •
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
DTYPE = torch.float16 if torch.cuda.is_available() and torch.cuda.is_bf16_supported() else torch.float32
ic_light = load_ic_light(device=DEVICE, dtype=DTYPE)
# ๋ชจ๋ธ์„ ์ง€์ •๋œ ๋””๋ฐ”์ด์Šค๋กœ ์ด๋™
ic_light.to(device=DEVICE, dtype=DTYPE)
ic_light.device = DEVICE
ic_light.dtype = DTYPE
ic_light.solver = ic_light.solver.to(device=DEVICE, dtype=DTYPE)
# ๋ฒˆ์—ญ ๊ด€๋ จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฐ ์„ค์ •
import os
from openai import OpenAI
import uuid
import time
import logging
import random
from datetime import datetime
# ๋กœ๊น… ์„ค์ •
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(message)s'
)
logger = logging.getLogger(__name__)
# API ํ‚ค ์„ค์ •
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
# API ํด๋ผ์ด์–ธํŠธ ์„ค์ •
openai_client = OpenAI(api_key=OPENAI_API_KEY)
def validate_input(text: str) -> bool:
"""์ž…๋ ฅ ํ…์ŠคํŠธ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ"""
if not text.strip():
return False
if not any('\u3131' <= char <= '\u318F' or '\uAC00' <= char <= '\uD7A3' for char in text):
return False
return True
def translate_to_english(input_text: str):
"""ํ•œ๊ตญ์–ด๋ฅผ ์˜์–ด๋กœ ๋ฒˆ์—ญํ•˜๋Š” ํ•จ์ˆ˜ (GPT-4-mini ์‚ฌ์šฉ)"""
logger.info("GPT-4-mini ๋ฒˆ์—ญ ์‹œ์ž‘")
if not validate_input(input_text):
logger.info("์œ ํšจํ•˜์ง€ ์•Š์€ ์ž…๋ ฅ์ž…๋‹ˆ๋‹ค.")
return "์œ ํšจํ•˜์ง€ ์•Š์€ ์ž…๋ ฅ์ž…๋‹ˆ๋‹ค. ํ•œ๊ธ€์„ ํฌํ•จํ•œ ํ…์ŠคํŠธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."
try:
# ํ˜„์žฌ ์‹œ๊ฐ„์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋žœ๋ค ์‹œ๋“œ ์„ค์ •
current_time = int(time.time() * 1000)
random.seed(current_time)
# ์˜จ๋„(temperature)์™€ top_p ๊ฐ’์„ ๋งค๋ฒˆ ๋žœ๋คํ•˜๊ฒŒ ์„ค์ •
temperature = random.uniform(0.4, 0.85)
top_p = random.uniform(0.9, 0.98)
request_id = str(uuid.uuid4())[:8]
timestamp_micro = int(time.time() * 1000000) % 1000
system_msg = f"REQ-{request_id}-{timestamp_micro}"
# ์‹œ๊ฐ„์— ๋”ฐ๋ผ ๋ณ€ํ™”ํ•˜๋Š” ํ”„๋กฌํ”„ํŠธ ์ˆ˜์‹์–ด ์ถ”๊ฐ€
current_hour = datetime.now().hour
time_context = f"Consider the current time is {current_hour}:00. "
response = openai_client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
"role": "system",
"content": system_msg
},
{
"role": "user",
"content": f"""
1. Translate the input Korean text into English with these rules:
2. Output ONLY the English translation.
3. Create a coherent, brief scene using provided words/phrases.
4. Stay strictly within the scope of input words.
5. Maintain flow and natural transitions.
6. Keep scene descriptions concise but complete.
7. Do not add external elements.
8. Focus on core scene elements.
9. Preserve original meaning and intent.
10. No explanations or meta-commentary.
11. No formatting beyond basic text.
12. Just the direct English translation.
Additional guidance:
13. Use input words harmoniously.
14. Create clear mental imagery.
15. Keep scene contained and focused.
16. Ensure logical connections.
17. Maintain narrative flow.
Input: {input_text} [Seed: {current_time}]
"""
}
],
max_tokens=100,
temperature=temperature,
top_p=top_p,
seed=current_time
)
translated = response.choices[0].message.content.strip()
logger.info("GPT-4-mini ๋ฒˆ์—ญ ์™„๋ฃŒ")
return translated
except Exception as e:
logger.error(f"๋ฒˆ์—ญ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
return f"๋ฒˆ์—ญ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {str(e)}"
@spaces.GPU(duration=120) # GPU ์‚ฌ์šฉ์„ ๋ช…์‹œํ•˜๋Š” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ
@no_grad()
def generate_images(
image: Image.Image,
prompt: str,
) -> tuple[Image.Image, Image.Image, Image.Image, Image.Image]:
assert image.mode == "RGBA", "์ž…๋ ฅ ์ด๋ฏธ์ง€๋Š” RGBA ๋ชจ๋“œ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."
# ์‹œ๋“œ ๋žœ๋ค ์„ค์ •
seed = torch.seed() # ๋žœ๋ค ์‹œ๋“œ ์ƒ์„ฑ
manual_seed(seed)
# ๋„ค๊ฑฐํ‹ฐ๋ธŒ ํ”„๋กฌํ”„ํŠธ ๊ณ ์ •
negative_prompt = "dirty, messy, worst quality, low quality, watermark, signature, jpeg artifacts, deformed, monochrome, black and white"
# ๊ธฐ๋ณธ๊ฐ’ ์„ค์ •
condition_scale = 1.25
num_inference_steps = 25
strength_first_pass = 0.9
strength_second_pass = 0.5
# ์ด๋ฏธ์ง€ ํฌ๊ธฐ ์กฐ์ •
image = resize_modulo_8(image, 768)
# RGB์™€ ์•ŒํŒŒ ์ฑ„๋„ ๋ถ„๋ฆฌ
mask = image.getchannel("A")
image_rgb = image.convert("RGB")
# ํ…์ŠคํŠธ ์ž„๋ฒ ๋”ฉ ๊ณ„์‚ฐ
clip_text_embedding = ic_light.compute_clip_text_embedding(text=prompt, negative_text=negative_prompt)
ic_light.set_ic_light_condition(image=image_rgb, mask=mask)
# ์กฐ๋ช… ์„ ํ˜ธ๋„ ๊ธฐ๋ณธ๊ฐ’ ์„ค์ •
light_pref_image = None # "None"์œผ๋กœ ๊ณ ์ •
if light_pref_image is None:
x = torch.randn_like(ic_light._ic_light_condition) # pyright: ignore[reportPrivateUsage]
strength_first_pass = 1.0
else:
x = ic_light.lda.image_to_latents(light_pref_image)
x = ic_light.solver.add_noise(x, noise=torch.randn_like(x), step=0)
# ์ฒซ ๋ฒˆ์งธ ํŒจ์Šค ์„ค์ •
num_steps = int(round(num_inference_steps / strength_first_pass))
first_step = int(num_steps * (1 - strength_first_pass))
ic_light.set_inference_steps(num_steps, first_step)
# ์ฒซ ๋ฒˆ์งธ ํŒจ์Šค
for step in ic_light.steps:
x = ic_light(
x,
step=step,
clip_text_embedding=clip_text_embedding,
condition_scale=condition_scale,
)
# ๋‘ ๋ฒˆ์งธ ํŒจ์Šค ์„ค์ •
num_steps = int(round(num_inference_steps / strength_second_pass))
first_step = int(num_steps * (1 - strength_second_pass))
ic_light.set_inference_steps(num_steps, first_step)
# ์ดˆ๊ธฐํ™”๋œ ๋ผํ…ํŠธ
x = ic_light.solver.add_noise(x, noise=torch.randn_like(x), step=first_step)
# ๋‘ ๋ฒˆ์งธ ํŒจ์Šค
for step in ic_light.steps:
x = ic_light(
x,
step=step,
clip_text_embedding=clip_text_embedding,
condition_scale=condition_scale,
)
# ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ์ƒ์„ฑ
bg_image = ic_light.lda.latents_to_image(x)
# ์ตœ์ข… ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ (์ „๊ฒฝ์€ ์กฐ์ • ์—†์ด ํ•ฉ์„ฑ)
result = Image.composite(image_rgb, bg_image, mask)
return image_rgb, bg_image, mask, result # image_rgb ์ถ”๊ฐ€
@no_grad()
def adjust_final_image(
bg_image: Image.Image,
mask: Image.Image,
bg_brightness: float,
bg_contrast: float,
bg_saturation: float,
bg_temperature: float, # ์ƒ‰์˜จ๋„ ํŒŒ๋ผ๋ฏธํ„ฐ
bg_vibrance: float,
bg_color_mixer_blues: float,
bg_shadows: float, # Shadows ํŒŒ๋ผ๋ฏธํ„ฐ
) -> Image.Image:
"""๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€๋ฅผ ์กฐ์ •ํ•˜๋Š” ํ•จ์ˆ˜"""
print("Adjusting Final Image with the following parameters:")
print(f"Background - Brightness: {bg_brightness}, Contrast: {bg_contrast}, "
f"Saturation: {bg_saturation}, Temperature: {bg_temperature}, "
f"Vibrance: {bg_vibrance}, Blues: {bg_color_mixer_blues}, Shadows: {bg_shadows}")
# ๋ฐ๊ธฐ ์กฐ์ •
enhancer = ImageEnhance.Brightness(bg_image)
bg_image = enhancer.enhance(bg_brightness)
# ๋Œ€๋น„ ์กฐ์ •
enhancer = ImageEnhance.Contrast(bg_image)
bg_image = enhancer.enhance(bg_contrast)
# ์ฑ„๋„ ์กฐ์ •
enhancer = ImageEnhance.Color(bg_image)
bg_image = enhancer.enhance(bg_saturation)
# ์ƒ‰์˜จ๋„ ์กฐ์ •
if bg_temperature != 0.0:
# PIL ์ด๋ฏธ์ง€๋ฅผ OpenCV ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜
cv_image = cv2.cvtColor(np.array(bg_image), cv2.COLOR_RGB2BGR).astype(np.float32)
# ์ƒ‰์˜จ๋„ ์กฐ์ • ๋กœ์ง
value = float(bg_temperature) * 30 # ์ƒ‰์˜จ๋„ ํšจ๊ณผ ์Šค์ผ€์ผ ์กฐ์ ˆ
if value > 0: # ๋”ฐ๋œปํ•˜๊ฒŒ
cv_image[:, :, 2] = cv_image[:, :, 2] + value # Red ์ฑ„๋„ ์ฆ๊ฐ€
cv_image[:, :, 0] = cv_image[:, :, 0] - value # Blue ์ฑ„๋„ ๊ฐ์†Œ
else: # ์ฐจ๊ฐ‘๊ฒŒ
cv_image[:, :, 2] = cv_image[:, :, 2] + value # Red ์ฑ„๋„ ๊ฐ์†Œ
cv_image[:, :, 0] = cv_image[:, :, 0] - value # Blue ์ฑ„๋„ ์ฆ๊ฐ€
# ์ฑ„๋„ ๊ฐ’ ํด๋ฆฌํ•‘
cv_image[:, :, 2] = np.clip(cv_image[:, :, 2], 0, 255)
cv_image[:, :, 0] = np.clip(cv_image[:, :, 0], 0, 255)
# ๋‹ค์‹œ PIL ์ด๋ฏธ์ง€๋กœ ๋ณ€ํ™˜
bg_image = Image.fromarray(cv2.cvtColor(cv_image.astype(np.uint8), cv2.COLOR_BGR2RGB))
# ํ™œ๊ธฐ(Vibrance) ์กฐ์ •: ์ฑ„๋„๋ฅผ ๋น„๋ก€์ ์œผ๋กœ ์ฆ๊ฐ€/๊ฐ์†Œ
if bg_vibrance != 0.0:
# Vibrance๋Š” ์ผ๋ฐ˜์ ์ธ ์ฑ„๋„๋ณด๋‹ค ๋œ ๋ฏผ๊ฐํ•˜๊ฒŒ ์ฑ„๋„๋ฅผ ์กฐ์ ˆ
converter = ImageEnhance.Color(bg_image)
factor = 1 + (bg_vibrance / 100.0)
bg_image = converter.enhance(factor)
# ์ปฌ๋Ÿฌ ๋ฏน์„œ ๋ธ”๋ฃจ ์กฐ์ •: ํŒŒ๋ž‘ ์ฑ„๋„์„ ๊ฐœ๋ณ„์ ์œผ๋กœ ์กฐ์ •
if bg_color_mixer_blues != 0.0:
r, g, b = bg_image.split()
b = b.point(lambda p: min(255, max(0, p + bg_color_mixer_blues)))
bg_image = Image.merge("RGB", (r, g, b))
# Shadows ์กฐ์ •: ์–ด๋‘์šด ์˜์—ญ์„ ์กฐ์ •ํ•˜๊ณ  ๊ฐ€์žฅ์ž๋ฆฌ๋ฅผ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์ฒ˜๋ฆฌ
if bg_shadows != 0.0:
# ์–ด๋‘์šด ์˜์—ญ์„ ์‹๋ณ„ํ•˜๊ธฐ ์œ„ํ•ด ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ๋ณ€ํ™˜
grayscale = bg_image.convert("L")
# ์–ด๋‘์šด ์˜์—ญ ๋งˆ์Šคํฌ ์ƒ์„ฑ (์ž„๊ณ„๊ฐ’ 128 ์ดํ•˜)
mask_dark = grayscale.point(lambda p: p < 128 and 255)
mask_dark = mask_dark.convert("L") # ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ๋กœ ๋ณ€ํ™˜
# ๋งˆ์Šคํฌ ๊ฐ€์žฅ์ž๋ฆฌ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์ฒ˜๋ฆฌ (Gaussian Blur ์ ์šฉ)
mask_dark = mask_dark.filter(ImageFilter.GaussianBlur(radius=10)) # ๋ธ”๋Ÿฌ ๋ฐ˜๊ฒฝ ์กฐ์ ˆ ๊ฐ€๋Šฅ
# ๊ทธ๋ฆผ์ž ์กฐ์ •์„ ์œ„ํ•œ ๋ฐ๊ธฐ ์กฐ์ •
if bg_shadows < 0:
# ์–ด๋‘์šด ์˜์—ญ์„ ๋” ์–ด๋‘ก๊ฒŒ
factor = 1 + (bg_shadows / 100.0) # ์˜ˆ: -50 -> 0.5
enhancer = ImageEnhance.Brightness(bg_image)
dark_adjusted = enhancer.enhance(factor)
else:
# ์–ด๋‘์šด ์˜์—ญ์„ ๋ฐ๊ฒŒ
factor = 1 + (bg_shadows / 100.0) # ์˜ˆ: 50 -> 1.5
enhancer = ImageEnhance.Brightness(bg_image)
dark_adjusted = enhancer.enhance(factor)
# ๋งˆ์Šคํฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์–ด๋‘์šด ์˜์—ญ๋งŒ ์กฐ์ •๋œ ์ด๋ฏธ์ง€์™€ ํ•ฉ์„ฑ
bg_image = Image.composite(dark_adjusted, bg_image, mask_dark)
# ์กฐ์ •๋œ ๋ฐฐ๊ฒฝ ๋ฐ˜ํ™˜
return bg_image
def download_all(original_image, bg_image, final_image):
"""
๋ชจ๋“  ์ด๋ฏธ์ง€๋ฅผ ZIP ํŒŒ์ผ์œผ๋กœ ์••์ถ•ํ•˜์—ฌ ๋‹ค์šด๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
"""
if original_image is None or bg_image is None or final_image is None:
print("Download function received None input.")
return None
print("Preparing images for download...")
# Convert images to RGB if they are not
if original_image.mode != "RGB":
original_image = original_image.convert("RGB")
if bg_image.mode != "RGB":
bg_image = bg_image.convert("RGB")
if final_image.mode != "RGB":
final_image = final_image.convert("RGB")
# ZIP ํŒŒ์ผ ์ƒ์„ฑ
with tempfile.NamedTemporaryFile(delete=False, suffix=".zip") as tmp_file:
with zipfile.ZipFile(tmp_file, 'w') as zip_file:
# ์›๋ณธ ์ด๋ฏธ์ง€ ์ €์žฅ
original_buffer = BytesIO()
original_image.save(original_buffer, format="JPEG")
zip_file.writestr("original_image.jpg", original_buffer.getvalue())
# ์ƒ์„ฑ๋œ ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ์ €์žฅ
bg_buffer = BytesIO()
bg_image.save(bg_buffer, format="JPEG")
zip_file.writestr("background_image.jpg", bg_buffer.getvalue())
# ์ตœ์ข… ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€ ์ €์žฅ
final_buffer = BytesIO()
final_image.save(final_buffer, format="JPEG")
zip_file.writestr("final_image.jpg", final_buffer.getvalue())
zip_path = tmp_file.name
print(f"ZIP file created at {zip_path}")
return zip_path
def reset_sliders(initial_result):
"""
์Šฌ๋ผ์ด๋”๋ฅผ "ํ•„ํ„ฐ์—†์Œ"์˜ ์ดˆ๊ธฐ๊ฐ’์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  ์ตœ์ข… ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€๋ฅผ ์ดˆ๊ธฐ ์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€๋กœ ๋ณต์›ํ•˜๋Š” ํ•จ์ˆ˜.
"""
print("Resetting sliders to default values.")
return (
"ํ•„ํ„ฐ์—†์Œ", # filter_radio
gr.update(value=1.0), # ๋ฐ๊ธฐ
gr.update(value=1.0), # ๋Œ€๋น„
gr.update(value=1.0), # ์ฑ„๋„
gr.update(value=0.0), # ์ƒ‰์˜จ๋„
gr.update(value=0.0), # ํ™œ๊ธฐ
gr.update(value=0.0), # ์ปฌ๋Ÿฌ ๋ฏน์„œ (๋ธ”๋ฃจ)
gr.update(value=0.0), # ๊ทธ๋ฆผ์ž
initial_result, # ํ•„ํ„ฐ ์ ์šฉ ์ด๋ฏธ์ง€
)
# Gradio ์ธํ„ฐํŽ˜์ด์Šค ์„ค์ •
def create_interface():
css = """
/* ํ”„๋กฌํ”„ํŠธ ํ…์ŠคํŠธ๋ฐ•์Šค ๋ผ๋ฒจ๊ณผ ์ž…๋ ฅ ํ…์ŠคํŠธ๋งŒ ๊ฒ€์€์ƒ‰์œผ๋กœ ์„ค์ • */
.gr-textbox > label {
color: #000 !important;
}
.gr-textbox input {
color: #000 !important;
}
/* ํ•„ํ„ฐ ์„ ํƒ ๋ผ๋””์˜ค ๋ฒ„ํŠผ ๋ผ๋ฒจ๋งŒ ๊ฒ€์€์ƒ‰์œผ๋กœ ์„ค์ • */
.gradio-radio > label {
color: #000 !important;
}
/* ์Šฌ๋ผ์ด๋” ๋ผ๋ฒจ๊ณผ ํ˜„์žฌ ๊ฐ’ ํ…์ŠคํŠธ๋งŒ ๊ฒ€์€์ƒ‰์œผ๋กœ ์„ค์ • */
.gr-slider > label {
color: #000 !important;
}
.gr-slider .slider-value {
color: #000 !important;
}
/* ์„ ํƒ๋œ ๋ผ๋””์˜ค ๋ฒ„ํŠผ ํ…์ŠคํŠธ๋ฅผ ๊ฒ€์€์ƒ‰์œผ๋กœ ์œ ์ง€ํ•˜๊ณ  ๋ฐฐ๊ฒฝ์ƒ‰์„ ํฐ์ƒ‰์œผ๋กœ ์„ค์ • */
.gradio-radio label[aria-checked="true"] {
color: #000 !important;
background-color: #fff !important; /* ํฐ์ƒ‰ ๋ฐฐ๊ฒฝ */
padding: 4px 8px;
border-radius: 4px;
border: 1px solid #000 !important; /* ๊ฒ€์€์ƒ‰ ํ…Œ๋‘๋ฆฌ ์ถ”๊ฐ€ */
}
/* ์„ ํƒ๋˜์ง€ ์•Š์€ ๋ผ๋””์˜ค ๋ฒ„ํŠผ ํ…์ŠคํŠธ๋Š” ๊ฒ€์€์ƒ‰ ์œ ์ง€ */
.gradio-radio label[aria-checked="false"] {
color: #000 !important;
background-color: #fff !important; /* ํฐ์ƒ‰ ๋ฐฐ๊ฒฝ ์œ ์ง€ */
border: 1px solid #000 !important; /* ๊ฒ€์€์ƒ‰ ํ…Œ๋‘๋ฆฌ ์ถ”๊ฐ€ */
}
/* ๋ผ๋””์˜ค ๋ฒ„ํŠผ์˜ ์„ ํƒ ํ‘œ์‹œ ์  ์ˆจ๊ธฐ๊ธฐ */
.gradio-radio input[type="radio"] + label::before {
display: none;
}
/* ๋ผ๋””์˜ค ๋ฒ„ํŠผ ์„ ํƒ ์‹œ ์„ ํƒ ํ‘œ์‹œ ์ ์„ ์ˆจ๊ธฐ๊ธฐ */
.gradio-radio input[type="radio"]:checked + label::after {
content: "";
display: none;
}
/* ๋ฒ„ํŠผ ํ…์ŠคํŠธ ์ƒ‰์ƒ๋„ ๊ฒ€์€์ƒ‰์œผ๋กœ ๋ณ€๊ฒฝ */
.btn-primary, .btn-secondary, .gr-button {
color: #000 !important;
}
/* ๋‹ค์šด๋กœ๋“œ ๋ฒ„ํŠผ ํ…์ŠคํŠธ ์ƒ‰์ƒ ๊ฒ€์€์ƒ‰์œผ๋กœ ๋ณ€๊ฒฝ */
.gr-button.download-button {
color: #000 !important;
}
/* ๊ธฐํƒ€ ํ…์ŠคํŠธ ์š”์†Œ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋„๋ก ์ฃผ์˜ */
/* ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์ถ”๊ฐ€์ ์ธ ์„ ํƒ์ž๋ฅผ ์—ฌ๊ธฐ์— ์ž‘์„ฑ */
"""
# ํ•„ํ„ฐ ํ”„๋ฆฌ์…‹ ์ •์˜
filters = [
{
"name": "ํ•„ํ„ฐ์—†์Œ",
"bg_brightness": 1.0,
"bg_contrast": 1.0,
"bg_saturation": 1.0,
"bg_temperature": 0.0,
"bg_vibrance": 0.0,
"bg_color_mixer_blues": 0.0,
"bg_shadows": 0.0
},
{
"name": "๊นจ๋—ํ•œ",
"bg_brightness": 1.3,
"bg_contrast": 1.2,
"bg_saturation": 1.0,
"bg_temperature": -0.2,
"bg_vibrance": -20.0,
"bg_color_mixer_blues": 5.0,
"bg_shadows": -10.0
},
{
"name": "๋”ฐ์Šคํ•œ",
"bg_brightness": 1.3,
"bg_contrast": 1.2,
"bg_saturation": 0.8,
"bg_temperature": 0.0,
"bg_vibrance": -10.0,
"bg_color_mixer_blues": 2.0,
"bg_shadows": -10.0
},
{
"name": "ํ‘ธ๋ฅธ๋น›",
"bg_brightness": 1.3,
"bg_contrast": 1.0,
"bg_saturation": 1.0,
"bg_temperature": 0.0,
"bg_vibrance": 0.0,
"bg_color_mixer_blues": 10.0,
"bg_shadows": 5.0
}
]
# ๊ธฐ๋ณธ ํ•„ํ„ฐ ์„ค์ • (๊นจ๋—ํ•œ)
default_filter = next(filter for filter in filters if filter["name"] == "๊นจ๋—ํ•œ")
with gr.Blocks(theme=gr.themes.Soft(
primary_hue=gr.themes.Color(
c50="#FFF7ED", # ๊ฐ€์žฅ ๋ฐ์€ ์ฃผํ™ฉ
c100="#FFEDD5",
c200="#FED7AA",
c300="#FDBA74",
c400="#FB923C",
c500="#F97316", # ๊ธฐ๋ณธ ์ฃผํ™ฉ
c600="#EA580C",
c700="#C2410C",
c800="#9A3412",
c900="#7C2D12", # ๊ฐ€์žฅ ์–ด๋‘์šด ์ฃผํ™ฉ
c950="#431407",
),
secondary_hue="zinc", # ๋ชจ๋˜ํ•œ ๋Š๋‚Œ์˜ ํšŒ์ƒ‰ ๊ณ„์—ด
neutral_hue="zinc",
font=("Pretendard", "sans-serif")
), css=css) as demo:
with gr.Row():
# ์™ผ์ชฝ ์—ด: ์ž…๋ ฅ๊ณผ ์„ค์ •
with gr.Column(scale=1):
input_image = gr.Image(
label="์ด๋ฏธ์ง€ ์ถ”๊ฐ€",
image_mode="RGBA",
type="pil",
)
prompt = gr.Textbox(
label="ํ”„๋กฌํ”„ํŠธ (ํ•œ๊ตญ์–ด)",
placeholder="๋ˆˆ๋ฎ์ธ ํžˆ๋ง๋ผ์•ผ, ๋ญ‰๊ฒŒ๊ตฌ๋ฆ„, ํฐ์ƒ‰ ๋ฐ”๋‹ฅ์— ๋†“์—ฌ์žˆ๋Š” ์–‘๋ง",
)
run_button = gr.Button(
value="์ด๋ฏธ์ง€ ์ƒ์„ฑ",
variant="primary",
size="lg"
)
reset_button = gr.Button(
value="ํ•„ํ„ฐ ์ดˆ๊ธฐํ™”",
variant="secondary",
size="lg"
)
# ํ•„ํ„ฐ ์„ ํƒ ๋ผ๋””์˜ค ๋ฒ„ํŠผ ์ถ”๊ฐ€
filter_radio = gr.Radio(
label="ํ•„ํ„ฐ ์„ ํƒ",
choices=["ํ•„ํ„ฐ์—†์Œ", "๊นจ๋—ํ•œ", "๋”ฐ์Šคํ•œ", "ํ‘ธ๋ฅธ๋น›"],
value="๊นจ๋—ํ•œ", # ๊ธฐ๋ณธ ์„ ํƒ์€ ๊นจ๋—ํ•œ
type="value",
)
# ์˜ˆ์‹œ ์ถ”๊ฐ€
examples = [
["img/1.png", "๋ˆˆ, ํžˆ๋ง๋ผ์•ผ", "ํ‘ธ๋ฅธ๋น›"],
["img/2.png", "ํฐ์ƒ‰ ๋ฒฝ์ง€์— ๊ธฐ๋Œ€์–ด ์žˆ๋Š” ์‚ฌ๋žŒ", "๊นจ๋—ํ•œ"],
["img/3.png", "๋‹จ์ˆœํ•œ ์ฑ…์ƒ์œ„์— ๋†“์ธ ๋ฏธ๋‹ˆ ํŒŒ์šฐ์น˜", "๋”ฐ์Šคํ•œ"],
]
gr.Examples(
examples=examples,
inputs=[input_image, prompt, filter_radio],
fn=lambda img, prm, flt: on_generate(img, prm, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0) if flt == "ํ•„ํ„ฐ์—†์Œ" else on_generate(img, prm,
next(filter for filter in filters if filter["name"] == flt)["bg_brightness"],
next(filter for filter in filters if filter["name"] == flt)["bg_contrast"],
next(filter for filter in filters if filter["name"] == flt)["bg_saturation"],
next(filter for filter in filters if filter["name"] == flt)["bg_temperature"],
next(filter for filter in filters if filter["name"] == flt)["bg_vibrance"],
next(filter for filter in filters if filter["name"] == flt)["bg_color_mixer_blues"],
next(filter for filter in filters if filter["name"] == flt)["bg_shadows"],
),
outputs=[
"background_output",
"mask_state",
"final_output",
"image_rgb_state",
"initial_result",
],
label="์˜ˆ์‹œ ์„ ํƒ",
cache_examples=False,
)
# ๋ฐฐ๊ฒฝ ์กฐ์ • ์˜ต์…˜ ์ˆœ์„œ ๋ณ€๊ฒฝ (๋ผ๋ฒจ์—์„œ "๋ฐฐ๊ฒฝ" ์ œ๊ฑฐ)
bg_brightness = gr.Slider(label="๋ฐ๊ธฐ", minimum=0.1, maximum=5.0, value=default_filter["bg_brightness"], step=0.1)
bg_contrast = gr.Slider(label="๋Œ€๋น„", minimum=0.1, maximum=5.0, value=default_filter["bg_contrast"], step=0.1)
bg_saturation = gr.Slider(label="์ฑ„๋„", minimum=0.0, maximum=5.0, value=default_filter["bg_saturation"], step=0.1)
bg_temperature = gr.Slider( # ์ƒ‰์˜จ๋„ ์Šฌ๋ผ์ด๋”
label="์ƒ‰์˜จ๋„",
minimum=-1.0,
maximum=1.0,
value=default_filter["bg_temperature"],
step=0.1
)
bg_vibrance = gr.Slider(label="ํ™œ๊ธฐ", minimum=-100.0, maximum=100.0, value=default_filter["bg_vibrance"], step=1.0)
bg_color_mixer_blues = gr.Slider(label="์ปฌ๋Ÿฌ ๋ฏน์„œ (๋ธ”๋ฃจ)", minimum=-100.0, maximum=100.0, value=default_filter["bg_color_mixer_blues"], step=1.0)
bg_shadows = gr.Slider(label="๊ทธ๋ฆผ์ž", minimum=-100.0, maximum=100.0, value=default_filter["bg_shadows"], step=1.0) # Shadows ์Šฌ๋ผ์ด๋”
# ์˜ค๋ฅธ์ชฝ ์—ด: ์ถœ๋ ฅ๊ณผ ๋‹ค์šด๋กœ๋“œ ๋ฒ„ํŠผ
with gr.Column(scale=2):
background_output = gr.Image(
label="์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€",
type="pil",
interactive=False # ์ถœ๋ ฅ ์ „์šฉ์œผ๋กœ ์„ค์ •
)
final_output = gr.Image(
label="ํ•„ํ„ฐ ์ ์šฉ ์ด๋ฏธ์ง€",
type="pil",
interactive=False # ์ถœ๋ ฅ ์ „์šฉ์œผ๋กœ ์„ค์ •
)
# "๋ชจ๋“  ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ" ๋ฒ„ํŠผ๊ณผ ๋‹ค์šด๋กœ๋“œ ์ถœ๋ ฅ์ฐฝ์„ ์ค‘์•™์— ๋ฐฐ์น˜
with gr.Row(elem_classes="download-container"):
download_button = gr.Button(
value="๋ชจ๋“  ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ",
elem_classes="download-button",
variant="primary",
size="lg"
)
with gr.Row(elem_classes="download-container"):
download_output = gr.File(label="๋ชจ๋“  ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ", elem_classes="download-output")
# ์ƒํƒœ ์ปดํฌ๋„ŒํŠธ
mask_state = gr.State()
image_rgb_state = gr.State() # image_rgb ์ƒํƒœ ์ถ”๊ฐ€
initial_result = gr.State()
# ํ•„ํ„ฐ ์„ ํƒ ์‹œ ์Šฌ๋ผ์ด๋” ๊ฐ’ ๋ณ€๊ฒฝ ํ•จ์ˆ˜
def apply_filter_preset(selected_filter):
for filter in filters:
if filter["name"] == selected_filter:
return (
gr.update(value=filter["bg_brightness"]),
gr.update(value=filter["bg_contrast"]),
gr.update(value=filter["bg_saturation"]),
gr.update(value=filter["bg_temperature"]),
gr.update(value=filter["bg_vibrance"]),
gr.update(value=filter["bg_color_mixer_blues"]),
gr.update(value=filter["bg_shadows"]),
)
# ๊ธฐ๋ณธ๊ฐ’ ๋ฐ˜ํ™˜ (ํ•„ํ„ฐ์—†์Œ)
return (
gr.update(value=1.0),
gr.update(value=1.0),
gr.update(value=1.0),
gr.update(value=0.0),
gr.update(value=0.0),
gr.update(value=0.0),
gr.update(value=0.0),
)
# ํ•„ํ„ฐ ๋ผ๋””์˜ค ๋ฒ„ํŠผ ์„ ํƒ ์‹œ ์Šฌ๋ผ์ด๋” ๊ฐ’ ์—…๋ฐ์ดํŠธ
filter_radio.change(
fn=apply_filter_preset,
inputs=[filter_radio],
outputs=[
bg_brightness,
bg_contrast,
bg_saturation,
bg_temperature,
bg_vibrance,
bg_color_mixer_blues,
bg_shadows,
],
)
# ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ generate_images ํ•จ์ˆ˜ ํ˜ธ์ถœ ๋ฐ ํ”„๋กฌํ”„ํŠธ ๋ฒˆ์—ญ
def on_generate(image, prompt, bg_brightness, bg_contrast, bg_saturation, bg_temperature, bg_vibrance, bg_color_mixer_blues, bg_shadows):
# ํ”„๋กฌํ”„ํŠธ๊ฐ€ ๋น„์–ด์žˆ๋Š”์ง€ ํ™•์ธ
if not prompt.strip():
logger.info("ํ”„๋กฌํ”„ํŠธ๊ฐ€ ๋น„์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.")
# ๊ธฐ๋ณธ ํ”„๋กฌํ”„ํŠธ ์„ค์ •
final_prompt = "high-quality professional studio photography, Realistic soft white tone bright lighting, HEIC, CR2, NEF"
else:
# ํ•œ๊ตญ์–ด ํ”„๋กฌํ”„ํŠธ๋ฅผ ์˜์–ด๋กœ ๋ฒˆ์—ญ
translated_prompt = translate_to_english(prompt)
if translated_prompt.startswith("์œ ํšจํ•˜์ง€ ์•Š์€ ์ž…๋ ฅ") or translated_prompt.startswith("๋ฒˆ์—ญ ์ค‘ ์˜ค๋ฅ˜"):
return None, None, None, None, None # ๋ฒˆ์—ญ ์‹คํŒจ ์‹œ ์ข…๋ฃŒ
# ๋ฒˆ์—ญ๋œ ํ”„๋กฌํ”„ํŠธ์— ์ž๋™์œผ๋กœ ๋ฌธ์ž์—ด ์ถ”๊ฐ€
final_prompt = f"{translated_prompt}, high-quality professional studio photography, Realistic soft white tone bright lighting, HEIC, CR2, NEF"
image_rgb, bg_img, mask, result = generate_images(image, final_prompt)
print("Image generation completed.")
# ์Šฌ๋ผ์ด๋” ๊ฐ’ ์ ์šฉ
if bg_img is not None and mask is not None and image_rgb is not None:
adjusted_bg = adjust_final_image(
bg_image=bg_img,
mask=mask,
bg_brightness=bg_brightness,
bg_contrast=bg_contrast,
bg_saturation=bg_saturation,
bg_temperature=bg_temperature,
bg_vibrance=bg_vibrance,
bg_color_mixer_blues=bg_color_mixer_blues,
bg_shadows=bg_shadows,
)
final_result = Image.composite(image_rgb, adjusted_bg, mask)
else:
final_result = result
# ์ดˆ๊ธฐ ๊ฒฐ๊ณผ์— final_result ์ €์žฅ
initial_result.value = final_result
return bg_img, mask, final_result, image_rgb, final_result # initial_result ์ถ”๊ฐ€ ๋ฐ˜ํ™˜
run_button.click(
fn=on_generate,
inputs=[
input_image,
prompt,
bg_brightness,
bg_contrast,
bg_saturation,
bg_temperature,
bg_vibrance,
bg_color_mixer_blues,
bg_shadows,
],
outputs=[
background_output,
mask_state,
final_output,
image_rgb_state,
initial_result, # initial_result ์ถ”๊ฐ€ ๋ฐ˜ํ™˜
],
)
def on_adjust(
image_rgb, # image_rgb_state ์‚ฌ์šฉ
bg_image, mask,
bg_brightness, bg_contrast, bg_saturation, bg_temperature, bg_vibrance, bg_color_mixer_blues, bg_shadows,
):
if bg_image is None or mask is None or image_rgb is None:
print("Adjust function received None input.")
return None
print(f"Adjusting background image... Current slider values:")
print(f"Brightness: {bg_brightness}, Contrast: {bg_contrast}, Saturation: {bg_saturation}, "
f"Temperature: {bg_temperature}, Vibrance: {bg_vibrance}, Blues: {bg_color_mixer_blues}, Shadows: {bg_shadows}")
adjusted_bg = adjust_final_image(
bg_image=bg_image,
mask=mask, # mask๋ฅผ ๋ฐ˜๋“œ์‹œ ์ „๋‹ฌ
bg_brightness=bg_brightness,
bg_contrast=bg_contrast,
bg_saturation=bg_saturation,
bg_temperature=bg_temperature,
bg_vibrance=bg_vibrance,
bg_color_mixer_blues=bg_color_mixer_blues,
bg_shadows=bg_shadows, # Shadows ํŒŒ๋ผ๋ฏธํ„ฐ ์ถ”๊ฐ€
)
# ์กฐ์ •๋œ ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ํ™•์ธ
print(f"adjusted_bg size: {adjusted_bg.size}, mode: {adjusted_bg.mode}")
adjusted_bg.save("adjusted_bg_debug.jpg") # ๋””๋ฒ„๊น…์šฉ ์ž„์‹œ ํŒŒ์ผ ์ €์žฅ
# image_rgb์€ ์ด๋ฏธ resize๋œ ์ƒํƒœ์ด๋ฏ€๋กœ ์ถ”๊ฐ€์ ์ธ ๋ณ€ํ™˜ ํ•„์š” ์—†์Œ
input_image_rgb = image_rgb
# ์ด๋ฏธ์ง€ ํฌ๊ธฐ์™€ ๋ชจ๋“œ ํ™•์ธ
print(f"input_image_rgb size: {input_image_rgb.size}, mode: {input_image_rgb.mode}")
print(f"adjusted_bg size: {adjusted_bg.size}, mode: {adjusted_bg.mode}")
print(f"mask size: {mask.size}, mode: {mask.mode}")
# ์ด๋ฏธ์ง€ ํฌ๊ธฐ๊ฐ€ ๋™์ผํ•œ์ง€ ํ™•์ธํ•˜๊ณ , ๋‹ค๋ฅด๋ฉด ์กฐ์ •
if input_image_rgb.size != adjusted_bg.size:
print(f"Resizing input_image_rgb from {input_image_rgb.size} to {adjusted_bg.size}")
input_image_rgb = input_image_rgb.resize(adjusted_bg.size)
# mask๋ฅผ ๋‹จ์ผ ์ฑ„๋„๋กœ ๋ณ€ํ™˜ (ํ•„์š”์‹œ)
mask = mask.convert("L")
try:
final_result = Image.composite(input_image_rgb, adjusted_bg, mask)
except ValueError as e:
print(f"Composite error: {e}")
return None
# ์ตœ์ข… ๊ฒฐ๊ณผ ํ™•์ธ
final_result.save("final_result_debug.jpg") # ๋””๋ฒ„๊น…์šฉ ์ž„์‹œ ํŒŒ์ผ ์ €์žฅ
print("Final image composite completed.")
# ์ดˆ๊ธฐ ๊ฒฐ๊ณผ์— final_result ์ €์žฅ
initial_result.value = final_result
return final_result
bg_sliders = [
bg_brightness,
bg_contrast,
bg_saturation,
bg_temperature,
bg_vibrance,
bg_color_mixer_blues,
bg_shadows, # Shadows ์Šฌ๋ผ์ด๋” ์ถ”๊ฐ€
]
for slider in bg_sliders:
slider.change(
fn=on_adjust,
inputs=[
image_rgb_state, # image_rgb_state ์‚ฌ์šฉ
background_output, # ์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€
mask_state, # ์•ŒํŒŒ ์ฑ„๋„
bg_brightness,
bg_contrast,
bg_saturation,
bg_temperature,
bg_vibrance,
bg_color_mixer_blues,
bg_shadows, # Shadows ํŒŒ๋ผ๋ฏธํ„ฐ ์ถ”๊ฐ€
],
outputs=final_output, # ์ตœ์ข… ๊ฒฐ๊ณผ๋Š” ํ•ฉ์„ฑ๋œ ์ด๋ฏธ์ง€
)
# ๋ฆฌ์…‹ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ๋ชจ๋“  ์Šฌ๋ผ์ด๋”๋ฅผ "ํ•„ํ„ฐ์—†์Œ"์˜ ์ดˆ๊ธฐ๊ฐ’์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  ํ•„ํ„ฐ ์„ ํƒ์„ "ํ•„ํ„ฐ์—†์Œ"์œผ๋กœ ๋ณ€๊ฒฝ
reset_button.click(
fn=reset_sliders,
inputs=[final_output],
outputs=[
filter_radio, # ํ•„ํ„ฐ ์„ ํƒ ๋ผ๋””์˜ค ๋ฒ„ํŠผ
bg_brightness,
bg_contrast,
bg_saturation,
bg_temperature,
bg_vibrance,
bg_color_mixer_blues,
bg_shadows,
final_output,
],
)
# ๋‹ค์šด๋กœ๋“œ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ๋ชจ๋“  ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ
download_button.click(
fn=download_all,
inputs=[
input_image,
background_output,
final_output
],
outputs=download_output,
)
return demo
if __name__ == "__main__":
logger.info("์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘")
demo = create_interface()
demo.queue()
demo.launch(server_name='0.0.0.0')