seawolf2357 commited on
Commit
33caacc
ยท
verified ยท
1 Parent(s): be0f670

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +29 -651
app.py CHANGED
@@ -1,657 +1,35 @@
1
- # ===== CRITICAL: Import spaces FIRST before any CUDA operations =====
2
- try:
3
- import spaces
4
- HF_SPACES = True
5
- except ImportError:
6
- # If running locally, create a dummy decorator
7
- def spaces_gpu_decorator(duration=60):
8
- def decorator(func):
9
- return func
10
- return decorator
11
- spaces = type('spaces', (), {'GPU': spaces_gpu_decorator})()
12
- HF_SPACES = False
13
- print("Warning: Running without Hugging Face Spaces GPU allocation")
14
-
15
- # ===== Now import other libraries =====
16
- import random
17
  import os
18
- import uuid
19
- import re
20
- import time
21
- from datetime import datetime
22
-
23
- import gradio as gr
24
- import numpy as np
25
- import requests
26
- import torch
27
- from diffusers import DiffusionPipeline
28
- from PIL import Image
29
-
30
- # ===== OpenAI ์„ค์ • =====
31
- from openai import OpenAI
32
-
33
- # Add error handling for API key
34
- try:
35
- client = OpenAI(api_key=os.getenv("LLM_API"))
36
- except Exception as e:
37
- print(f"Warning: OpenAI client initialization failed: {e}")
38
- client = None
39
-
40
- # ===== ํ”„๋กฌํ”„ํŠธ ์ฆ๊ฐ•์šฉ ์Šคํƒ€์ผ ํ”„๋ฆฌ์…‹ =====
41
- STYLE_PRESETS = {
42
- "None": "",
43
- "Realistic Photo": "photorealistic, 8k, ultra-detailed, cinematic lighting, realistic skin texture",
44
- "Oil Painting": "oil painting, rich brush strokes, canvas texture, baroque lighting",
45
- "Comic Book": "comic book style, bold ink outlines, cel shading, vibrant colors",
46
- "Watercolor": "watercolor illustration, soft gradients, splatter effect, pastel palette",
47
- }
48
-
49
- # ===== ์ €์žฅ ํด๋” =====
50
- SAVE_DIR = "saved_images"
51
- if not os.path.exists(SAVE_DIR):
52
- os.makedirs(SAVE_DIR, exist_ok=True)
53
-
54
- # ===== ๋””๋ฐ”์ด์Šค & ๋ชจ๋ธ ๋กœ๋“œ =====
55
- device = "cuda" if torch.cuda.is_available() else "cpu"
56
- print(f"Using device: {device}")
57
-
58
- repo_id = "black-forest-labs/FLUX.1-dev"
59
- adapter_id = "seawolf2357/chocs"
60
-
61
- # Add error handling for model loading
62
- try:
63
- pipeline = DiffusionPipeline.from_pretrained(repo_id, torch_dtype=torch.bfloat16)
64
- pipeline.load_lora_weights(adapter_id)
65
- pipeline = pipeline.to(device)
66
- print("Model loaded successfully")
67
- except Exception as e:
68
- print(f"Error loading model: {e}")
69
- pipeline = None
70
-
71
- MAX_SEED = np.iinfo(np.int32).max
72
- MAX_IMAGE_SIZE = 1024
73
-
74
- # ===== ํ•œ๊ธ€ ์—ฌ๋ถ€ ํŒ๋ณ„ =====
75
- HANGUL_RE = re.compile(r"[\u3131-\u318E\uAC00-\uD7A3]+")
76
-
77
- def is_korean(text: str) -> bool:
78
- return bool(HANGUL_RE.search(text))
79
-
80
- # ===== ๋ฒˆ์—ญ & ์ฆ๊ฐ• ํ•จ์ˆ˜ =====
81
-
82
- def openai_translate(text: str, retries: int = 3) -> str:
83
- """ํ•œ๊ธ€์„ ์˜์–ด๋กœ ๋ฒˆ์—ญ (OpenAI GPT-4o-mini ์‚ฌ์šฉ). ์˜์–ด ์ž…๋ ฅ์ด๋ฉด ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜."""
84
- if not is_korean(text):
85
- return text
86
-
87
- if client is None:
88
- print("Warning: OpenAI client not available, returning original text")
89
- return text
90
-
91
- for attempt in range(retries):
92
- try:
93
- res = client.chat.completions.create(
94
- model="gpt-4o-mini",
95
- messages=[
96
- {
97
- "role": "system",
98
- "content": "Translate the following Korean prompt into concise, descriptive English suitable for an image generation model. Keep the meaning, do not add new concepts."
99
- },
100
- {"role": "user", "content": text}
101
- ],
102
- temperature=0.3,
103
- max_tokens=256,
104
- )
105
- return res.choices[0].message.content.strip()
106
- except Exception as e:
107
- print(f"[translate] attempt {attempt + 1} failed: {e}")
108
- time.sleep(2)
109
- return text # ๋ฒˆ์—ญ ์‹คํŒจ ์‹œ ์›๋ฌธ ๊ทธ๋Œ€๋กœ
110
-
111
- def enhance_prompt(text: str, retries: int = 3) -> str:
112
- """OpenAI๋ฅผ ํ†ตํ•ด ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ฆ๊ฐ•ํ•˜์—ฌ ๊ณ ํ’ˆ์งˆ ์ด๋ฏธ์ง€ ์ƒ์„ฑ์„ ์œ„ํ•œ ์ƒ์„ธํ•œ ์„ค๋ช…์œผ๋กœ ๋ณ€ํ™˜."""
113
- if client is None:
114
- print("Warning: OpenAI client not available, returning original text")
115
- return text
116
-
117
- for attempt in range(retries):
118
- try:
119
- res = client.chat.completions.create(
120
- model="gpt-4o-mini",
121
- messages=[
122
- {
123
- "role": "system",
124
- "content": """You are an expert prompt engineer for image generation models. Enhance the given prompt to create high-quality, detailed images.
125
-
126
- Guidelines:
127
- - Add specific visual details (lighting, composition, colors, textures)
128
- - Include technical photography terms (depth of field, focal length, etc.)
129
- - Add atmosphere and mood descriptors
130
- - Specify image quality terms (4K, ultra-detailed, professional, etc.)
131
- - Keep the core subject and meaning intact
132
- - Make it comprehensive but not overly long
133
- - Focus on visual elements that will improve image generation quality
134
 
135
- Example:
136
- Input: "A man giving a speech"
137
- Output: "A professional man giving an inspiring speech at a podium, dramatic lighting with warm spotlights, confident posture and gestures, high-resolution 4K photography, sharp focus, cinematic composition, bokeh background with audience silhouettes, professional event setting, detailed facial expressions, realistic skin texture"
138
- """
139
- },
140
- {"role": "user", "content": f"Enhance this prompt for high-quality image generation: {text}"}
141
- ],
142
- temperature=0.7,
143
- max_tokens=512,
144
- )
145
- return res.choices[0].message.content.strip()
146
- except Exception as e:
147
- print(f"[enhance] attempt {attempt + 1} failed: {e}")
148
- time.sleep(2)
149
- return text # ์ฆ๊ฐ• ์‹คํŒจ ์‹œ ์›๋ฌธ ๊ทธ๋Œ€๋กœ
150
-
151
- def prepare_prompt(user_prompt: str, style_key: str, enhance_prompt_enabled: bool = False) -> str:
152
- """ํ•œ๊ธ€์ด๋ฉด ๋ฒˆ์—ญํ•˜๊ณ , ํ”„๋กฌํ”„ํŠธ ์ฆ๊ฐ• ์˜ต์…˜์ด ํ™œ์„ฑํ™”๋˜๋ฉด ์ฆ๊ฐ•ํ•˜๊ณ , ์„ ํƒํ•œ ์Šคํƒ€์ผ ํ”„๋ฆฌ์…‹์„ ๋ถ™์—ฌ์„œ ์ตœ์ข… ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋งŒ๋“ ๋‹ค."""
153
- # 1. ๋ฒˆ์—ญ (ํ•œ๊ธ€์ธ ๊ฒฝ์šฐ)
154
- prompt_en = openai_translate(user_prompt)
155
-
156
- # 2. ํ”„๋กฌํ”„ํŠธ ์ฆ๊ฐ• (ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ)
157
- if enhance_prompt_enabled:
158
- prompt_en = enhance_prompt(prompt_en)
159
- print(f"Enhanced prompt: {prompt_en}")
160
-
161
- # 3. ์Šคํƒ€์ผ ํ”„๋ฆฌ์…‹ ์ ์šฉ
162
- style_suffix = STYLE_PRESETS.get(style_key, "")
163
- if style_suffix:
164
- final_prompt = f"{prompt_en}, {style_suffix}"
165
- else:
166
- final_prompt = prompt_en
167
-
168
- return final_prompt
169
-
170
- # ===== ์ด๋ฏธ์ง€ ์ €์žฅ =====
171
-
172
- def save_generated_image(image: Image.Image, prompt: str) -> str:
173
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
174
- unique_id = str(uuid.uuid4())[:8]
175
- filename = f"{timestamp}_{unique_id}.png"
176
- filepath = os.path.join(SAVE_DIR, filename)
177
- image.save(filepath)
178
-
179
- # ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ €์žฅ
180
- metadata_file = os.path.join(SAVE_DIR, "metadata.txt")
181
- with open(metadata_file, "a", encoding="utf-8") as f:
182
- f.write(f"{filename}|{prompt}|{timestamp}\n")
183
- return filepath
184
-
185
- # ===== Diffusion ํ˜ธ์ถœ =====
186
-
187
- def run_pipeline(prompt: str, seed: int, width: int, height: int, guidance_scale: float, num_steps: int, lora_scale: float):
188
- if pipeline is None:
189
- raise ValueError("Model pipeline not loaded")
190
-
191
- generator = torch.Generator(device=device).manual_seed(int(seed))
192
- result = pipeline(
193
- prompt=prompt,
194
- guidance_scale=guidance_scale,
195
- num_inference_steps=num_steps,
196
- width=width,
197
- height=height,
198
- generator=generator,
199
- joint_attention_kwargs={"scale": lora_scale},
200
- ).images[0]
201
- return result
202
-
203
- # ===== Gradio inference ๋ž˜ํผ =====
204
-
205
- @spaces.GPU(duration=60)
206
- def generate_image(
207
- user_prompt: str,
208
- style_key: str,
209
- enhance_prompt_enabled: bool = False,
210
- seed: int = 42,
211
- randomize_seed: bool = True,
212
- width: int = 1024,
213
- height: int = 768,
214
- guidance_scale: float = 3.5,
215
- num_inference_steps: int = 30,
216
- lora_scale: float = 1.0,
217
- progress=None,
218
- ):
219
  try:
