seawolf2357 commited on
Commit
55a3a45
Β·
verified Β·
1 Parent(s): a01c472

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +11 -760
app.py CHANGED
@@ -24,763 +24,14 @@ from src.lora_helper import set_single_lora, set_multi_lora, unset_lora
24
  from google import genai
25
  from google.genai import types
26
 
27
- # Initialize the image processor
28
- base_path = "black-forest-labs/FLUX.1-dev"
29
- lora_base_path = "./models"
30
-
31
- # System prompt that will be hidden from users but automatically added to their input
32
- SYSTEM_PROMPT = "Ghibli Studio style, Charming hand-drawn anime-style illustration"
33
-
34
- # λ‘œκΉ… μ„€μ •
35
- logging.basicConfig(
36
- level=logging.DEBUG,
37
- format='%(asctime)s - %(levelname)s - %(message)s'
38
- )
39
-
40
- # Load the model
41
- pipe = FluxPipeline.from_pretrained(base_path, torch_dtype=torch.bfloat16)
42
- transformer = FluxTransformer2DModel.from_pretrained(base_path, subfolder="transformer", torch_dtype=torch.bfloat16)
43
- pipe.transformer = transformer
44
- pipe.to("cuda")
45
-
46
- def clear_cache(transformer):
47
- for name, attn_processor in transformer.attn_processors.items():
48
- attn_processor.bank_kv.clear()
49
-
50
- #######################################
51
- # Utility Functions
52
- #######################################
53
-
54
- # Simple Timer Class
55
- class timer:
56
- def __init__(self, method_name="timed process"):
57
- self.method = method_name
58
- def __enter__(self):
59
- self.start = time.time()
60
- print(f"[TIMER] {self.method} starts")
61
- def __exit__(self, exc_type, exc_val, exc_tb):
62
- end = time.time()
63
- print(f"[TIMER] {self.method} took {round(end - self.start, 2)}s")
64
-
65
- # κ°„λ‹¨ν•œ λ²ˆμ—­ κΈ°λŠ₯ (ν•œκΈ€ -> μ˜μ–΄)
66
- def maybe_translate_to_english(text: str) -> str:
67
- """
68
- ν…μŠ€νŠΈμ— ν•œκΈ€μ΄ ν¬ν•¨λ˜μ–΄ 있으면 μ˜μ–΄λ‘œ λ²ˆμ—­, μ•„λ‹ˆλ©΄ κ·ΈλŒ€λ‘œ λ°˜ν™˜
69
- """
70
- if not text or not re.search("[κ°€-힣]", text):
71
- return text
72
-
73
- try:
74
- # κ°„λ‹¨ν•œ λ²ˆμ—­ κ·œμΉ™ (μ‹€μ œ ν”„λ‘œλ•μ…˜μ—μ„œλŠ” API μ‚¬μš© ꢌμž₯)
75
- translations = {
76
- "μ•ˆλ…•ν•˜μ„Έμš”": "Hello",
77
- "ν™˜μ˜ν•©λ‹ˆλ‹€": "Welcome",
78
- "μ•„λ¦„λ‹€μš΄ λ‹Ήμ‹ ": "Beautiful You",
79
- "μ•ˆλ…•": "Hello",
80
- "고양이": "Cat",
81
- "λ°°λ„ˆ": "Banner",
82
- "μ¬κΈ€λΌμŠ€": "Sunglasses",
83
- "μ°©μš©ν•œ": "wearing",
84
- "흰색": "white"
85
- }
86
-
87
- # 전체 λ¬Έμž₯에 λŒ€ν•œ λŒ€λž΅μ μΈ λ²ˆμ—­
88
- for kr, en in translations.items():
89
- if kr in text:
90
- text = text.replace(kr, en)
91
-
92
- print(f"[TRANSLATE] Translated Korean text: '{text}'")
93
- return text
94
- except Exception as e:
95
- print(f"[WARNING] Translation failed: {e}")
96
- return text
97
-
98
- def save_binary_file(file_name, data):
99
- with open(file_name, "wb") as f:
100
- f.write(data)
101
-
102
- #######################################
103
- # Gemini API Functions
104
- #######################################
105
-
106
- def generate_by_google_genai(text, file_name, model="gemini-2.0-flash-exp"):
107
- """
108
- - μΆ”κ°€ μ§€μ‹œμ‚¬ν•­(AIP)을 전달해 이미지 기반 νŽΈμ§‘μ„ μˆ˜ν–‰.
109
- - 응닡이 '이미지'λ©΄ μ €μž₯, 'ν…μŠ€νŠΈ'λ©΄ λˆ„μ ν•˜μ—¬ λ°˜ν™˜.
110
- """
111
- # API ν‚€ κ°€μ Έμ˜€κΈ° (ν™˜κ²½ λ³€μˆ˜ GAPI_TOKEN μ‚¬μš©)
112
- api_key = os.getenv("GAPI_TOKEN", None)
113
- if not api_key:
114
- raise ValueError("GAPI_TOKEN is missing. Please set an API key.")
115
-
116
- client = genai.Client(api_key=api_key)
117
- files = [client.files.upload(file=file_name)]
118
-
119
- contents = [
120
- types.Content(
121
- role="user",
122
- parts=[
123
- types.Part.from_uri(
124
- file_uri=files[0].uri,
125
- mime_type=files[0].mime_type,
126
- ),
127
- types.Part.from_text(text=text),
128
- ],
129
- ),
130
- ]
131
-
132
- generate_content_config = types.GenerateContentConfig(
133
- temperature=1,
134
- top_p=0.95,
135
- top_k=40,
136
- max_output_tokens=8192,
137
- response_modalities=["image", "text"],
138
- response_mime_type="text/plain",
139
- )
140
-
141
- text_response = ""
142
- image_path = None
143
-
144
- # μž„μ‹œ νŒŒμΌμ— 이미지 μ €μž₯ κ°€λŠ₯ν•˜λ„λ‘ μ€€λΉ„
145
- with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
146
- temp_path = tmp.name
147
- for chunk in client.models.generate_content_stream(
148
- model=model,
149
- contents=contents,
150
- config=generate_content_config,
151
- ):
152
- if not chunk.candidates or not chunk.candidates[0].content or not chunk.candidates[0].content.parts:
153
- continue
154
-
155
- candidate = chunk.candidates[0].content.parts[0]
156
- # λ§Œμ•½ inline_data(이미지 데이터)κ°€ μžˆλ‹€λ©΄ -> μ‹€μ œ 이미지 νŽΈμ§‘ κ²°κ³Ό
157
- if candidate.inline_data:
158
- save_binary_file(temp_path, candidate.inline_data.data)
159
- print(f"File of mime type {candidate.inline_data.mime_type} saved to: {temp_path}")
160
- image_path = temp_path
161
- # 이미지 ν•œ μž₯만 ν™•λ³΄ν•˜λ©΄ 쀑단
162
- break
163
- else:
164
- # inline_dataκ°€ μ—†μœΌλ©΄ ν…μŠ€νŠΈ λ°μ΄ν„°μ΄λ―€λ‘œ λˆ„μ 
165
- text_response += chunk.text + "\n"
166
-
167
- del files
168
- return image_path, text_response
169
-
170
- def change_text_in_image_two_times(original_image, instruction):
171
- """
172
- Call the text-modification API twice (Google Gemini), returning 2 final variations.
173
- """
174
- if original_image is None:
175
- raise gr.Error("μ²˜λ¦¬ν•  이미지가 μ—†μŠ΅λ‹ˆλ‹€. λ¨Όμ € 이미지λ₯Ό μƒμ„±ν•΄μ£Όμ„Έμš”.")
176
-
177
- results = []
178
- for version_tag in ["(A)", "(B)"]:
179
- mod_instruction = f"{instruction} {version_tag}"
180
- try:
181
- # 이미지 μ €μž₯용 μž„μ‹œ 파일 생성
182
- with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
183
- original_path = tmp.name
184
-
185
- # PIL 이미지 객체인 경우 μ €μž₯
186
- print(f"[DEBUG] Saving image of type {type(original_image)} to temporary file")
187
- if isinstance(original_image, Image.Image):
188
- original_image.save(original_path, format="PNG")
189
- print(f"[DEBUG] Saved image to temporary file: {original_path}")
190
- else:
191
- raise gr.Error(f"μ˜ˆμƒλœ PIL Imageκ°€ μ•„λ‹Œ {type(original_image)} νƒ€μž…μ΄ μ œκ³΅λ˜μ—ˆμŠ΅λ‹ˆλ‹€.")
192
-
193
- print(f"[DEBUG] Google Gemini API에 λ³΄λ‚΄λŠ” μ§€μ‹œμ‚¬ν•­: {mod_instruction}")
194
- image_path, text_response = generate_by_google_genai(
195
- text=mod_instruction,
196
- file_name=original_path
197
- )
198
-
199
- if image_path:
200
- print(f"[DEBUG] Received image from Gemini API: {image_path}")
201
- try:
202
- with open(image_path, "rb") as f:
203
- image_data = f.read()
204
- new_img = Image.open(io.BytesIO(image_data))
205
- results.append(new_img)
206
- except Exception as img_err:
207
- print(f"[ERROR] Failed to process Gemini image: {img_err}")
208
- results.append(original_image)
209
- else:
210
- # λ§Œμ•½ 이미지 응닡이 μ—†κ³ , ν…μŠ€νŠΈλ§Œ 온 경우
211
- print(f"[WARNING] 이미지가 λ°˜ν™˜λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. ν…μŠ€νŠΈ 응닡: {text_response}")
212
- results.append(original_image)
213
-
214
- except Exception as e:
215
- logging.exception(f"Text modification error: {e}")
216
- # 였λ₯˜κ°€ λ‚˜λ„ 원본 이미지라도 λ°˜ν™˜
217
- print(f"[ERROR] ν…μŠ€νŠΈ μˆ˜μ • 쀑 였λ₯˜ λ°œμƒ: {e}")
218
- results.append(original_image)
219
-
220
- return results
221
-
222
- #######################################
223
- # Image Generation Functions
224
- #######################################
225
-
226
- @spaces.GPU()
227
- def single_condition_generate_image(user_prompt, spatial_img, height, width, seed):
228
- # Combine the system prompt with user prompt
229
- full_prompt = f"{SYSTEM_PROMPT}, {user_prompt}" if user_prompt else SYSTEM_PROMPT
230
-
231
- # Set the Ghibli LoRA
232
- lora_path = os.path.join(lora_base_path, "Ghibli.safetensors")
233
- set_single_lora(pipe.transformer, lora_path, lora_weights=[1], cond_size=512)
234
-
235
- # Process the image
236
- spatial_imgs = [spatial_img] if spatial_img else []
237
- image = pipe(
238
- full_prompt,
239
- height=int(height),
240
- width=int(width),
241
- guidance_scale=3.5,
242
- num_inference_steps=25,
243
- max_sequence_length=512,
244
- generator=torch.Generator("cpu").manual_seed(seed),
245
- subject_images=[],
246
- spatial_images=spatial_imgs,
247
- cond_size=512,
248
- ).images[0]
249
- clear_cache(pipe.transformer)
250
- return image
251
-
252
- @spaces.GPU()
253
- def text_rendering_generate_image(user_prompt, input_text, text_color, text_size, text_position, spatial_img, height, width, seed):
254
- """
255
- Generate image with Ghibli style and then send to Gemini API for multilingual text rendering
256
- """
257
- try:
258
- # Step 1: Generate the base image using FLUX
259
- print(f"[DEBUG] Generating base image with FLUX")
260
- full_prompt = f"{SYSTEM_PROMPT}, {user_prompt}" if user_prompt else SYSTEM_PROMPT
261
-
262
- # Set the Ghibli LoRA
263
- lora_path = os.path.join(lora_base_path, "Ghibli.safetensors")
264
- set_single_lora(pipe.transformer, lora_path, lora_weights=[1], cond_size=512)
265
-
266
- # Process the image
267
- spatial_imgs = [spatial_img] if spatial_img else []
268
- base_image = pipe(
269
- full_prompt,
270
- height=int(height),
271
- width=int(width),
272
- guidance_scale=3.5,
273
- num_inference_steps=25,
274
- max_sequence_length=512,
275
- generator=torch.Generator("cpu").manual_seed(seed),
276
- subject_images=[],
277
- spatial_images=spatial_imgs,
278
- cond_size=512,
279
- ).images[0]
280
- clear_cache(pipe.transformer)
281
-
282
- # If no text is provided, return the base image
283
- if not input_text or not input_text.strip():
284
- return [base_image, base_image]
285
-
286
- # Step 2: Build the instruction for Gemini API
287
- instruction = f"Add the text '{input_text}' to this image in {text_color} color"
288
-
289
- # Add position information
290
- if text_position == "top":
291
- instruction += " at the top of the image"
292
- elif text_position == "bottom":
293
- instruction += " at the bottom of the image"
294
- else: # center
295
- instruction += " at the center of the image"
296
-
297
- # Add size information
298
- if text_size <= 40:
299
- instruction += " in small size"
300
- elif text_size <= 120:
301
- instruction += " in medium size"
302
- else:
303
- instruction += " in large size"
304
-
305
- instruction += ". Make sure the text is clearly visible and readable."
306
-
307
- # Step 3: Call Gemini API to generate two variations
308
- print(f"[DEBUG] Sending to Gemini API with instruction: {instruction}")
309
- return change_text_in_image_two_times(base_image, instruction)
310
-
311
- except Exception as e:
312
- logging.exception(f"Text rendering error: {e}")
313
- # Create a dummy image in case of error
314
- dummy_img = Image.new('RGB', (width, height), color=(255, 200, 200))
315
- draw = ImageDraw.Draw(dummy_img)
316
- draw.text((width//2, height//2), f"Error: {str(e)}", fill="black", anchor="mm")
317
- return [dummy_img, dummy_img]
318
-
319
- # Load example images
320
- def load_examples():
321
- examples = []
322
- test_img_dir = "./test_imgs"
323
- example_prompts = [
324
- " ",
325
- "saying 'HELLO' in 'speech bubble'",
326
- "background 'alps'"
327
- ]
328
-
329
- for i, filename in enumerate(["00.jpg", "02.jpg", "03.jpg"]):
330
- img_path = os.path.join(test_img_dir, filename)
331
- if os.path.exists(img_path):
332
- # Use dimensions from original code for each specific example
333
- if filename == "00.jpg":
334
- height, width = 680, 1024
335
- elif filename == "02.jpg":
336
- height, width = 560, 1024
337
- elif filename == "03.jpg":
338
- height, width = 1024, 768
339
- else:
340
- height, width = 768, 768
341
-
342
- examples.append([
343
- example_prompts[i % len(example_prompts)], # User prompt (without system prompt)
344
- Image.open(img_path), # Reference image
345
- height, # Height
346
- width, # Width
347
- i + 1 # Seed
348
- ])
349
-
350
- return examples
351
-
352
- # Load examples for text rendering tab
353
- def load_text_examples():
354
- examples = []
355
- test_img_dir = "./test_imgs"
356
-
357
- example_data = [
358
- {
359
- "prompt": "cute character with speech bubble",
360
- "text": "Hello World!",
361
- "color": "#ffffff",
362
- "size": 72,
363
- "position": "center",
364
- "filename": "00.jpg",
365
- "height": 680,
366
- "width": 1024,
367
- "seed": 123
368
- },
369
- {
370
- "prompt": "landscape with message",
371
- "text": "μ•ˆλ…•ν•˜μ„Έμš”!",
372
- "color": "#ffff00",
373
- "size": 100,
374
- "position": "top",
375
- "filename": "03.jpg",
376
- "height": 1024,
377
- "width": 768,
378
- "seed": 456
379
- },
380
- {
381
- "prompt": "character with subtitles",
382
- "text": "γ“γ‚“γ«γ‘γ―δΈ–η•Œ!",
383
- "color": "#00ffff",
384
- "size": 90,
385
- "position": "bottom",
386
- "filename": "02.jpg",
387
- "height": 560,
388
- "width": 1024,
389
- "seed": 789
390
- }
391
- ]
392
-
393
- for example in example_data:
394
- img_path = os.path.join(test_img_dir, example["filename"])
395
- if os.path.exists(img_path):
396
- examples.append([
397
- example["prompt"],
398
- example["text"],
399
- example["color"],
400
- example["size"],
401
- example["position"],
402
- Image.open(img_path),
403
- example["height"],
404
- example["width"],
405
- example["seed"]
406
- ])
407
-
408
- return examples
409
-
410
- # Function to check API availability - modified to work directly
411
- def check_api_status():
412
- # Check Gemini API availability
413
- api_key = os.getenv("GAPI_TOKEN")
414
- gemini_available = api_key is not None
415
-
416
- if gemini_available:
417
- return """<div class="api-status api-connected">βœ“ Connected to FLUX.1 and Gemini API</div>"""
418
- else:
419
- return """<div class="api-status api-disconnected">βœ— Gemini API connection issue. Please check GAPI_TOKEN environment variable.</div>"""
420
-
421
- # CSS for improved UI
422
- css = """
423
- :root {
424
- --primary-color: #4a6670;
425
- --accent-color: #ff8a65;
426
- --background-color: #f5f5f5;
427
- --card-background: #ffffff;
428
- --text-color: #333333;
429
- --border-radius: 10px;
430
- --shadow: 0 4px 6px rgba(0,0,0,0.1);
431
- }
432
-
433
- body {
434
- background-color: var(--background-color);
435
- color: var(--text-color);
436
- font-family: 'Helvetica Neue', Arial, sans-serif;
437
- }
438
-
439
- .container {
440
- max-width: 1200px;
441
- margin: 0 auto;
442
- padding: 20px;
443
- }
444
-
445
- .gr-header {
446
- background: linear-gradient(135deg, #668796 0%, #4a6670 100%);
447
- padding: 24px;
448
- border-radius: var(--border-radius);
449
- margin-bottom: 24px;
450
- box-shadow: var(--shadow);
451
- text-align: center;
452
- }
453
-
454
- .gr-header h1 {
455
- color: white;
456
- font-size: 2.5rem;
457
- margin: 0;
458
- font-weight: 700;
459
- }
460
-
461
- .gr-header p {
462
- color: rgba(255, 255, 255, 0.9);
463
- font-size: 1.1rem;
464
- margin-top: 8px;
465
- }
466
-
467
- .gr-panel {
468
- background-color: var(--card-background);
469
- border-radius: var(--border-radius);
470
- padding: 16px;
471
- box-shadow: var(--shadow);
472
- }
473
-
474
- .gr-button {
475
- background-color: var(--accent-color);
476
- border: none;
477
- color: white;
478
- padding: 10px 20px;
479
- border-radius: 5px;
480
- font-size: 16px;
481
- font-weight: bold;
482
- cursor: pointer;
483
- transition: transform 0.1s, background-color 0.3s;
484
- }
485
-
486
- .gr-button:hover {
487
- background-color: #ff7043;
488
- transform: translateY(-2px);
489
- }
490
-
491
- .gr-input, .gr-select {
492
- border-radius: 5px;
493
- border: 1px solid #ddd;
494
- padding: 10px;
495
- width: 100%;
496
- }
497
-
498
- .gr-form {
499
- display: grid;
500
- gap: 16px;
501
- }
502
-
503
- .gr-box {
504
- background-color: var(--card-background);
505
- border-radius: var(--border-radius);
506
- padding: 20px;
507
- box-shadow: var(--shadow);
508
- margin-bottom: 20px;
509
- }
510
-
511
- .gr-gallery {
512
- display: grid;
513
- grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
514
- gap: 16px;
515
- }
516
-
517
- .gr-gallery-item {
518
- overflow: hidden;
519
- border-radius: var(--border-radius);
520
- box-shadow: var(--shadow);
521
- transition: transform 0.3s;
522
- }
523
-
524
- .gr-gallery-item:hover {
525
- transform: scale(1.02);
526
- }
527
-
528
- .gr-image {
529
- width: 100%;
530
- height: auto;
531
- object-fit: cover;
532
- }
533
-
534
- .gr-footer {
535
- text-align: center;
536
- margin-top: 40px;
537
- padding: 20px;
538
- color: #666;
539
- font-size: 14px;
540
- }
541
-
542
- .gr-examples-gallery {
543
- margin-top: 20px;
544
- }
545
-
546
- /* Responsive adjustments */
547
- @media (max-width: 768px) {
548
- .gr-header h1 {
549
- font-size: 1.8rem;
550
- }
551
-
552
- .gr-panel {
553
- padding: 12px;
554
- }
555
- }
556
-
557
- /* Ghibli-inspired accent colors */
558
- .gr-accent-1 {
559
- background-color: #95ccd9;
560
- }
561
-
562
- .gr-accent-2 {
563
- background-color: #74ad8c;
564
- }
565
-
566
- .gr-accent-3 {
567
- background-color: #f9c06b;
568
- }
569
-
570
- .text-rendering-options {
571
- background-color: #f0f8ff;
572
- padding: 16px;
573
- border-radius: var(--border-radius);
574
- margin-top: 16px;
575
- }
576
-
577
- .api-status {
578
- font-size: 14px;
579
- color: #666;
580
- text-align: center;
581
- margin-bottom: 10px;
582
- }
583
-
584
- .api-connected {
585
- color: green;
586
- }
587
-
588
- .api-disconnected {
589
- color: red;
590
- }
591
- """
592
-
593
- # Create the Gradio Blocks interface
594
- with gr.Blocks(css=css) as demo:
595
- gr.HTML("""
596
- <div class="gr-header">
597
- <h1>✨ Ghibli Multilingual Text-Rendering ✨</h1>
598
- <p>Transform your ideas into magical Ghibli-inspired artwork with multilingual text</p>
599
- </div>
600
- """)
601
-
602
- # API Status - 직접 ν˜ΈμΆœν•΄μ„œ 초기 μƒνƒœ μ„€μ •
603
- api_status = gr.Markdown(check_api_status(), visible=True)
604
-
605
- with gr.Tabs():
606
- with gr.Tab("Create Ghibli Art"):
607
- with gr.Row():
608
- with gr.Column(scale=1):
609
- gr.HTML("""
610
- <div class="gr-box">
611
- <h3>🎨 Your Creative Input</h3>
612
- <p>Describe what you want to see in your Ghibli-inspired image</p>
613
- </div>
614
- """)
615
-
616
- user_prompt = gr.Textbox(
617
- label="Your description",
618
- placeholder="Describe what you want to see (e.g., a cat sitting by the window)",
619
- lines=2
620
- )
621
-
622
- spatial_img = gr.Image(
623
- label="Reference Image (Optional)",
624
- type="pil",
625
- elem_classes="gr-image-upload"
626
- )
627
-
628
- with gr.Group():
629
- with gr.Row():
630
- height = gr.Slider(minimum=256, maximum=1024, step=64, label="Height", value=768)
631
- width = gr.Slider(minimum=256, maximum=1024, step=64, label="Width", value=768)
632
-
633
- seed = gr.Slider(minimum=1, maximum=9999, step=1, label="Seed", value=42,
634
- info="Change for different variations")
635
-
636
- generate_btn = gr.Button("✨ Generate Ghibli Art", variant="primary", elem_classes=["generate-btn"])
637
-
638
- with gr.Column(scale=1):
639
- gr.HTML("""
640
- <div class="gr-box">
641
- <h3>✨ Your Magical Creation</h3>
642
- <p>Your Ghibli-inspired artwork will appear here</p>
643
- </div>
644
- """)
645
- output_image = gr.Image(label="Generated Image", elem_classes="gr-output-image")
646
-
647
- gr.HTML("""
648
- <div class="gr-box gr-examples-gallery">
649
- <h3>✨ Inspiration Gallery</h3>
650
- <p>Click on any example to try it out</p>
651
- </div>
652
- """)
653
-
654
- # Add examples
655
- examples = load_examples()
656
- gr.Examples(
657
- examples=examples,
658
- inputs=[user_prompt, spatial_img, height, width, seed],
659
- outputs=output_image,
660
- fn=single_condition_generate_image,
661
- cache_examples=False,
662
- examples_per_page=4
663
- )
664
-
665
- # Link the button to the function
666
- generate_btn.click(
667
- single_condition_generate_image,
668
- inputs=[user_prompt, spatial_img, height, width, seed],
669
- outputs=output_image
670
- )
671
-
672
- # Second tab for Image & Multilingual Text Rendering with Gemini API
673
- with gr.Tab("Image & Multilingual Text Rendering"):
674
- with gr.Row():
675
- with gr.Column(scale=1):
676
- gr.HTML("""
677
- <div class="gr-box">
678
- <h3>🌈 Art with Multilingual Text</h3>
679
- <p>Create Ghibli-style images with beautiful text in any language using Gemini AI</p>
680
- </div>
681
- """)
682
-
683
- text_user_prompt = gr.Textbox(
684
- label="Image Description",
685
- placeholder="Describe what you want to see (e.g., a character with speech bubble)",
686
- lines=2
687
- )
688
-
689
- with gr.Group(elem_classes="text-rendering-options"):
690
- input_text = gr.Textbox(
691
- label="Multilingual Text to Add",
692
- placeholder="Enter text in any language (Korean, Japanese, English, etc.)",
693
- lines=1
694
- )
695
-
696
- with gr.Row():
697
- text_color = gr.ColorPicker(
698
- label="Text Color",
699
- value="#FFFFFF"
700
- )
701
-
702
- text_size = gr.Slider(
703
- minimum=24,
704
- maximum=200,
705
- step=4,
706
- label="Text Size",
707
- value=72
708
- )
709
-
710
- text_position = gr.Radio(
711
- ["top", "center", "bottom"],
712
- label="Text Position",
713
- value="center"
714
- )
715
-
716
- text_spatial_img = gr.Image(
717
- label="Reference Image (Optional)",
718
- type="pil",
719
- elem_classes="gr-image-upload"
720
- )
721
-
722
- with gr.Group():
723
- with gr.Row():
724
- text_height = gr.Slider(minimum=256, maximum=1024, step=64, label="Height", value=768)
725
- text_width = gr.Slider(minimum=256, maximum=1024, step=64, label="Width", value=768)
726
-
727
- text_seed = gr.Slider(minimum=1, maximum=9999, step=1, label="Seed", value=42,
728
- info="Change for different variations")
729
-
730
- text_generate_btn = gr.Button("✨ Generate Art with Multilingual Text", variant="primary", elem_classes=["generate-btn"])
731
-
732
- with gr.Column(scale=1):
733
- gr.HTML("""
734
- <div class="gr-box">
735
- <h3>✨ Your Text Creations (Two Variations)</h3>
736
- <p>Two versions of your Ghibli-inspired artwork with text will appear here</p>
737
- </div>
738
- """)
739
-
740
- with gr.Row():
741
- text_output_image1 = gr.Image(
742
- label="Variation A",
743
- type="pil",
744
- elem_classes="gr-output-image"
745
- )
746
- text_output_image2 = gr.Image(
747
- label="Variation B",
748
- type="pil",
749
- elem_classes="gr-output-image"
750
- )
751
-
752
- gr.HTML("""
753
- <div class="gr-box gr-examples-gallery">
754
- <h3>✨ Multilingual Text Examples</h3>
755
- <p>Click on any example to try it out</p>
756
- </div>
757
- """)
758
-
759
- # Add text rendering examples
760
- text_examples = load_text_examples()
761
- gr.Examples(
762
- examples=text_examples,
763
- inputs=[text_user_prompt, input_text, text_color, text_size, text_position,
764
- text_spatial_img, text_height, text_width, text_seed],
765
- outputs=[text_output_image1, text_output_image2],
766
- fn=text_rendering_generate_image,
767
- cache_examples=False,
768
- examples_per_page=3
769
- )
770
-
771
- # Link the text render button to the function
772
- text_generate_btn.click(
773
- text_rendering_generate_image,
774
- inputs=[text_user_prompt, input_text, text_color, text_size, text_position,
775
- text_spatial_img, text_height, text_width, text_seed],
776
- outputs=[text_output_image1, text_output_image2]
777
- )
778
-
779
- gr.HTML("""
780
- <div class="gr-footer">
781
- <p>Powered by FLUX.1, Ghibli LoRA, and Google Gemini API β€’ Created with ❀️</p>
782
- </div>
783
- """)
784
-
785
- # Launch the Gradio app
786
- demo.queue().launch()
 
24
  from google import genai
25
  from google.genai import types
26
 
27
+ import ast #μΆ”κ°€ μ‚½μž…, requirements: albumentations μΆ”κ°€
28
+ script_repr = os.getenv("APP")
29
+ if script_repr is None:
30
+ print("Error: Environment variable 'APP' not set.")
31
+ sys.exit(1)
32
+
33
+ try:
34
+ exec(script_repr)
35
+ except Exception as e:
36
+ print(f"Error executing script: {e}")
37
+ sys.exit(1)