|
import os |
|
import sys |
|
import base64 |
|
import io |
|
import logging |
|
import tempfile |
|
import traceback |
|
import requests |
|
from PIL import Image |
|
import gradio as gr |
|
from gradio_client import Client |
|
from dotenv import load_dotenv |
|
|
|
|
|
load_dotenv() |
|
|
|
|
|
logging.basicConfig( |
|
level=logging.INFO, |
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', |
|
handlers=[ |
|
logging.FileHandler("app.log"), |
|
logging.StreamHandler(sys.stdout) |
|
] |
|
) |
|
logger = logging.getLogger("image-enhancer-control-tower") |
|
|
|
|
|
API_ENDPOINT = os.environ.get("API_ENDPOINT") |
|
if not API_ENDPOINT: |
|
logger.error("API_ENDPOINT environment variable is not set") |
|
sys.exit(1) |
|
|
|
try: |
|
|
|
client = Client(API_ENDPOINT) |
|
logger.info("API client initialized successfully") |
|
except Exception as e: |
|
logger.error(f"Failed to initialize API client: {e}") |
|
sys.exit(1) |
|
|
|
|
|
def load_backgrounds_from_env(): |
|
"""ํ๊ฒฝ๋ณ์์์ backgrounds.py ์ ์ฒด ๋ด์ฉ ๋ก๋""" |
|
backgrounds_code = os.environ.get("BACKGROUNDS_DATA", "") |
|
if not backgrounds_code: |
|
logger.warning("BACKGROUNDS_DATA environment variable is empty") |
|
|
|
with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as temp_file: |
|
temp_filename = temp_file.name |
|
logger.info(f"Created temporary file: {temp_filename}") |
|
|
|
|
|
if isinstance(uploaded_file, str): |
|
logger.info(f"Uploaded file is already a path: {uploaded_file}") |
|
return uploaded_file |
|
|
|
|
|
if isinstance(uploaded_file, Image.Image): |
|
logger.info("Uploaded file is a PIL Image") |
|
uploaded_file.save(temp_filename, format="PNG") |
|
return temp_filename |
|
|
|
|
|
with open(temp_filename, "wb") as f: |
|
if hasattr(uploaded_file, "read"): |
|
logger.info("Processing file object") |
|
content = uploaded_file.read() |
|
f.write(content) |
|
logger.info(f"Wrote {len(content)} bytes to {temp_filename}") |
|
else: |
|
logger.info("Processing binary data") |
|
f.write(uploaded_file) |
|
logger.info(f"Wrote data to {temp_filename}") |
|
|
|
return temp_filename |
|
except Exception as e: |
|
logger.error(f"Error saving uploaded file: {e}") |
|
logger.error(traceback.format_exc()) |
|
return None |
|
|
|
|
|
def api_update_dropdowns(bg_type): |
|
"""๋๋กญ๋ค์ด ์
๋ฐ์ดํธ API ํธ์ถ""" |
|
try: |
|
result = client.predict(bg_type=bg_type, api_name="/update_dropdowns") |
|
return result |
|
except Exception as e: |
|
logger.error(f"Error calling update_dropdowns API: {e}") |
|
return None |
|
|
|
def api_generate_prompt_with_password_check(password, bg_type, simple, studio, nature, indoor, special, jewelry, special_effects, request_text, aspect_ratio): |
|
"""ํ๋กฌํํธ ์์ฑ API ํธ์ถ""" |
|
try: |
|
result = client.predict( |
|
password=password, |
|
bg_type=bg_type, |
|
simple=simple, |
|
studio=studio, |
|
nature=nature, |
|
indoor=indoor, |
|
special=special, |
|
jewelry=jewelry, |
|
special_effects=special_effects, |
|
request_text=request_text, |
|
aspect_ratio=aspect_ratio, |
|
api_name="/generate_prompt_with_password_check" |
|
) |
|
return result |
|
except Exception as e: |
|
logger.error(f"Error calling generate_prompt API: {e}") |
|
return f"API ํธ์ถ ์ค๋ฅ: {str(e)}" |
|
|
|
def api_check_password(password, image_file, bg_type, simple, studio, nature, indoor, special, jewelry, special_effects, request_text, quality_level, aspect_ratio, output_format, enable_enhancement): |
|
"""์ด๋ฏธ์ง ์ฒ๋ฆฌ API ํธ์ถ""" |
|
try: |
|
result = client.predict( |
|
password=password, |
|
param_1=image_file, |
|
param_2=bg_type, |
|
param_3=simple, |
|
param_4=studio, |
|
param_5=nature, |
|
param_6=indoor, |
|
param_7=special, |
|
param_8=jewelry, |
|
param_9=special_effects, |
|
param_10=request_text, |
|
param_11=quality_level, |
|
param_12=aspect_ratio, |
|
param_13=output_format, |
|
param_14=enable_enhancement, |
|
api_name="/check_password" |
|
) |
|
return result |
|
except Exception as e: |
|
logger.error(f"Error calling check_password API: {e}") |
|
return None, None, None, None, "", "", f"API ํธ์ถ ์ค๋ฅ: {str(e)}" |
|
|
|
|
|
def create_gradio_interface(): |
|
try: |
|
logger.info("Creating Gradio interface") |
|
with gr.Blocks(title="AI ์ด๋ฏธ์ง ํธ์ง ๋ฐ ํ์ง ๊ฐ์ ") as app: |
|
gr.Markdown("# AI ์ด๋ฏธ์ง ํธ์ง ๋ฐ ํ์ง ๊ฐ์ ๋๊ตฌ") |
|
|
|
|
|
password_box = gr.Textbox( |
|
label="๋น๋ฐ๋ฒํธ", |
|
type="password", |
|
placeholder="์ฌ์ฉํ๋ ค๋ฉด ๋น๋ฐ๋ฒํธ๋ฅผ ์
๋ ฅํ์ธ์", |
|
interactive=True |
|
) |
|
|
|
|
|
with gr.Row(): |
|
with gr.Column(): |
|
|
|
image = gr.Image(label="์ํ ์ด๋ฏธ์ง ์
๋ก๋", type="pil") |
|
|
|
with gr.Row(): |
|
with gr.Column(): |
|
background_type = gr.Radio( |
|
choices=["์ฌํ ๋ฐฐ๊ฒฝ", "์คํ๋์ค ๋ฐฐ๊ฒฝ", "์์ฐ ํ๊ฒฝ", "์ค๋ด ํ๊ฒฝ", "ํน์๋ฐฐ๊ฒฝ", "์ฃผ์ผ๋ฆฌ", "ํน์ํจ๊ณผ"], |
|
label="๋ฐฐ๊ฒฝ ์ ํ", |
|
value="์ฌํ ๋ฐฐ๊ฒฝ" |
|
) |
|
|
|
|
|
simple_dropdown = gr.Dropdown( |
|
choices=list(SIMPLE_BACKGROUNDS.keys()) if SIMPLE_BACKGROUNDS else ["ํ์ดํธ ๊ธฐ๋ณธ"], |
|
value=list(SIMPLE_BACKGROUNDS.keys())[0] if SIMPLE_BACKGROUNDS else "ํ์ดํธ ๊ธฐ๋ณธ", |
|
label="์ฌํ ๋ฐฐ๊ฒฝ ์ ํ", |
|
visible=True, |
|
interactive=True |
|
) |
|
|
|
studio_dropdown = gr.Dropdown( |
|
choices=list(STUDIO_BACKGROUNDS.keys()) if STUDIO_BACKGROUNDS else ["์ฐ๋
น์ ์ฅ๋ฏธ ์ ์"], |
|
value=list(STUDIO_BACKGROUNDS.keys())[0] if STUDIO_BACKGROUNDS else "์ฐ๋
น์ ์ฅ๋ฏธ ์ ์", |
|
label="์คํ๋์ค ๋ฐฐ๊ฒฝ ์ ํ", |
|
visible=False, |
|
interactive=True |
|
) |
|
|
|
nature_dropdown = gr.Dropdown( |
|
choices=list(NATURE_BACKGROUNDS.keys()) if NATURE_BACKGROUNDS else ["์์ ํ๋๊ฐ ์๋ ํด๋ณ"], |
|
value=list(NATURE_BACKGROUNDS.keys())[0] if NATURE_BACKGROUNDS else "์์ ํ๋๊ฐ ์๋ ํด๋ณ", |
|
label="์์ฐ ํ๊ฒฝ ์ ํ", |
|
visible=False, |
|
interactive=True |
|
) |
|
|
|
indoor_dropdown = gr.Dropdown( |
|
choices=list(INDOOR_BACKGROUNDS.keys()) if INDOOR_BACKGROUNDS else ["๊ธฐ๋ณธ ์ฑ
์"], |
|
value=list(INDOOR_BACKGROUNDS.keys())[0] if INDOOR_BACKGROUNDS else "๊ธฐ๋ณธ ์ฑ
์", |
|
label="์ค๋ด ํ๊ฒฝ ์ ํ", |
|
visible=False, |
|
interactive=True |
|
) |
|
|
|
special_dropdown = gr.Dropdown( |
|
choices=list(SPECIAL_BACKGROUNDS.keys()) if SPECIAL_BACKGROUNDS else ["๋ค์ด๋น ๋นํฐ์ง ํ๋ก๋ด ๋ฒฝ์ง"], |
|
value=list(SPECIAL_BACKGROUNDS.keys())[0] if SPECIAL_BACKGROUNDS else "๋ค์ด๋น ๋นํฐ์ง ํ๋ก๋ด ๋ฒฝ์ง", |
|
label="ํน์๋ฐฐ๊ฒฝ ์ ํ", |
|
visible=False, |
|
interactive=True |
|
) |
|
|
|
jewelry_dropdown = gr.Dropdown( |
|
choices=list(JEWELRY_BACKGROUNDS.keys()) if JEWELRY_BACKGROUNDS else ["ํ์ดํธ ๋ฏธ๋ฌ ์คํ ๋ผ์ดํธ"], |
|
value=list(JEWELRY_BACKGROUNDS.keys())[0] if JEWELRY_BACKGROUNDS else "ํ์ดํธ ๋ฏธ๋ฌ ์คํ ๋ผ์ดํธ", |
|
label="์ฃผ์ผ๋ฆฌ ๋ฐฐ๊ฒฝ ์ ํ", |
|
visible=False, |
|
interactive=True |
|
) |
|
|
|
special_effects_dropdown = gr.Dropdown( |
|
choices=list(SPECIAL_EFFECTS_BACKGROUNDS.keys()) if SPECIAL_EFFECTS_BACKGROUNDS else ["๋ธ๋ฃจ๋ธ๋ ํฐ ๋ฌผ๋ฐฉ์ธ ํจ๊ณผ"], |
|
value=list(SPECIAL_EFFECTS_BACKGROUNDS.keys())[0] if SPECIAL_EFFECTS_BACKGROUNDS else "๋ธ๋ฃจ๋ธ๋ ํฐ ๋ฌผ๋ฐฉ์ธ ํจ๊ณผ", |
|
label="ํน์ํจ๊ณผ ๋ฐฐ๊ฒฝ ์ ํ", |
|
visible=False, |
|
interactive=True |
|
) |
|
|
|
|
|
def update_dropdowns(bg_type): |
|
return { |
|
simple_dropdown: gr.update(visible=(bg_type == "์ฌํ ๋ฐฐ๊ฒฝ")), |
|
studio_dropdown: gr.update(visible=(bg_type == "์คํ๋์ค ๋ฐฐ๊ฒฝ")), |
|
nature_dropdown: gr.update(visible=(bg_type == "์์ฐ ํ๊ฒฝ")), |
|
indoor_dropdown: gr.update(visible=(bg_type == "์ค๋ด ํ๊ฒฝ")), |
|
special_dropdown: gr.update(visible=(bg_type == "ํน์๋ฐฐ๊ฒฝ")), |
|
jewelry_dropdown: gr.update(visible=(bg_type == "์ฃผ์ผ๋ฆฌ")), |
|
special_effects_dropdown: gr.update(visible=(bg_type == "ํน์ํจ๊ณผ")) |
|
} |
|
|
|
background_type.change( |
|
fn=update_dropdowns, |
|
inputs=[background_type], |
|
outputs=[simple_dropdown, studio_dropdown, nature_dropdown, indoor_dropdown, special_dropdown, jewelry_dropdown, special_effects_dropdown] |
|
) |
|
|
|
|
|
request_text = gr.Textbox( |
|
label="์์ฒญ์ฌํญ", |
|
placeholder="์ํ ์ด๋ฏธ์ง์ ์ ์ฉํ ์คํ์ผ, ๋ถ์๊ธฐ, ํน๋ณ ์์ฒญ์ฌํญ ๋ฑ์ ์
๋ ฅํ์ธ์.", |
|
lines=3 |
|
) |
|
|
|
|
|
quality_level = gr.Radio( |
|
label="ํ์ง ๋ ๋ฒจ", |
|
choices=["gpt", "flux"], |
|
value="flux", |
|
info="GPT: GPT ๋ชจ๋ธ (๊ณ ํ์ง), ์ผ๋ฐ: Flux ๋ชจ๋ธ (๋น ๋ฅธ ์ฒ๋ฆฌ + ๊ธฐ๋ณธ ํ์ง๊ฐ์ )" |
|
) |
|
|
|
aspect_ratio = gr.Dropdown( |
|
label="์ข
ํก๋น", |
|
choices=["1:1", "3:2", "2:3"], |
|
value="1:1" |
|
) |
|
|
|
output_format = gr.Dropdown( |
|
label="์ด๋ฏธ์ง ํ์", |
|
choices=["jpg", "png"], |
|
value="jpg" |
|
) |
|
|
|
|
|
enable_enhancement = gr.Checkbox( |
|
label="์ถ๊ฐ ํ์ง ๊ฐ์ ", |
|
value=False, |
|
info="GPT: 1ํ ํ์ง๊ฐ์ , Flux: 2์ฐจ ํ์ง๊ฐ์ (๊ธฐ๋ณธ 1ํ + ์ถ๊ฐ 1ํ)" |
|
) |
|
enhancement_level = gr.Slider(label="ํ์ง ๊ฐ์ ๋ ๋ฒจ", minimum=1, maximum=4, value=2, step=1, visible=False) |
|
|
|
|
|
generate_prompt_btn = gr.Button("ํ๋กฌํํธ๋ง ์์ฑ") |
|
|
|
|
|
edit_btn = gr.Button("์ด๋ฏธ์ง ํธ์ง ๋ฐ ํ์ง ๊ฐ์ ") |
|
|
|
with gr.Column(): |
|
with gr.Row(): |
|
with gr.Column(): |
|
gr.Markdown("## ํธ์ง๋ ์ด๋ฏธ์ง") |
|
original_output = gr.Gallery(label="ํธ์ง ๊ฒฐ๊ณผ", preview=True) |
|
original_download = gr.File(label="ํธ์ง ์ด๋ฏธ์ง ๋ค์ด๋ก๋", interactive=False) |
|
|
|
with gr.Column(): |
|
gr.Markdown("## ํ์ง ๊ฐ์ ๋ ์ด๋ฏธ์ง") |
|
enhanced_output = gr.Gallery(label="ํ์ง ๊ฐ์ ๊ฒฐ๊ณผ", preview=True) |
|
enhanced_download = gr.File(label="๊ฐ์ ์ด๋ฏธ์ง ๋ค์ด๋ก๋", interactive=False) |
|
|
|
|
|
prompt_output = gr.Textbox(label="์์ฑ๋ ํ๋กฌํํธ", lines=10, interactive=False) |
|
info = gr.Textbox(label="์ฒ๋ฆฌ ์ ๋ณด", interactive=False) |
|
error = gr.Textbox(label="์ค๋ฅ ๋ฉ์์ง", interactive=False, visible=True) |
|
|
|
|
|
def generate_prompt_wrapper(password, bg_type, simple, studio, nature, indoor, special, jewelry, special_effects, request_text, aspect_ratio): |
|
return api_generate_prompt_with_password_check( |
|
password, bg_type, simple, studio, nature, indoor, special, jewelry, special_effects, request_text, aspect_ratio |
|
) |
|
|
|
|
|
def process_image_wrapper(password, image, bg_type, simple, studio, nature, indoor, special, jewelry, special_effects, request_text, quality_level, aspect_ratio, output_format, enable_enhancement): |
|
if image is None: |
|
return [], None, [], None, "", "", "์ด๋ฏธ์ง๋ฅผ ์
๋ก๋ํด์ผ ํฉ๋๋ค." |
|
|
|
|
|
temp_path = save_uploaded_file(image) |
|
if temp_path is None: |
|
return [], None, [], None, "", "", "์ด๋ฏธ์ง ์ฒ๋ฆฌ์ ์คํจํ์ต๋๋ค." |
|
|
|
try: |
|
|
|
result = api_check_password( |
|
password, temp_path, bg_type, simple, studio, nature, indoor, special, jewelry, special_effects, |
|
request_text, quality_level, aspect_ratio, output_format, enable_enhancement |
|
) |
|
return result |
|
finally: |
|
|
|
if os.path.exists(temp_path): |
|
try: |
|
os.remove(temp_path) |
|
except: |
|
pass |
|
|
|
|
|
generate_prompt_btn.click( |
|
fn=generate_prompt_wrapper, |
|
inputs=[ |
|
password_box, |
|
background_type, |
|
simple_dropdown, studio_dropdown, nature_dropdown, indoor_dropdown, special_dropdown, |
|
jewelry_dropdown, special_effects_dropdown, |
|
request_text, aspect_ratio |
|
], |
|
outputs=[prompt_output] |
|
) |
|
|
|
|
|
edit_btn.click( |
|
fn=process_image_wrapper, |
|
inputs=[ |
|
password_box, |
|
image, background_type, |
|
simple_dropdown, studio_dropdown, nature_dropdown, indoor_dropdown, special_dropdown, |
|
jewelry_dropdown, special_effects_dropdown, |
|
request_text, quality_level, aspect_ratio, output_format, enable_enhancement |
|
], |
|
outputs=[ |
|
original_output, original_download, |
|
enhanced_output, enhanced_download, |
|
prompt_output, info, error |
|
] |
|
) |
|
|
|
logger.info("Gradio interface created successfully") |
|
return app |
|
except Exception as e: |
|
logger.error(f"Error creating Gradio interface: {e}") |
|
logger.error(traceback.format_exc()) |
|
raise |
|
|
|
|
|
if __name__ == "__main__": |
|
try: |
|
logger.info("Starting control tower application") |
|
|
|
|
|
os.makedirs("imgs", exist_ok=True) |
|
logger.info("์ด๋ฏธ์ง ๋๋ ํ ๋ฆฌ ์ค๋น ์๋ฃ") |
|
|
|
app = create_gradio_interface() |
|
logger.info("Launching Gradio app") |
|
app.launch(share=True) |
|
except Exception as e: |
|
logger.error(f"Error running app: {e}") |
|
logger.error(traceback.format_exc()), None, None, None, None, None, None |
|
|
|
try: |
|
|
|
local_vars = {} |
|
exec(backgrounds_code, {}, local_vars) |
|
|
|
return ( |
|
local_vars.get("SIMPLE_BACKGROUNDS", {}), |
|
local_vars.get("STUDIO_BACKGROUNDS", {}), |
|
local_vars.get("NATURE_BACKGROUNDS", {}), |
|
local_vars.get("INDOOR_BACKGROUNDS", {}), |
|
local_vars.get("SPECIAL_BACKGROUNDS", {}), |
|
local_vars.get("JEWELRY_BACKGROUNDS", {}), |
|
local_vars.get("SPECIAL_EFFECTS_BACKGROUNDS", {}) |
|
) |
|
except Exception as e: |
|
logger.error(f"Failed to parse BACKGROUNDS_DATA: {e}") |
|
return None, None, None, None, None, None, None |
|
|
|
|
|
(SIMPLE_BACKGROUNDS, STUDIO_BACKGROUNDS, NATURE_BACKGROUNDS, |
|
INDOOR_BACKGROUNDS, SPECIAL_BACKGROUNDS, JEWELRY_BACKGROUNDS, |
|
SPECIAL_EFFECTS_BACKGROUNDS) = load_backgrounds_from_env() |
|
|
|
|
|
if not SIMPLE_BACKGROUNDS: |
|
SIMPLE_BACKGROUNDS = { |
|
"ํ์ดํธ ๊ธฐ๋ณธ": "clean white background with soft lighting", |
|
"ํ์ ํฌํค": "light gray background with minimal shadows", |
|
"๋ผ์ดํธ ๊ทธ๋ ์ด": "soft light gray backdrop with even illumination" |
|
} |
|
|
|
if not STUDIO_BACKGROUNDS: |
|
STUDIO_BACKGROUNDS = { |
|
"์ฐ๋
น์ ์ฅ๋ฏธ ์ ์": "soft green background with rose garden elements", |
|
"์ฐ๋ถํ ์ฅ๋ฏธ ๋๋ฆฌ์": "pink rose with marble surface backdrop" |
|
} |
|
|
|
if not NATURE_BACKGROUNDS: |
|
NATURE_BACKGROUNDS = { |
|
"์์ ํ๋๊ฐ ์๋ ํด๋ณ": "serene beach with gentle waves", |
|
"์ด๋ํด๋ณ": "tropical beach setting with palm trees" |
|
} |
|
|
|
if not INDOOR_BACKGROUNDS: |
|
INDOOR_BACKGROUNDS = { |
|
"๊ธฐ๋ณธ ์ฑ
์": "clean modern desk setup", |
|
"๋น์ด ๋น์น๋ ๊ฑฐ์ค": "bright living room with natural light" |
|
} |
|
|
|
if not SPECIAL_BACKGROUNDS: |
|
SPECIAL_BACKGROUNDS = { |
|
"๋ค์ด๋น ๋นํฐ์ง ํ๋ก๋ด ๋ฒฝ์ง": "navy vintage floral wallpaper background" |
|
} |
|
|
|
if not JEWELRY_BACKGROUNDS: |
|
JEWELRY_BACKGROUNDS = { |
|
"ํ์ดํธ ๋ฏธ๋ฌ ์คํ ๋ผ์ดํธ": "white mirror surface with spotlight" |
|
} |
|
|
|
if not SPECIAL_EFFECTS_BACKGROUNDS: |
|
SPECIAL_EFFECTS_BACKGROUNDS = { |
|
"๋ธ๋ฃจ๋ธ๋ ํฐ ๋ฌผ๋ฐฉ์ธ ํจ๊ณผ": "blue-black background with water droplet effects" |
|
} |
|
|
|
|
|
def save_uploaded_file(uploaded_file, suffix='.png'): |
|
try: |
|
logger.info(f"Processing uploaded file: {type(uploaded_file)}") |
|
|
|
if uploaded_file is None: |
|
logger.warning("Uploaded file is None") |
|
return |