220
- if randomize_seed:
221
- seed = random.randint(0, MAX_SEED)
222
-
223
- # 1) ๋ฒˆ์—ญ + ์ฆ๊ฐ•
224
- final_prompt = prepare_prompt(user_prompt, style_key, enhance_prompt_enabled)
225
- print(f"Final prompt: {final_prompt}")
226
-
227
- # 2) ํŒŒ์ดํ”„๋ผ์ธ ํ˜ธ์ถœ
228
- image = run_pipeline(final_prompt, seed, width, height, guidance_scale, num_inference_steps, lora_scale)
229
-
230
- # 3) ์ €์žฅ
231
- save_generated_image(image, final_prompt)
232
-
233
- return image, seed
234
-
235
- except Exception as e:
236
- print(f"Error generating image: {e}")
237
- # Return a placeholder or error message
238
- error_image = Image.new('RGB', (width, height), color='red')
239
- return error_image, seed
240
-
241
- # ===== ์˜ˆ์‹œ ํ”„๋กฌํ”„ํŠธ (ํ•œ๊ตญ์–ด/์˜์–ด ํ˜ผ์šฉ ํ—ˆ์šฉ) =====
242
-
243
- examples = [
244
- "Mr. cho ๋‘ ์†์œผ๋กœ 'Healing !' ํ˜„์ˆ˜๋ง‰์„ ๋“ค๊ณ  ์žˆ๋Š” ๋ชจ์Šต, ํ™˜๊ฒฝ๋ณดํ˜ธ์™€ ์ง€์†๊ฐ€๋Šฅํ•œ ์ž„์—… ๋ฐœ์ „์— ๋Œ€ํ•œ ์˜์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๊ณ  ์žˆ๋‹ค.",
245
- "Mr. cho ์–‘ํŒ”์„ ๋“ค์–ด ์˜ฌ๋ฆฌ๋ฉฐ ๊ธฐ์œ ํ‘œ์ •์œผ๋กœ ํ™˜ํ˜ธํ•˜๋Š” ๋ชจ์Šต, ์กฐ๋ฆผ ์‚ฌ์—… ์„ฑ๊ณต๊ณผ ๋ฏธ๋ž˜ ์ž„์—…์— ๋Œ€ํ•œ ํฌ๋ง์„ ๋ณด์—ฌ์ฃผ๊ณ  ์žˆ๋‹ค.",
246
- "Mr. cho ์šด๋™๋ณต์„ ์ž…๊ณ  ์‚ฐ๋ฆผ ์†์—์„œ ํŠธ๋ ˆํ‚นํ•˜๋Š” ๋ชจ์Šต, ๊ฑด๊ฐ•ํ•œ ์ƒํ™œ์Šต๊ด€๊ณผ ํ™œ๊ธฐ์ฐฌ ๋ฆฌ๋”์‹ญ์„ ๋ณด์—ฌ์ฃผ๊ณ  ์žˆ๋‹ค.",
247
- "Mr. cho ์‚ฐ์ดŒ ๋งˆ์„์—์„œ ์—ฌ์„ฑ ์ž„์—…์ธ๋“ค๊ณผ ๋”ฐ๋œปํ•˜๊ฒŒ ์•…์ˆ˜ํ•˜๋Š” ๋ชจ์Šต, ์—ฌ์„ฑ ์ž„์—…์ข…์‚ฌ์ž๋“ค์— ๋Œ€ํ•œ ์ง„์ •ํ•œ ๊ด€์‹ฌ๊ณผ ์†Œํ†ต์„ ๋ณด์—ฌ์ฃผ๊ณ  ์žˆ๋‹ค.",
248
- "Mr. cho ์ž„์—…๋ฐ•๋žŒํšŒ์žฅ์—์„œ ์šธ์ฐฝํ•œ ์ˆฒ์„ ํ–ฅํ•ด ์†๊ฐ€๋ฝ์œผ๋กœ ๊ฐ€๋ฆฌํ‚ค๋ฉฐ ์˜๊ฐ์„ ์ฃผ๋Š” ์ œ์Šค์ฒ˜๋ฅผ ์ทจํ•˜๊ณ  ์žˆ๊ณ , ์—ฌ์„ฑ๋“ค๊ณผ ์•„์ด๋“ค์ด ๋ฐ•์ˆ˜๋ฅผ ์น˜๊ณ  ์žˆ๋‹ค.",
249
- "Mr. cho ์‚ฐ๋ฆผ์ถ•์ œ์— ์ฐธ์—ฌํ•˜์—ฌ ์—ด์ •์ ์œผ๋กœ ์‘์›ํ•˜๋Š” ์—ฌ์„ฑ ์ž„์—…์ธ๋“ค์—๊ฒŒ ๋‘˜๋Ÿฌ์‹ธ์—ฌ ์žˆ๋Š” ๋ชจ์Šต.",
250
- "Mr. cho ๋ชฉ์žฌ์‹œ์žฅ์„ ๋ฐฉ๋ฌธํ•˜์—ฌ ์—ฌ์„ฑ ๋ชฉ์žฌ์ƒ๋“ค๊ณผ ๋ชฉ๊ณต์˜ˆ ์žฅ์ธ๋“ค๊ณผ ์นœ๊ทผํ•˜๊ฒŒ ๋Œ€ํ™”ํ•˜๋Š” ๋ชจ์Šต.",
251
- "Mr. cho ์‚ฐ๋ฆผ๊ณผํ•™์›์„ ๋‘˜๋Ÿฌ๋ณด๋ฉฐ ์—ฌ์„ฑ ์—ฐ๊ตฌ์›๋“ค๊ณผ ๊ต์ˆ˜๋“ค๊ณผ ํ•จ๊ป˜ ์ž„์—… ์ •์ฑ…์— ๋Œ€ํ•ด ํ† ๋ก ํ•˜๋Š” ๋ชจ์Šต.",
252
- "Mr. cho ๋Œ€๊ทœ๋ชจ ์ž„์—…์ธ ๋Œ€๏ฟฝ๏ฟฝ๏ฟฝ์—์„œ ์ž์‹ ๊ฐ ์žˆ๋Š” ์ œ์Šค์ฒ˜์™€ ๊ฒฐ์—ฐํ•œ ํ‘œ์ •์œผ๋กœ ์—ญ๋™์ ์ธ ์—ฐ์„ค์„ ํ•˜๋Š” ๋ชจ์Šต.",
253
- "Mr. cho ํ™œ๊ธฐ์ฐฌ ์ธํ„ฐ๋ทฐ ํ˜„์žฅ์—์„œ ๋ฏธ๋ž˜ ์ž„์—… ๋ฐœ์ „์— ๋Œ€ํ•œ ๋น„์ „์„ ์—ด์ •์ ์œผ๋กœ ์„ค๋ช…ํ•˜๋Š” ๋ชจ์Šต.",
254
- "Mr. cho ์ค‘์š”ํ•œ ์ž„์—…์ •์ฑ… ํšŒ์˜๋ฅผ ์ค€๋น„ํ•˜๋ฉฐ ์„œ๋ฅ˜๋“ค์— ๋‘˜๋Ÿฌ์‹ธ์—ฌ ์ง‘์ค‘ํ•˜๊ณ  ๋‹จํ˜ธํ•œ ๋ชจ์Šต์„ ๋ณด์ด๋Š” ๋ชจ์Šต."
255
- ]
256
-
257
- # ===== ์ปค์Šคํ…€ CSS (์ง„ํ•œ ๋ถ‰์€์ƒ‰ ๊ณ ๊ธ‰ ๋””์ž์ธ) =====
258
- custom_css = """
259
- :root {
260
- --color-primary: #E91E63;
261
- --color-secondary: #FCE4EC;
262
- --color-accent: #F8BBD9;
263
- --color-rose: #F06292;
264
- --color-gold: #FFB74D;
265
- --color-warm-gray: #F5F5F5;
266
- --color-dark-gray: #424242;
267
- --background-primary: linear-gradient(135deg, #FAFAFA 0%, #F5F5F5 50%, #EEEEEE 100%);
268
- --background-accent: linear-gradient(135deg, #FCE4EC 0%, #F8BBD9 100%);
269
- --text-primary: #212121;
270
- --text-secondary: #757575;
271
- --shadow-soft: 0 4px 20px rgba(0, 0, 0, 0.08);
272
- --shadow-medium: 0 8px 30px rgba(0, 0, 0, 0.12);
273
- --border-radius: 16px;
274
- }
275
-
276
- /* ์ „์ฒด ๋ฐฐ๊ฒฝ */
277
- footer {visibility: hidden;}
278
- .gradio-container {
279
- background: var(--background-primary) !important;
280
- min-height: 100vh;
281
- font-family: 'Inter', 'Noto Sans KR', sans-serif;
282
- }
283
-
284
- /* ํƒ€์ดํ‹€ ์Šคํƒ€์ผ */
285
- .title {
286
- color: var(--text-primary) !important;
287
- font-size: 3rem !important;
288
- font-weight: 700 !important;
289
- text-align: center;
290
- margin: 2rem 0;
291
- background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-rose) 50%, var(--color-gold) 100%);
292
- -webkit-background-clip: text;
293
- -webkit-text-fill-color: transparent;
294
- background-clip: text;
295
- letter-spacing: -0.02em;
296
- }
297
-
298
- .subtitle {
299
- color: var(--text-secondary) !important;
300
- font-size: 1.2rem !important;
301
- text-align: center;
302
- margin-bottom: 2rem;
303
- font-weight: 400;
304
- }
305
-
306
- .collection-link {
307
- text-align: center;
308
- margin-bottom: 2rem;
309
- font-size: 1rem;
310
- }
311
-
312
- .collection-link a {
313
- color: var(--color-primary);
314
- text-decoration: none;
315
- transition: all 0.3s ease;
316
- font-weight: 500;
317
- border-bottom: 1px solid transparent;
318
- }
319
-
320
- .collection-link a:hover {
321
- color: var(--color-rose);
322
- border-bottom-color: var(--color-rose);
323
- }
324
-
325
- /* ์‹ฌํ”Œํ•œ ์นด๋“œ ์Šคํƒ€์ผ */
326
- .model-description {
327
- background: rgba(255, 255, 255, 0.9);
328
- border: 1px solid rgba(233, 30, 99, 0.1);
329
- border-radius: var(--border-radius);
330
- padding: 2rem;
331
- margin: 1.5rem 0;
332
- box-shadow: var(--shadow-soft);
333
- backdrop-filter: blur(10px);
334
- -webkit-backdrop-filter: blur(10px);
335
- }
336
-
337
- .model-description p {
338
- color: var(--text-primary) !important;
339
- font-size: 1rem;
340
- line-height: 1.6;
341
- margin: 0;
342
- }
343
-
344
- /* ๋ฒ„ํŠผ ์Šคํƒ€์ผ */
345
- button.primary {
346
- background: var(--background-accent) !important;
347
- color: var(--color-primary) !important;
348
- border: 1px solid var(--color-accent) !important;
349
- border-radius: 12px !important;
350
- box-shadow: var(--shadow-soft) !important;
351
- transition: all 0.2s ease !important;
352
- font-weight: 600 !important;
353
- font-size: 0.95rem !important;
354
- }
355
-
356
- button.primary:hover {
357
- background: linear-gradient(135deg, var(--color-accent) 0%, var(--color-secondary) 100%) !important;
358
- transform: translateY(-1px) !important;
359
- box-shadow: var(--shadow-medium) !important;
360
- }
361
-
362
- /* ์ž…๋ ฅ ์ปจํ…Œ์ด๋„ˆ */
363
- .input-container {
364
- background: rgba(255, 255, 255, 0.8);
365
- border: 1px solid rgba(233, 30, 99, 0.15);
366
- border-radius: var(--border-radius);
367
- padding: 1.5rem;
368
- margin-bottom: 1.5rem;
369
- box-shadow: var(--shadow-soft);
370
- backdrop-filter: blur(10px);
371
- -webkit-backdrop-filter: blur(10px);
372
- }
373
-
374
- /* ๊ณ ๊ธ‰ ์„ค์ • */
375
- .advanced-settings {
376
- background: rgba(255, 255, 255, 0.6);
377
- border: 1px solid rgba(233, 30, 99, 0.1);
378
- border-radius: var(--border-radius);
379
- padding: 1.5rem;
380
- margin-top: 1rem;
381
- box-shadow: var(--shadow-soft);
382
- backdrop-filter: blur(8px);
383
- -webkit-backdrop-filter: blur(8px);
384
- }
385
-
386
- /* ์˜ˆ์‹œ ์˜์—ญ */
387
- .example-region {
388
- background: rgba(252, 228, 236, 0.3);
389
- border: 1px solid rgba(233, 30, 99, 0.15);
390
- border-radius: var(--border-radius);
391
- padding: 1.5rem;
392
- margin-top: 1rem;
393
- box-shadow: var(--shadow-soft);
394
- }
395
-
396
- /* ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ์นธ ์Šคํƒ€์ผ */
397
- .large-prompt textarea {
398
- min-height: 120px !important;
399
- font-size: 15px !important;
400
- line-height: 1.5 !important;
401
- background: rgba(255, 255, 255, 0.9) !important;
402
- border: 2px solid rgba(233, 30, 99, 0.2) !important;
403
- border-radius: 12px !important;
404
- color: var(--text-primary) !important;
405
- transition: all 0.3s ease !important;
406
- padding: 1rem !important;
407
- }
408
-
409
- .large-prompt textarea:focus {
410
- border-color: var(--color-primary) !important;
411
- box-shadow: 0 0 0 3px rgba(233, 30, 99, 0.1) !important;
412
- outline: none !important;
413
- }
414
-
415
- .large-prompt textarea::placeholder {
416
- color: var(--text-secondary) !important;
417
- font-style: italic;
418
- }
419
-
420
- /* ์ƒ์„ฑ ๋ฒ„ํŠผ */
421
- .small-generate-btn {
422
- max-width: 140px !important;
423
- height: 48px !important;
424
- font-size: 15px !important;
425
- padding: 12px 24px !important;
426
- border-radius: 12px !important;
427
- font-weight: 600 !important;
428
- }
429
-
430
- /* ํ”„๋กฌํ”„ํŠธ ์ฆ๊ฐ• ์„น์…˜ */
431
- .prompt-enhance-section {
432
- background: linear-gradient(135deg, rgba(255, 183, 77, 0.1) 0%, rgba(252, 228, 236, 0.2) 100%);
433
- border: 1px solid rgba(255, 183, 77, 0.3);
434
- border-radius: var(--border-radius);
435
- padding: 1.2rem;
436
- margin-top: 1rem;
437
- box-shadow: var(--shadow-soft);
438
- }
439
-
440
- /* ์Šคํƒ€์ผ ํ”„๋ฆฌ์…‹ ์„น์…˜ */
441
- .style-preset-section {
442
- background: linear-gradient(135deg, rgba(248, 187, 217, 0.15) 0%, rgba(252, 228, 236, 0.2) 100%);
443
- border: 1px solid rgba(233, 30, 99, 0.2);
444
- border-radius: var(--border-radius);
445
- padding: 1.2rem;
446
- margin-top: 1rem;
447
- box-shadow: var(--shadow-soft);
448
- }
449
-
450
- /* ๋ผ๋ฒจ ํ…์ŠคํŠธ */
451
- label {
452
- color: var(--text-primary) !important;
453
- font-weight: 600 !important;
454
- font-size: 0.95rem !important;
455
- }
456
-
457
- /* ์ •๋ณด ํ…์ŠคํŠธ */
458
- .gr-info, .gr-textbox-info {
459
- color: var(--text-secondary) !important;
460
- font-size: 0.85rem !important;
461
- line-height: 1.4 !important;
462
- }
463
-
464
- /* ์˜ˆ์‹œ ๋งˆํฌ๋‹ค์šด */
465
- .example-region h3 {
466
- color: var(--text-primary) !important;
467
- font-weight: 600 !important;
468
- margin-bottom: 1rem !important;
469
- }
470
-
471
- /* ํผ ์š”์†Œ๋“ค */
472
- input[type="radio"], input[type="checkbox"] {
473
- accent-color: var(--color-primary) !important;
474
- }
475
-
476
- input[type="range"] {
477
- accent-color: var(--color-primary) !important;
478
- }
479
-
480
- /* ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€ ์ปจํ…Œ์ด๋„ˆ */
481
- .image-container {
482
- border-radius: var(--border-radius) !important;
483
- overflow: hidden !important;
484
- box-shadow: var(--shadow-medium) !important;
485
- background: rgba(255, 255, 255, 0.9) !important;
486
- border: 1px solid rgba(233, 30, 99, 0.1) !important;
487
- }
488
-
489
- /* ์Šฌ๋ผ์ด๋” ์ปจํ…Œ์ด๋„ˆ ์Šคํƒ€์ผ๋ง */
490
- .gr-slider {
491
- margin: 0.5rem 0 !important;
492
- }
493
-
494
- /* ์•„์ฝ”๋””์–ธ ์Šคํƒ€์ผ */
495
- .gr-accordion {
496
- border: 1px solid rgba(233, 30, 99, 0.15) !important;
497
- border-radius: var(--border-radius) !important;
498
- background: rgba(255, 255, 255, 0.7) !important;
499
- }
500
-
501
- .gr-accordion-header {
502
- background: var(--background-accent) !important;
503
- color: var(--color-primary) !important;
504
- font-weight: 600 !important;
505
- border-radius: var(--border-radius) var(--border-radius) 0 0 !important;
506
- }
507
-
508
- /* ๋ถ€๋“œ๋Ÿฌ์šด ์• ๋‹ˆ๋ฉ”์ด์…˜ */
509
- .model-description, .input-container, .prompt-enhance-section, .style-preset-section {
510
- animation: fadeInUp 0.4s ease-out;
511
- }
512
-
513
- @keyframes fadeInUp {
514
- from {
515
- opacity: 0;
516
- transform: translateY(20px);
517
- }
518
- to {
519
- opacity: 1;
520
- transform: translateY(0);
521
- }
522
- }
523
-
524
- /* ์ „์ฒด์ ์ธ ํ…์ŠคํŠธ ๊ฐ€๋…์„ฑ ํ–ฅ์ƒ */
525
- * {
526
- -webkit-font-smoothing: antialiased;
527
- -moz-osx-font-smoothing: grayscale;
528
- }
529
-
530
- /* ๋“œ๋กญ๋‹ค์šด ๋ฐ ์…€๋ ‰ํŠธ ์Šคํƒ€์ผ */
531
- select, .gr-dropdown {
532
- background: rgba(255, 255, 255, 0.9) !important;
533
- border: 1px solid rgba(233, 30, 99, 0.2) !important;
534
- border-radius: 8px !important;
535
- color: var(--text-primary) !important;
536
- }
537
-
538
- /* ์ฒดํฌ๋ฐ•์Šค์™€ ๋ผ๋””์˜ค ๋ฒ„ํŠผ ๊ฐœ์„  */
539
- .gr-checkbox, .gr-radio {
540
- background: transparent !important;
541
- }
542
-
543
- /* ์ „์ฒด ์ปจํ…Œ์ด๋„ˆ ์—ฌ๋ฐฑ ์กฐ์ • */
544
- .gr-container {
545
- max-width: 1200px !important;
546
- margin: 0 auto !important;
547
- padding: 2rem 1rem !important;
548
- }
549
-
550
- /* ๋ชจ๋ฐ”์ผ ๋ฐ˜์‘ํ˜• */
551
- @media (max-width: 768px) {
552
- .title {
553
- font-size: 2.2rem !important;
554
- }
555
-
556
- .model-description, .input-container, .advanced-settings, .example-region {
557
- padding: 1rem !important;
558
- margin: 1rem 0 !important;
559
- }
560
-
561
- .large-prompt textarea {
562
- min-height: 100px !important;
563
- font-size: 14px !important;
564
- }
565
- }
566
- """
567
-
568
- # ===== Gradio UI =====
569
- def create_interface():
570
- with gr.Blocks(css=custom_css, analytics_enabled=False) as demo:
571
- with gr.Group(elem_classes="model-description"):
572
- gr.HTML("""
573
- <p>
574
- <strong>Mr. CHO CS</strong><br>
575
- <small style="opacity: 0.8;">๋ณธ ๋ชจ๋ธ์€ ์—ฐ๊ตฌ ๋ชฉ์ ์œผ๋กœ ํŠน์ •์ธ์˜ ์–ผ๊ตด๊ณผ ์™ธ๋ชจ๋ฅผ LoRA ๊ธฐ์ˆ ๋กœ ํ•™์Šตํ•œ ๋ชจ๋ธ์ž…๋‹ˆ๋‹ค.๋ชฉ์  ์™ธ์˜ ์šฉ๋„๋กœ ๋ฌด๋‹จ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋„๋ก ์œ ์˜ํ•ด ์ฃผ์„ธ์š”. ํ”„๋กฌํ”„ํŠธ์— 'cho'์„ ํฌํ•จํ•˜์—ฌ ์ฃผ์„ธ์š”.</small><br><br>
576
- """)
577
-
578
- # ===== ๋ฉ”์ธ ์ž…๋ ฅ =====
579
- with gr.Column():
580
- with gr.Row(elem_classes="input-container"):
581
- with gr.Column(scale=4):
582
- user_prompt = gr.Text(
583
- label="Prompt (ํ”„๋กฌํ”„ํŠธ)",
584
- max_lines=5,
585
- value=examples[0],
586
- elem_classes="large-prompt",
587
- placeholder="Enter your image description here... (์ด๋ฏธ์ง€ ์„ค๋ช…์„ ์ž…๋ ฅํ•˜์„ธ์š”...)"
588
- )
589
- with gr.Column(scale=1):
590
- run_button = gr.Button(
591
- "Generate (์ƒ์„ฑ)",
592
- variant="primary",
593
- elem_classes="small-generate-btn"
594
- )
595
-
596
- # ํ”„๋กฌํ”„ํŠธ ์ฆ๊ฐ• ์˜ต์…˜ (์ƒ์„ฑ ๋ฒ„ํŠผ ์•„๋ž˜)
597
- with gr.Group(elem_classes="prompt-enhance-section"):
598
- enhance_prompt_checkbox = gr.Checkbox(
599
- label="๐Ÿš€ Prompt Enhancement (ํ”„๋กฌํ”„ํŠธ ์ฆ๊ฐ•)",
600
- value=False,
601
- info="Automatically improve your prompt using OpenAI API for high-quality image generation (OpenAI API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ณ ํ’ˆ์งˆ ์ด๋ฏธ์ง€ ์ƒ์„ฑ์„ ์œ„ํ•ด ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ž๋™์œผ๋กœ ๊ฐœ์„ ํ•ฉ๋‹ˆ๋‹ค)"
602
- )
603
 
