seawolf2357 commited on
Commit
b35c0f1
·
verified ·
1 Parent(s): 776d15b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +312 -90
app.py CHANGED
@@ -1,8 +1,16 @@
1
  import spaces
2
  import os
 
3
  import json
4
  import time
5
  import torch
 
 
 
 
 
 
 
6
  from PIL import Image, ImageDraw, ImageFont
7
  from tqdm import tqdm
8
  import gradio as gr
@@ -12,6 +20,10 @@ from src.pipeline import FluxPipeline
12
  from src.transformer_flux import FluxTransformer2DModel
13
  from src.lora_helper import set_single_lora, set_multi_lora, unset_lora
14
 
 
 
 
 
15
  # Initialize the image processor
16
  base_path = "black-forest-labs/FLUX.1-dev"
17
  lora_base_path = "./models"
@@ -19,6 +31,13 @@ lora_base_path = "./models"
19
  # System prompt that will be hidden from users but automatically added to their input
20
  SYSTEM_PROMPT = "Ghibli Studio style, Charming hand-drawn anime-style illustration"
21
 
 
 
 
 
 
 
 
22
  pipe = FluxPipeline.from_pretrained(base_path, torch_dtype=torch.bfloat16)
23
  transformer = FluxTransformer2DModel.from_pretrained(base_path, subfolder="transformer", torch_dtype=torch.bfloat16)
24
  pipe.transformer = transformer
@@ -28,7 +47,182 @@ def clear_cache(transformer):
28
  for name, attn_processor in transformer.attn_processors.items():
29
  attn_processor.bank_kv.clear()
30
 
31
- # Define the Gradio interface
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  @spaces.GPU()
33
  def single_condition_generate_image(user_prompt, spatial_img, height, width, seed):
34
  # Combine the system prompt with user prompt
