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()