openfree commited on
Commit
43a7c4d
ยท
verified ยท
1 Parent(s): b3fb122

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +497 -1
app.py CHANGED
@@ -1,2 +1,498 @@
1
  import os
2
- exec(os.environ.get('APP'))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
+ import gc
3
+ import uuid
4
+ import random
5
+ import tempfile
6
+ import time
7
+ from datetime import datetime
8
+ from typing import Any
9
+ from huggingface_hub import login, hf_hub_download
10
+ import spaces
11
+
12
+ import gradio as gr
13
+ import numpy as np
14
+ import torch
15
+ from PIL import Image, ImageDraw, ImageFont
16
+ from diffusers import FluxPipeline
17
+ from transformers import pipeline
18
+
19
+ # ๋ฉ”๋ชจ๋ฆฌ ์ •๋ฆฌ ํ•จ์ˆ˜
20
+ def clear_memory():
21
+ gc.collect()
22
+ try:
23
+ if torch.cuda.is_available():
24
+ with torch.cuda.device(0):
25
+ torch.cuda.empty_cache()
26
+ except:
27
+ pass
28
+
29
+ # GPU ์„ค์ •
30
+ device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
31
+
32
+ if torch.cuda.is_available():
33
+ try:
34
+ with torch.cuda.device(0):
35
+ torch.cuda.empty_cache()
36
+ torch.backends.cudnn.benchmark = True
37
+ torch.backends.cuda.matmul.allow_tf32 = True
38
+ except:
39
+ print("Warning: Could not configure CUDA settings")
40
+
41
+ # HF ํ† ํฐ ์„ค์ •
42
+ HF_TOKEN = os.getenv("HF_TOKEN")
43
+ if HF_TOKEN is None:
44
+ raise ValueError("Please set the HF_TOKEN environment variable")
45
+
46
+ try:
47
+ login(token=HF_TOKEN)
48
+ except Exception as e:
49
+ raise ValueError(f"Failed to login to Hugging Face: {str(e)}")
50
+
51
+
52
+
53
+ translator = pipeline("translation", model="Helsinki-NLP/opus-mt-ko-en", device=-1) # CPU์—์„œ ์‹คํ–‰
54
+
55
+ def translate_to_english(text: str) -> str:
56
+ """ํ•œ๊ธ€ ํ…์ŠคํŠธ๋ฅผ ์˜์–ด๋กœ ๋ฒˆ์—ญ"""
57
+ try:
58
+ if any(ord('๊ฐ€') <= ord(char) <= ord('ํžฃ') for char in text):
59
+ translated = translator(text, max_length=128)[0]['translation_text']
60
+ print(f"Translated '{text}' to '{translated}'")
61
+ return translated
62
+ return text
63
+ except Exception as e:
64
+ print(f"Translation error: {str(e)}")
65
+ return text
66
+
67
+
68
+ # FLUX ํŒŒ์ดํ”„๋ผ์ธ ์ดˆ๊ธฐํ™” ๋ถ€๋ถ„ ์ˆ˜์ •
69
+ print("Initializing FLUX pipeline...")
70
+ try:
71
+ pipe = FluxPipeline.from_pretrained(
72
+ "black-forest-labs/FLUX.1-dev",
73
+ torch_dtype=torch.float16,
74
+ use_auth_token=HF_TOKEN
75
+ )
76
+ print("FLUX pipeline initialized successfully")
77
+
78
+ # ๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™” ์„ค์ •
79
+ pipe.enable_attention_slicing(slice_size=1)
80
+
81
+ # GPU ์„ค์ •
82
+ if torch.cuda.is_available():
83
+ pipe = pipe.to("cuda:0")
84
+ torch.cuda.empty_cache()
85
+ torch.backends.cudnn.benchmark = True
86
+ torch.backends.cuda.matmul.allow_tf32 = True
87
+
88
+ print("Pipeline optimization settings applied")
89
+
90
+ except Exception as e:
91
+ print(f"Error initializing FLUX pipeline: {str(e)}")
92
+ raise
93
+
94
+ # LoRA ๊ฐ€์ค‘์น˜ ๋กœ๋“œ ๋ถ€๋ถ„ ์ˆ˜์ •
95
+ print("Loading LoRA weights...")
96
+ try:
97
+ # ๋กœ์ปฌ LoRA ํŒŒ์ผ์˜ ์ ˆ๋Œ€ ๊ฒฝ๋กœ ํ™•์ธ
98
+ current_dir = os.path.dirname(os.path.abspath(__file__))
99
+ lora_path = os.path.join(current_dir, "myt-flux-fantasy.safetensors")
100
+
101
+ if not os.path.exists(lora_path):
102
+ raise FileNotFoundError(f"LoRA file not found at: {lora_path}")
103
+
104
+ print(f"Loading LoRA weights from: {lora_path}")
105
+
106
+ # LoRA ๊ฐ€์ค‘์น˜ ๋กœ๋“œ
107
+ pipe.load_lora_weights(lora_path)
108
+ pipe.fuse_lora(lora_scale=0.75) # lora_scale ๊ฐ’ ์กฐ์ •
109
+
110
+ # ๋ฉ”๋ชจ๋ฆฌ ์ •๋ฆฌ
111
+ torch.cuda.empty_cache()
112
+ gc.collect()
113
+
114
+ print("LoRA weights loaded and fused successfully")
115
+ print(f"Current device: {pipe.device}")
116
+
117
+ except Exception as e:
118
+ print(f"Error loading LoRA weights: {str(e)}")
119
+ print(f"Full error details: {repr(e)}")
120
+ raise ValueError(f"Failed to load LoRA weights: {str(e)}")
121
+
122
+
123
+ @spaces.GPU(duration=60)
124
+ def generate_image(
125
+ prompt: str,
126
+ seed: int,
127
+ randomize_seed: bool,
128
+ width: int,
129
+ height: int,
130
+ guidance_scale: float,
131
+ num_inference_steps: int,
132
+ progress: gr.Progress = gr.Progress()
133
+ ):
134
+ try:
135
+ clear_memory()
136
+
137
+ translated_prompt = translate_to_english(prompt)
138
+ print(f"Processing prompt: {translated_prompt}")
139
+
140
+ if randomize_seed:
141
+ seed = random.randint(0, MAX_SEED)
142
+
143
+ generator = torch.Generator(device=device).manual_seed(seed)
144
+
145
+ print(f"Current device: {pipe.device}")
146
+ print(f"Starting image generation...")
147
+
148
+ with torch.inference_mode(), torch.cuda.amp.autocast(enabled=True):
149
+ image = pipe(
150
+ prompt=translated_prompt,
151
+ width=width,
152
+ height=height,
153
+ num_inference_steps=num_inference_steps,
154
+ guidance_scale=guidance_scale,
155
+ generator=generator,
156
+ num_images_per_prompt=1,
157
+ ).images[0]
158
+
159
+ filepath = save_generated_image(image, translated_prompt)
160
+ print(f"Image generated and saved to: {filepath}")
161
+ return image, seed
162
+
163
+ except Exception as e:
164
+ print(f"Generation error: {str(e)}")
165
+ print(f"Full error details: {repr(e)}")
166
+ raise gr.Error(f"Image generation failed: {str(e)}")
167
+ finally:
168
+ clear_memory()
169
+
170
+ # ์ €์žฅ ๋””๋ ‰ํ† ๋ฆฌ ์„ค์ •
171
+ SAVE_DIR = "saved_images"
172
+ if not os.path.exists(SAVE_DIR):
173
+ os.makedirs(SAVE_DIR, exist_ok=True)
174
+
175
+ MAX_SEED = np.iinfo(np.int32).max
176
+ MAX_IMAGE_SIZE = 1024
177
+
178
+ def save_generated_image(image, prompt):
179
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
180
+ unique_id = str(uuid.uuid4())[:8]
181
+ filename = f"{timestamp}_{unique_id}.png"
182
+ filepath = os.path.join(SAVE_DIR, filename)
183
+ image.save(filepath)
184
+ return filepath
185
+
186
+
187
+
188
+ def add_text_with_stroke(draw, text, x, y, font, text_color, stroke_width):
189
+ """ํ…์ŠคํŠธ์— ์™ธ๊ณฝ์„ ์„ ์ถ”๊ฐ€ํ•˜๋Š” ํ•จ์ˆ˜"""
190
+ for adj_x in range(-stroke_width, stroke_width + 1):
191
+ for adj_y in range(-stroke_width, stroke_width + 1):
192
+ draw.text((x + adj_x, y + adj_y), text, font=font, fill=text_color)
193
+
194
+ def add_text_to_image(
195
+ input_image,
196
+ text,
197
+ font_size,
198
+ color,
199
+ opacity,
200
+ x_position,
201
+ y_position,
202
+ thickness,
203
+ text_position_type,
204
+ font_choice
205
+ ):
206
+ try:
207
+ if input_image is None or text.strip() == "":
208
+ return input_image
209
+
210
+ if not isinstance(input_image, Image.Image):
211
+ if isinstance(input_image, np.ndarray):
212
+ image = Image.fromarray(input_image)
213
+ else:
214
+ raise ValueError("Unsupported image type")
215
+ else:
216
+ image = input_image.copy()
217
+
218
+ if image.mode != 'RGBA':
219
+ image = image.convert('RGBA')
220
+
221
+ font_files = {
222
+ "Default": "DejaVuSans.ttf",
223
+ "Korean Regular": "ko-Regular.ttf"
224
+ }
225
+
226
+ try:
227
+ font_file = font_files.get(font_choice, "DejaVuSans.ttf")
228
+ font = ImageFont.truetype(font_file, int(font_size))
229
+ except Exception as e:
230
+ print(f"Font loading error ({font_choice}): {str(e)}")
231
+ font = ImageFont.load_default()
232
+
233
+ color_map = {
234
+ 'White': (255, 255, 255),
235
+ 'Black': (0, 0, 0),
236
+ 'Red': (255, 0, 0),
237
+ 'Green': (0, 255, 0),
238
+ 'Blue': (0, 0, 255),
239
+ 'Yellow': (255, 255, 0),
240
+ 'Purple': (128, 0, 128)
241
+ }
242
+ rgb_color = color_map.get(color, (255, 255, 255))
243
+
244
+ temp_draw = ImageDraw.Draw(image)
245
+ text_bbox = temp_draw.textbbox((0, 0), text, font=font)
246
+ text_width = text_bbox[2] - text_bbox[0]
247
+ text_height = text_bbox[3] - text_bbox[1]
248
+
249
+ actual_x = int((image.width - text_width) * (x_position / 100))
250
+ actual_y = int((image.height - text_height) * (y_position / 100))
251
+
252
+ text_color = (*rgb_color, int(opacity))
253
+
254
+ txt_overlay = Image.new('RGBA', image.size, (255, 255, 255, 0))
255
+ draw = ImageDraw.Draw(txt_overlay)
256
+
257
+ add_text_with_stroke(
258
+ draw,
259
+ text,
260
+ actual_x,
261
+ actual_y,
262
+ font,
263
+ text_color,
264
+ int(thickness)
265
+ )
266
+ output_image = Image.alpha_composite(image, txt_overlay)
267
+
268
+ output_image = output_image.convert('RGB')
269
+
270
+ return output_image
271
+
272
+ except Exception as e:
273
+ print(f"Error in add_text_to_image: {str(e)}")
274
+ return input_image
275
+
276
+
277
+ css = """
278
+ footer {display: none}
279
+ .main-title {
280
+ text-align: center;
281
+ margin: 1em 0;
282
+ padding: 1.5em;
283
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
284
+ border-radius: 15px;
285
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
286
+ }
287
+ .main-title h1 {
288
+ color: #2196F3;
289
+ font-size: 2.8em;
290
+ margin-bottom: 0.3em;
291
+ font-weight: 700;
292
+ }
293
+ .main-title p {
294
+ color: #555;
295
+ font-size: 1.3em;
296
+ line-height: 1.4;
297
+ }
298
+ .container {
299
+ max-width: 1200px;
300
+ margin: auto;
301
+ padding: 20px;
302
+ }
303
+ .input-panel, .output-panel {
304
+ background: white;
305
+ padding: 1.5em;
306
+ border-radius: 12px;
307
+ box-shadow: 0 2px 8px rgba(0,0,0,0.08);
308
+ margin-bottom: 1em;
309
+ }
310
+ """
311
+
312
+ import requests
313
+
314
+ def enhance_prompt(prompt: str) -> str:
315
+ """ํ”„๋กฌํ”„ํŠธ๋ฅผ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์Šคํƒ€์ผ๋กœ ์ฆ๊ฐ•"""
316
+ try:
317
+ # ๊ธฐ๋ณธ ํ’ˆ์งˆ ํ–ฅ์ƒ ํ”„๋กฌํ”„ํŠธ ์ถ”๊ฐ€
318
+ enhancements = [
319
+ "masterpiece, best quality, highly detailed",
320
+ "anime style, animation style",
321
+ "vibrant colors, perfect lighting",
322
+ "professional composition",
323
+ "dynamic pose, expressive features",
324
+ "detailed background, perfect shadows",
325
+ "[trigger]"
326
+ ]
327
+
328
+ # ์• ๋‹ˆ๋ฉ”์ด์…˜ ์Šคํƒ€์ผ ํ”„๋กฌํ”„ํŠธ ๋ณ€ํ™˜
329
+ anime_style_prompt = f"an animated {prompt}, detailed anime art style"
330
+
331
+ # ์ตœ์ข… ํ”„๋กฌํ”„ํŠธ ๊ตฌ์„ฑ
332
+ final_prompt = f"{anime_style_prompt}, {', '.join(enhancements)}"
333
+ print(f"Enhanced prompt: {final_prompt}")
334
+
335
+ return final_prompt
336
+ except Exception as e:
337
+ print(f"Prompt enhancement failed: {str(e)}")
338
+ return prompt
339
+
340
+ # ๊ธฐ์กด์˜ pipeline ์ดˆ๊ธฐํ™” ๋ถ€๋ถ„ ์ œ๊ฑฐ
341
+ # try:
342
+ # prompt_enhancer = pipeline(...)
343
+ # except Exception as e:
344
+ # print(f"Error initializing prompt enhancer: {str(e)}")
345
+ # prompt_enhancer = None
346
+
347
+
348
+ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
349
+ gr.HTML("""
350
+ <div class="main-title">
351
+ <h1>๐ŸŽจ Webtoon Studio</h1>
352
+ <p>Generate webtoon-style images and add text with various styles and positions.</p>
353
+ </div>
354
+ """)
355
+
356
+ with gr.Row():
357
+ with gr.Column(scale=1):
358
+ gen_prompt = gr.Textbox(
359
+ label="Generation Prompt",
360
+ placeholder="Enter your image generation prompt..."
361
+ )
362
+ enhance_btn = gr.Button("โœจ Enhance Prompt", variant="secondary")
363
+
364
+ with gr.Row():
365
+ gen_width = gr.Slider(512, 1024, 768, step=64, label="Width")
366
+ gen_height = gr.Slider(512, 1024, 768, step=64, label="Height")
367
+
368
+ with gr.Row():
369
+ guidance_scale = gr.Slider(1, 20, 7.5, step=0.5, label="Guidance Scale")
370
+ num_steps = gr.Slider(1, 50, 30, step=1, label="Number of Steps")
371
+
372
+ with gr.Row():
373
+ seed = gr.Number(label="Seed", value=-1)
374
+ randomize_seed = gr.Checkbox(label="Randomize Seed", value=True)
375
+
376
+ generate_btn = gr.Button("Generate Image", variant="primary")
377
+
378
+ output_image = gr.Image(
379
+ label="Generated Image",
380
+ type="pil",
381
+ show_download_button=True
382
+ )
383
+ output_seed = gr.Number(label="Used Seed", interactive=False)
384
+
385
+ # ํ…์ŠคํŠธ ์ถ”๊ฐ€ ์„น์…˜
386
+ with gr.Accordion("Text Options", open=False):
387
+ text_input = gr.Textbox(
388
+ label="Text Content",
389
+ placeholder="Enter text to add..."
390
+ )
391
+ text_position_type = gr.Radio(
392
+ choices=["Text Over Image"],
393
+ value="Text Over Image",
394
+ label="Text Position",
395
+ visible=True
396
+ )
397
+ with gr.Row():
398
+ font_choice = gr.Dropdown(
399
+ choices=["Default", "Korean Regular"],
400
+ value="Default",
401
+ label="Font Selection",
402
+ interactive=True
403
+ )
404
+ font_size = gr.Slider(
405
+ minimum=10,
406
+ maximum=200,
407
+ value=40,
408
+ step=5,
409
+ label="Font Size"
410
+ )
411
+ with gr.Row():
412
+ color_dropdown = gr.Dropdown(
413
+ choices=["White", "Black", "Red", "Green", "Blue", "Yellow", "Purple"],
414
+ value="White",
415
+ label="Text Color"
416
+ )
417
+ thickness = gr.Slider(
418
+ minimum=0,
419
+ maximum=10,
420
+ value=1,
421
+ step=1,
422
+ label="Text Thickness"
423
+ )
424
+ with gr.Row():
425
+ opacity_slider = gr.Slider(
426
+ minimum=0,
427
+ maximum=255,
428
+ value=255,
429
+ step=1,
430
+ label="Opacity"
431
+ )
432
+ with gr.Row():
433
+ x_position = gr.Slider(
434
+ minimum=0,
435
+ maximum=100,
436
+ value=50,
437
+ step=1,
438
+ label="Left(0%)~Right(100%)"
439
+ )
440
+ y_position = gr.Slider(
441
+ minimum=0,
442
+ maximum=100,
443
+ value=50,
444
+ step=1,
445
+ label="High(0%)~Low(100%)"
446
+ )
447
+ add_text_btn = gr.Button("Apply Text", variant="primary")
448
+
449
+ # ์ด๋ฒคํŠธ ๋ฐ”์ธ๋”ฉ
450
+ generate_btn.click(
451
+ fn=generate_image,
452
+ inputs=[
453
+ gen_prompt,
454
+ seed,
455
+ randomize_seed,
456
+ gen_width,
457
+ gen_height,
458
+ guidance_scale,
459
+ num_steps,
460
+ ],
461
+ outputs=[output_image, output_seed]
462
+ )
463
+
464
+ add_text_btn.click(
465
+ fn=add_text_to_image,
466
+ inputs=[
467
+ output_image,
468
+ text_input,
469
+ font_size,
470
+ color_dropdown,
471
+ opacity_slider,
472
+ x_position,
473
+ y_position,
474
+ thickness,
475
+ text_position_type,
476
+ font_choice
477
+ ],
478
+ outputs=output_image
479
+ )
480
+
481
+ # ์ด๋ฒคํŠธ ๋ฐ”์ธ๋”ฉ ์ถ”๊ฐ€
482
+ def update_prompt(prompt):
483
+ enhanced = enhance_prompt(prompt)
484
+ return enhanced
485
+
486
+ enhance_btn.click(
487
+ fn=update_prompt,
488
+ inputs=[gen_prompt],
489
+ outputs=[gen_prompt]
490
+ )
491
+
492
+ demo.queue(max_size=5)
493
+ demo.launch(
494
+ server_name="0.0.0.0",
495
+ server_port=7860,
496
+ share=False,
497
+ max_threads=2
498
+ )