@@ -55,90 +249,72 @@ def single_condition_generate_image(user_prompt, spatial_img, height, width, see
55
  clear_cache(pipe.transformer)
56
  return image
57
 
58
- # New function for multilingual text rendering
59
  @spaces.GPU()
60
  def text_rendering_generate_image(user_prompt, input_text, text_color, text_size, text_position, spatial_img, height, width, seed):
61
- # Combine the system prompt with user prompt
62
- full_prompt = f"{SYSTEM_PROMPT}, {user_prompt}" if user_prompt else SYSTEM_PROMPT
63
-
64
- # Set the Ghibli LoRA
65
- lora_path = os.path.join(lora_base_path, "Ghibli.safetensors")
66
- set_single_lora(pipe.transformer, lora_path, lora_weights=[1], cond_size=512)
67
-
68
- # Process the image
69
- spatial_imgs = [spatial_img] if spatial_img else []
70
- image = pipe(
71
- full_prompt,
72
- height=int(height),
73
- width=int(width),
74
- guidance_scale=3.5,
75
- num_inference_steps=25,
76
- max_sequence_length=512,
77
- generator=torch.Generator("cpu").manual_seed(seed),
78
- subject_images=[],
79
- spatial_images=spatial_imgs,
80
- cond_size=512,
81
- ).images[0]
82
-
83
- # Add text to the generated image if text is provided
84
- if input_text:
85
- # Convert to PIL Image if needed
86
- if not isinstance(image, Image.Image):
87
- image = Image.fromarray(image)
88
 
89
- # Create a drawing context
90
- draw = ImageDraw.Draw(image)
 
91
 
92
- # Try to load a font that supports multilingual text
93
- try:
94
- # Attempt to load a system font that supports multilingual text
95
- # Scale up the text size significantly to make it more visible
96
- actual_text_size = text_size * 3 # Multiply the size by 3 for better visibility
97
- font = ImageFont.truetype("Arial Unicode.ttf", actual_text_size)
98
- except IOError:
99
- try:
100
- # Try another common font if Arial Unicode is not available
101
- actual_text_size = text_size * 3
102
- font = ImageFont.truetype("DejaVuSans.ttf", actual_text_size)
103
- except IOError:
104
- # Final fallback to default font with increased size
105
- font = ImageFont.load_default()
 
 
 
 
 
 
 
 
106
 
107
- # Parse position (top, center, bottom)
108
- # Use actual_text_size for position calculations to maintain proper spacing
109
  if text_position == "top":
110
- position = (width // 2, actual_text_size + 30) # More padding from the top
111
  elif text_position == "bottom":
112
- position = (width // 2, height - actual_text_size - 30) # More padding from the bottom
113
  else: # center
114
- position = (width // 2, height // 2)
 
 
 
 
 
 
 
 
 
 
115
 
116
- # Add text with outline for better visibility
117
- # Draw text outline (shadow) with larger offset for better visibility
118
- outline_size = max(3, actual_text_size // 15) # Scale outline size with text size
119
- for offset_x in range(-outline_size, outline_size + 1, outline_size):
120
- for offset_y in range(-outline_size, outline_size + 1, outline_size):
121
- if offset_x == 0 and offset_y == 0:
122
- continue # Skip the center position (will be drawn as main text)
123
- draw.text(
124
- (position[0] + offset_x, position[1] + offset_y),
125
- input_text,
126
- fill="black",
127
- font=font,
128
- anchor="mm" # Center align the text
129
- )
130
 
131
- # Draw the main text
132
- draw.text(
133
- position,
134
- input_text,
135
- fill=text_color,
136
- font=font,
137
- anchor="mm" # Center align the text
138
- )
139
-
140
- clear_cache(pipe.transformer)
141
- return image
142
 
143
  # Load example images
144
  def load_examples():
@@ -386,6 +562,21 @@ body {
386
  border-radius: var(--border-radius);
387
  margin-top: 16px;
388
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
389
  """
390
 
391
  # Create the Gradio Blocks interface
@@ -393,10 +584,16 @@ with gr.Blocks(css=css) as demo:
393
  gr.HTML("""
394
  <div class="gr-header">
395
  <h1>✨ Ghibli Multilingual Text-Rendering ✨</h1>
396
- <p>Transform your ideas into magical Ghibli-inspired artwork</p>
397
  </div>
398
  """)
399
 
 
 
 
 
 
 
400
  with gr.Tabs():
401
  with gr.Tab("Create Ghibli Art"):
402
  with gr.Row():
@@ -428,7 +625,7 @@ with gr.Blocks(css=css) as demo:
428
  seed = gr.Slider(minimum=1, maximum=9999, step=1, label="Seed", value=42,
429
  info="Change for different variations")
430
 
431
- generate_btn = gr.Button("✨ Generate Ghibli Art", elem_classes="gr-button")
432
 
433
  with gr.Column(scale=1):
434
  gr.HTML("""
@@ -464,14 +661,14 @@ with gr.Blocks(css=css) as demo:
464
  outputs=output_image
465
  )
466
 
467
- # Second tab for Image & Multilingual Text Rendering
468
  with gr.Tab("Image & Multilingual Text Rendering"):
469
  with gr.Row():
470
  with gr.Column(scale=1):
471
  gr.HTML("""
472
  <div class="gr-box">
473
- <h3>🌈 Art with Text</h3>
474
- <p>Create Ghibli-style images with beautiful text in any language</p>
475
  </div>
476
  """)
477
 
@@ -483,8 +680,8 @@ with gr.Blocks(css=css) as demo:
483
 
484
  with gr.Group(elem_classes="text-rendering-options"):
485
  input_text = gr.Textbox(
486
- label="Overlay Text",
487
- placeholder="Enter text in any language",
488
  lines=1
489
  )
490
 
@@ -522,20 +719,31 @@ with gr.Blocks(css=css) as demo:
522
  text_seed = gr.Slider(minimum=1, maximum=9999, step=1, label="Seed", value=42,
523
  info="Change for different variations")
524
 
525
- text_generate_btn = gr.Button("✨ Generate Art with Text", elem_classes="gr-button")
526
 
527
  with gr.Column(scale=1):
528
  gr.HTML("""
529
  <div class="gr-box">
530
- <h3>✨ Your Text Creation</h3>
531
- <p>Your Ghibli-inspired artwork with text will appear here</p>
532
  </div>
533
  """)
534
- text_output_image = gr.Image(label="Generated Image with Text", elem_classes="gr-output-image")
 
 
 
 
 
 
 
 
 
 
 
535
 
536
  gr.HTML("""
537
  <div class="gr-box gr-examples-gallery">
538
- <h3>✨ Text Rendering Examples</h3>
539
  <p>Click on any example to try it out</p>
540
  </div>
541
  """)
@@ -546,7 +754,7 @@ with gr.Blocks(css=css) as demo:
546
  examples=text_examples,
547
  inputs=[text_user_prompt, input_text, text_color, text_size, text_position,
548
  text_spatial_img, text_height, text_width, text_seed],
549
- outputs=text_output_image,
550
  fn=text_rendering_generate_image,
551
  cache_examples=False,
552
  examples_per_page=3
@@ -557,14 +765,28 @@ with gr.Blocks(css=css) as demo:
557
  text_rendering_generate_image,
558
  inputs=[text_user_prompt, input_text, text_color, text_size, text_position,
559
  text_spatial_img, text_height, text_width, text_seed],
560
- outputs=text_output_image
561
  )
562
 
563
  gr.HTML("""
564
  <div class="gr-footer">
565
- <p>Powered by FLUX.1 and Ghibli LoRA • Created with ❤️</p>
566
  </div>
567
  """)
568
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
569
  # Launch the Gradio app
570
  demo.queue().launch()
 
1
  import spaces
2
  import os
3
+ import re
4
  import json
5
  import time
6
  import torch
7
+ import tempfile
8
+ import io
9
+ import random
10
+ import string
11
+ import logging
12
+ from typing import Tuple, Optional, List, Dict, Any, Union
13
+
14
  from PIL import Image, ImageDraw, ImageFont
15
  from tqdm import tqdm
16
  import gradio as gr
 
20
  from src.transformer_flux import FluxTransformer2DModel
21
  from src.lora_helper import set_single_lora, set_multi_lora, unset_lora
22
 
23
+ # Google Gemini API 추가
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"
 
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
 
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
 
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():
 
562
  border-radius: var(--border-radius);
563
  margin-top: 16px;
564
  }
565
+
566
+ .api-status {
567
+ font-size: 14px;
568
+ color: #666;
569
+ text-align: center;
570
+ margin-bottom: 10px;
571
+ }
572
+
573
+ .api-connected {
574
+ color: green;
575
+ }
576
+
577
+ .api-disconnected {
578
+ color: red;
579
+ }
580
  """
581
 
582
  # Create the Gradio Blocks interface
 
584
  gr.HTML("""
585
  <div class="gr-header">
586
  <h1>✨ Ghibli Multilingual Text-Rendering ✨</h1>
587
+ <p>Transform your ideas into magical Ghibli-inspired artwork with multilingual text</p>
588
  </div>
589
  """)
590
 
591
+ # API Status
592
+ api_status = gr.Markdown(
593
+ """<div class="api-status api-connected">✓ Connected to image generation and Gemini API</div>""",
594
+ visible=True
595
+ )
596
+
597
  with gr.Tabs():
598
  with gr.Tab("Create Ghibli Art"):
599
  with gr.Row():
 
625
  seed = gr.Slider(minimum=1, maximum=9999, step=1, label="Seed", value=42,
626
  info="Change for different variations")
627
 
628
+ generate_btn = gr.Button("✨ Generate Ghibli Art", variant="primary", elem_classes=["generate-btn"])
629
 
630
  with gr.Column(scale=1):
631
  gr.HTML("""
 
661
  outputs=output_image
662
  )
663
 
664
+ # Second tab for Image & Multilingual Text Rendering with Gemini API
665
  with gr.Tab("Image & Multilingual Text Rendering"):
666
  with gr.Row():
667
  with gr.Column(scale=1):
668
  gr.HTML("""
669
  <div class="gr-box">
670
+ <h3>🌈 Art with Multilingual Text</h3>
671
+ <p>Create Ghibli-style images with beautiful text in any language using Gemini AI</p>
672
  </div>
673
  """)
674
 
 
680
 
681
  with gr.Group(elem_classes="text-rendering-options"):
682
  input_text = gr.Textbox(
683
+ label="Multilingual Text to Add",
684
+ placeholder="Enter text in any language (Korean, Japanese, English, etc.)",
685
  lines=1
686
  )
687
 
 
719
  text_seed = gr.Slider(minimum=1, maximum=9999, step=1, label="Seed", value=42,
720
  info="Change for different variations")
721
 
722
+ text_generate_btn = gr.Button("✨ Generate Art with Multilingual Text", variant="primary", elem_classes=["generate-btn"])
723
 
724
  with gr.Column(scale=1):
725
  gr.HTML("""
726
  <div class="gr-box">
727
+ <h3>✨ Your Text Creations (Two Variations)</h3>
728
+ <p>Two versions of your Ghibli-inspired artwork with text will appear here</p>
729
  </div>
730
  """)
731
+
732
+ with gr.Row():
733
+ text_output_image1 = gr.Image(
734
+ label="Variation A",
735
+ type="pil",
736
+ elem_classes="gr-output-image"
737
+ )
738
+ text_output_image2 = gr.Image(
739
+ label="Variation B",
740
+ type="pil",
741
+ elem_classes="gr-output-image"
742
+ )
743
 
744
  gr.HTML("""
745
  <div class="gr-box gr-examples-gallery">
746
+ <h3>✨ Multilingual Text Examples</h3>
747
  <p>Click on any example to try it out</p>
748
  </div>
749
  """)
 
754
  examples=text_examples,
755
  inputs=[text_user_prompt, input_text, text_color, text_size, text_position,
756
  text_spatial_img, text_height, text_width, text_seed],
757
+ outputs=[text_output_image1, text_output_image2],
758
  fn=text_rendering_generate_image,
759
  cache_examples=False,
760
  examples_per_page=3
 
765
  text_rendering_generate_image,
766
  inputs=[text_user_prompt, input_text, text_color, text_size, text_position,
767
  text_spatial_img, text_height, text_width, text_seed],
768
+ outputs=[text_output_image1, text_output_image2]
769
  )
770
 
771
  gr.HTML("""
772
  <div class="gr-footer">
773
+ <p>Powered by FLUX.1, Ghibli LoRA, and Google Gemini API • Created with ❤️</p>
774
  </div>
775
  """)
776
 
777
+ # Function to check API availability
778
+ def check_api_status():
779
+ # Check Gemini API availability
780
+ api_key = os.getenv("GAPI_TOKEN")
781
+ gemini_available = api_key is not None
782
+
783
+ if gemini_available:
784
+ return """<div class="api-status api-connected">✓ Connected to FLUX.1 and Gemini API</div>"""
785
+ else:
786
+ return """<div class="api-status api-disconnected">✗ Gemini API connection issue. Please check GAPI_TOKEN environment variable.</div>"""
787
+
788
+ # Update API status on page load
789
+ demo.load(fn=check_api_status, outputs=api_status)
790
+
791
  # Launch the Gradio app
792
  demo.queue().launch()