604
- # ์Šคํƒ€์ผ ํ”„๋ฆฌ์…‹ ์„น์…˜
605
- with gr.Group(elem_classes="style-preset-section"):
606
- style_select = gr.Radio(
607
- label="๐ŸŽจ Style Preset (์Šคํƒ€์ผ ํ”„๋ฆฌ์…‹)",
608
- choices=list(STYLE_PRESETS.keys()),
609
- value="None",
610
- interactive=True
611
- )
612
-
613
- result_image = gr.Image(label="Generated Image (์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€)")
614
- seed_output = gr.Number(label="Seed (์‹œ๋“œ๊ฐ’)")
615
-
616
- # ===== ๊ณ ๊ธ‰ ์„ค์ • =====
617
- with gr.Accordion("Advanced Settings (๊ณ ๊ธ‰ ์„ค์ •)", open=False, elem_classes="advanced-settings"):
618
- seed = gr.Slider(label="Seed (์‹œ๋“œ๊ฐ’)", minimum=0, maximum=MAX_SEED, step=1, value=42)
619
- randomize_seed = gr.Checkbox(label="Randomize seed (์‹œ๋“œ๊ฐ’ ๋ฌด์ž‘์œ„)", value=True)
620
- with gr.Row():
621
- width = gr.Slider(label="Width (๊ฐ€๋กœ)", minimum=256, maximum=MAX_IMAGE_SIZE, step=32, value=1024)
622
- height = gr.Slider(label="Height (์„ธ๋กœ)", minimum=256, maximum=MAX_IMAGE_SIZE, step=32, value=768)
623
- with gr.Row():
624
- guidance_scale = gr.Slider(label="Guidance scale (๊ฐ€์ด๋˜์Šค ์Šค์ผ€์ผ)", minimum=0.0, maximum=10.0, step=0.1, value=3.5)
625
- num_inference_steps = gr.Slider(label="Inference steps (์ถ”๋ก  ๋‹จ๊ณ„)", minimum=1, maximum=50, step=1, value=30)
626
- lora_scale = gr.Slider(label="LoRA scale (LoRA ์Šค์ผ€์ผ)", minimum=0.0, maximum=1.0, step=0.1, value=1.0)
627
-
628
- # ===== ์˜ˆ์‹œ ์˜์—ญ =====
629
- with gr.Group(elem_classes="example-region"):
630
- gr.Markdown("### Examples (์˜ˆ์‹œ)")
631
- gr.Examples(examples=examples, inputs=user_prompt, cache_examples=False)
632
-
633
- # ===== ์ด๋ฒคํŠธ =====
634
- run_button.click(
635
- fn=generate_image,
636
- inputs=[
637
- user_prompt,
638
- style_select,
639
- enhance_prompt_checkbox,
640
- seed,
641
- randomize_seed,
642
- width,
643
- height,
644
- guidance_scale,
645
- num_inference_steps,
646
- lora_scale,
647
- ],
648
- outputs=[result_image, seed_output],
649
- )
650
-
651
- return demo
652
 
653
- # ===== ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ =====
654
  if __name__ == "__main__":
655
- demo = create_interface()
656
- demo.queue()
657
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
+ import sys
3
+ import streamlit as st
4
+ from tempfile import NamedTemporaryFile
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
+ def main():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  try:
8
+ # Get the code from secrets
9
+ code = os.environ.get("MAIN_CODE")
10
+
11
+ if not code:
12
+ st.error("โš ๏ธ The application code wasn't found in secrets. Please add the MAIN_CODE secret.")
13
+ return
14
+
15
+ # Create a temporary Python file
16
+ with NamedTemporaryFile(suffix='.py', delete=False, mode='w') as tmp:
17
+ tmp.write(code)
18
+ tmp_path = tmp.name
19
+
20
+ # Execute the code
21
+ exec(compile(code, tmp_path, 'exec'), globals())
22
+
23
+ # Clean up the temporary file
24
+ try:
25
+ os.unlink(tmp_path)
26
+ except:
27
+ pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
+ except Exception as e:
30
+ st.error(f"โš ๏ธ Error loading or executing the application: {str(e)}")
31
+ import traceback
32
+ st.code(traceback.format_exc())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
 
34
  if __name__ == "__main__":
35
+ main()