akhaliq HF Staff commited on
Commit
c4bf22d
Β·
verified Β·
1 Parent(s): 514d67f

Update app.py from anycoder

Browse files
Files changed (1) hide show
  1. app.py +13 -111
app.py CHANGED
@@ -1,41 +1,28 @@
1
  """
2
- GLM-Image to Image Editing App (ZeroGPU Version)
3
- A Gradio 6 application for image-to-image editing using the GLM-Image model with ZeroGPU support.
4
 
5
  This app allows users to upload an image and provide a prompt to transform
6
- the image using the GLM-Image diffusion model with dynamic GPU allocation.
7
  """
8
 
9
  import gradio as gr
10
  import torch
11
  from diffusers.pipelines.glm_image import GlmImagePipeline
12
  from PIL import Image
13
- import os
14
- from datetime import datetime
15
 
16
- # Import ZeroGPU
17
- import spaces
18
-
19
- # Initialize the model (lazy loading for better startup performance)
20
- pipe = None
21
-
22
- def load_model():
23
- """Load the GLM-Image model with proper configuration."""
24
- global pipe
25
- if pipe is None:
26
- pipe = GlmImagePipeline.from_pretrained(
27
- "zai-org/GLM-Image",
28
- torch_dtype=torch.bfloat16,
29
- device_map="cuda"
30
- )
31
- return pipe
32
 
33
  def validate_dimensions(height: int, width: int) -> tuple:
34
  """
35
  Validate and adjust dimensions to be multiples of 32.
36
  GLM-Image requires height and width to be multiples of 32.
37
  """
38
- # Adjust to nearest multiples of 32
39
  adjusted_height = (height // 32 + (1 if height % 32 != 0 else 0)) * 32
40
  adjusted_width = (width // 32 + (1 if width % 32 != 0 else 0)) * 32
41
  return adjusted_height, adjusted_width
@@ -44,34 +31,6 @@ def get_image_dimensions(image: Image.Image) -> tuple:
44
  """Get the dimensions of an uploaded PIL image."""
45
  return image.size[1], image.size[0] # height, width
46
 
47
- def estimate_duration(num_inference_steps: int, height: int, width: int) -> int:
48
- """
49
- Estimate the duration needed for the GPU task based on complexity.
50
-
51
- Args:
52
- num_inference_steps: Number of diffusion steps
53
- height: Image height
54
- width: Image width
55
-
56
- Returns:
57
- Estimated duration in seconds
58
- """
59
- # Base time per step (adjust based on testing)
60
- base_time_per_step = 3.5 # seconds
61
-
62
- # Complexity factor based on image size (larger images take more time)
63
- size_factor = (height * width) / (1024 * 1024) # relative to 1024x1024
64
-
65
- # Estimate total time
66
- estimated_time = num_inference_steps * base_time_per_step * size_factor
67
-
68
- # Add buffer for image processing overhead
69
- total_duration = int(estimated_time) + 30 # +30 seconds buffer
70
-
71
- # Ensure minimum duration and cap at reasonable max
72
- return max(60, min(total_duration, 180)) # Between 60s and 180s
73
-
74
- @spaces.GPU(duration=estimate_duration)
75
  def process_image(
76
  image: Image.Image,
77
  prompt: str,
@@ -84,7 +43,6 @@ def process_image(
84
  ) -> tuple:
85
  """
86
  Process the image through the GLM-Image pipeline.
87
- Decorated with @spaces.GPU for ZeroGPU dynamic allocation.
88
 
89
  Args:
90
  image: Input PIL Image
@@ -100,33 +58,24 @@ def process_image(
100
  Tuple of (output_image, status_message)
101
  """
102
  try:
103
- # Validate inputs
104
  if image is None:
105
  raise ValueError("Please upload an image first.")
106
 
107
  if not prompt or not prompt.strip():
108
  raise ValueError("Please enter a prompt describing the image transformation.")
109
 
110
- # Adjust dimensions to be multiples of 32
111
  adjusted_height, adjusted_width = validate_dimensions(height, width)
112
 
113
  if adjusted_height != height or adjusted_width != width:
114
  height, width = adjusted_height, adjusted_width
115
 
116
- # Load model if not already loaded
117
- progress(0.1, desc="Loading model...")
118
- model = load_model()
119
-
120
- # Prepare image
121
- progress(0.2, desc="Processing image...")
122
  input_image = image.convert("RGB")
123
 
124
- # Create generator with seed
125
  generator = torch.Generator(device="cuda").manual_seed(seed)
126
 
127
- # Run the pipeline
128
  progress(0.3, desc="Generating image...")
129
- result = model(
130
  prompt=prompt,
131
  image=[input_image],
132
  height=height,
@@ -152,7 +101,6 @@ def update_dimensions_from_image(image: Image.Image) -> tuple:
152
  if image is None:
153
  return 1024, 1024
154
  h, w = get_image_dimensions(image)
155
- # Adjust to nearest multiples of 32
156
  adjusted_h = (h // 32 + (1 if h % 32 != 0 else 0)) * 32
157
  adjusted_w = (w // 32 + (1 if w % 32 != 0 else 0)) * 32
158
  return adjusted_h, adjusted_w
@@ -162,7 +110,6 @@ def generate_random_seed() -> int:
162
  import random
163
  return random.randint(0, 2**32 - 1)
164
 
165
- # Custom theme with modern design
166
  custom_theme = gr.themes.Soft(
167
  primary_hue="indigo",
168
  secondary_hue="blue",
@@ -183,10 +130,8 @@ custom_theme = gr.themes.Soft(
183
  input_focus_border_color="*primary_400",
184
  )
185
 
186
- # Build the Gradio 6 application
187
  with gr.Blocks(fill_height=True) as demo:
188
 
189
- # Header with branding
190
  gr.Markdown(
191
  """
192
  # 🎨 GLM-Image Editor
@@ -199,23 +144,10 @@ with gr.Blocks(fill_height=True) as demo:
199
  elem_classes=["header-markdown"]
200
  )
201
 
202
- # GPU Status indicator
203
- gr.Markdown(
204
- """
205
- <div class="gpu-status">
206
- πŸš€ <strong>ZeroGPU Enabled</strong> - Dynamic GPU allocation for optimal performance
207
- </div>
208
- """,
209
- elem_classes=["gpu-status-markdown"]
210
- )
211
-
212
- # Main content in a row
213
  with gr.Row(equal_height=True):
214
- # Left column - Input controls
215
  with gr.Column(scale=1, min_width=350):
216
  gr.Markdown("### πŸ“€ Input")
217
 
218
- # Image upload
219
  input_image = gr.Image(
220
  label="Upload Image",
221
  type="pil",
@@ -224,7 +156,6 @@ with gr.Blocks(fill_height=True) as demo:
224
  height=300
225
  )
226
 
227
- # Prompt input
228
  prompt = gr.Textbox(
229
  label="Prompt",
230
  placeholder="Describe how you want to transform the image...\n\nExample: Replace the background with an underground station featuring an automatic escalator.",
@@ -233,7 +164,6 @@ with gr.Blocks(fill_height=True) as demo:
233
  info="Be specific about what you want to change"
234
  )
235
 
236
- # Advanced settings accordion
237
  with gr.Accordion("βš™οΈ Advanced Settings", open=False):
238
  with gr.Row():
239
  height = gr.Number(
@@ -286,7 +216,6 @@ with gr.Blocks(fill_height=True) as demo:
286
  variant="secondary"
287
  )
288
 
289
- # Action buttons
290
  with gr.Row():
291
  generate_btn = gr.Button(
292
  "✨ Generate Image",
@@ -295,18 +224,15 @@ with gr.Blocks(fill_height=True) as demo:
295
  full_width=True
296
  )
297
 
298
- # Clear button
299
  clear_btn = gr.Button(
300
  "πŸ—‘οΈ Clear All",
301
  variant="stop",
302
  size="sm"
303
  )
304
 
305
- # Right column - Output
306
  with gr.Column(scale=1, min_width=350):
307
  gr.Markdown("### πŸ“₯ Output")
308
 
309
- # Output image display
310
  output_image = gr.Image(
311
  label="Generated Image",
312
  type="pil",
@@ -315,15 +241,13 @@ with gr.Blocks(fill_height=True) as demo:
315
  interactive=False
316
  )
317
 
318
- # Status message
319
  status = gr.Textbox(
320
  label="Status",
321
- value="Ready to generate! GPU will be allocated automatically.",
322
  interactive=False,
323
  show_label=True
324
  )
325
 
326
- # Download button
327
  download_btn = gr.DownloadButton(
328
  "πŸ’Ύ Download Image",
329
  value=None,
@@ -331,7 +255,6 @@ with gr.Blocks(fill_height=True) as demo:
331
  interactive=False
332
  )
333
 
334
- # Tips section
335
  with gr.Accordion("πŸ’‘ Tips for Better Results", open=False):
336
  gr.Markdown(
337
  """
@@ -348,7 +271,6 @@ with gr.Blocks(fill_height=True) as demo:
348
  """
349
  )
350
 
351
- # Example prompts section
352
  with gr.Accordion("πŸ“ Example Prompts", open=False):
353
  gr.Markdown(
354
  """
@@ -364,9 +286,6 @@ with gr.Blocks(fill_height=True) as demo:
364
  """
365
  )
366
 
367
- # Event handlers
368
-
369
- # Update dimensions when image is uploaded
370
  input_image.change(
371
  fn=update_dimensions_from_image,
372
  inputs=input_image,
@@ -374,14 +293,12 @@ with gr.Blocks(fill_height=True) as demo:
374
  api_visibility="private"
375
  )
376
 
377
- # Random seed generation
378
  random_seed_btn.click(
379
  fn=generate_random_seed,
380
  outputs=seed,
381
  api_visibility="private"
382
  )
383
 
384
- # Generate button handler - uses ZeroGPU via @spaces.GPU decorator
385
  generate_btn.click(
386
  fn=process_image,
387
  inputs=[
@@ -397,7 +314,6 @@ with gr.Blocks(fill_height=True) as demo:
397
  progress=gr.Progress()
398
  )
399
 
400
- # Update download button when output is generated
401
  def enable_download(img):
402
  if img is not None:
403
  return gr.DownloadButton(value=img, interactive=True)
@@ -410,13 +326,12 @@ with gr.Blocks(fill_height=True) as demo:
410
  api_visibility="private"
411
  )
412
 
413
- # Clear button handler
414
  def clear_all():
415
  return {
416
  input_image: None,
417
  prompt: "",
418
  output_image: None,
419
- status: "Ready to generate! GPU will be allocated automatically.",
420
  download_btn: gr.DownloadButton(interactive=False)
421
  }
422
 
@@ -432,7 +347,6 @@ with gr.Blocks(fill_height=True) as demo:
432
  api_visibility="private"
433
  )
434
 
435
- # Gradio 6 - ALL app parameters go in launch()!
436
  demo.launch(
437
  theme=custom_theme,
438
  css="""
@@ -455,17 +369,6 @@ demo.launch(
455
  color: #ffd700 !important;
456
  text-decoration: underline;
457
  }
458
- .gpu-status-markdown {
459
- background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
460
- padding: 0.75rem;
461
- border-radius: 0.5rem;
462
- margin-bottom: 1rem;
463
- text-align: center;
464
- color: white;
465
- }
466
- .gpu-status-markdown strong {
467
- color: #fff;
468
- }
469
  #input-image, #output-image {
470
  border: 2px dashed var(--neutral-300);
471
  border-radius: var(--radius-lg);
@@ -477,7 +380,6 @@ demo.launch(
477
  footer_links=[
478
  {"label": "Built with anycoder", "url": "https://huggingface.co/spaces/akhaliq/anycoder"},
479
  {"label": "GLM-Image Model", "url": "https://huggingface.co/zai-org/GLM-Image"},
480
- {"label": "ZeroGPU", "url": "https://huggingface.co/spaces/zero-gpu-explorers/README"},
481
  {"label": "Diffusers Library", "url": "https://github.com/huggingface/diffusers"}
482
  ],
483
  server_name="0.0.0.0",
 
1
  """
2
+ GLM-Image to Image Editing App
3
+ A Gradio 6 application for image-to-image editing using the GLM-Image model.
4
 
5
  This app allows users to upload an image and provide a prompt to transform
6
+ the image using the GLM-Image diffusion model.
7
  """
8
 
9
  import gradio as gr
10
  import torch
11
  from diffusers.pipelines.glm_image import GlmImagePipeline
12
  from PIL import Image
 
 
13
 
14
+ # Initialize the model at startup
15
+ pipe = GlmImagePipeline.from_pretrained(
16
+ "zai-org/GLM-Image",
17
+ torch_dtype=torch.bfloat16,
18
+ device_map="cuda"
19
+ )
 
 
 
 
 
 
 
 
 
 
20
 
21
  def validate_dimensions(height: int, width: int) -> tuple:
22
  """
23
  Validate and adjust dimensions to be multiples of 32.
24
  GLM-Image requires height and width to be multiples of 32.
25
  """
 
26
  adjusted_height = (height // 32 + (1 if height % 32 != 0 else 0)) * 32
27
  adjusted_width = (width // 32 + (1 if width % 32 != 0 else 0)) * 32
28
  return adjusted_height, adjusted_width
 
31
  """Get the dimensions of an uploaded PIL image."""
32
  return image.size[1], image.size[0] # height, width
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  def process_image(
35
  image: Image.Image,
36
  prompt: str,
 
43
  ) -> tuple:
44
  """
45
  Process the image through the GLM-Image pipeline.
 
46
 
47
  Args:
48
  image: Input PIL Image
 
58
  Tuple of (output_image, status_message)
59
  """
60
  try:
 
61
  if image is None:
62
  raise ValueError("Please upload an image first.")
63
 
64
  if not prompt or not prompt.strip():
65
  raise ValueError("Please enter a prompt describing the image transformation.")
66
 
 
67
  adjusted_height, adjusted_width = validate_dimensions(height, width)
68
 
69
  if adjusted_height != height or adjusted_width != width:
70
  height, width = adjusted_height, adjusted_width
71
 
72
+ progress(0.1, desc="Processing image...")
 
 
 
 
 
73
  input_image = image.convert("RGB")
74
 
 
75
  generator = torch.Generator(device="cuda").manual_seed(seed)
76
 
 
77
  progress(0.3, desc="Generating image...")
78
+ result = pipe(
79
  prompt=prompt,
80
  image=[input_image],
81
  height=height,
 
101
  if image is None:
102
  return 1024, 1024
103
  h, w = get_image_dimensions(image)
 
104
  adjusted_h = (h // 32 + (1 if h % 32 != 0 else 0)) * 32
105
  adjusted_w = (w // 32 + (1 if w % 32 != 0 else 0)) * 32
106
  return adjusted_h, adjusted_w
 
110
  import random
111
  return random.randint(0, 2**32 - 1)
112
 
 
113
  custom_theme = gr.themes.Soft(
114
  primary_hue="indigo",
115
  secondary_hue="blue",
 
130
  input_focus_border_color="*primary_400",
131
  )
132
 
 
133
  with gr.Blocks(fill_height=True) as demo:
134
 
 
135
  gr.Markdown(
136
  """
137
  # 🎨 GLM-Image Editor
 
144
  elem_classes=["header-markdown"]
145
  )
146
 
 
 
 
 
 
 
 
 
 
 
 
147
  with gr.Row(equal_height=True):
 
148
  with gr.Column(scale=1, min_width=350):
149
  gr.Markdown("### πŸ“€ Input")
150
 
 
151
  input_image = gr.Image(
152
  label="Upload Image",
153
  type="pil",
 
156
  height=300
157
  )
158
 
 
159
  prompt = gr.Textbox(
160
  label="Prompt",
161
  placeholder="Describe how you want to transform the image...\n\nExample: Replace the background with an underground station featuring an automatic escalator.",
 
164
  info="Be specific about what you want to change"
165
  )
166
 
 
167
  with gr.Accordion("βš™οΈ Advanced Settings", open=False):
168
  with gr.Row():
169
  height = gr.Number(
 
216
  variant="secondary"
217
  )
218
 
 
219
  with gr.Row():
220
  generate_btn = gr.Button(
221
  "✨ Generate Image",
 
224
  full_width=True
225
  )
226
 
 
227
  clear_btn = gr.Button(
228
  "πŸ—‘οΈ Clear All",
229
  variant="stop",
230
  size="sm"
231
  )
232
 
 
233
  with gr.Column(scale=1, min_width=350):
234
  gr.Markdown("### πŸ“₯ Output")
235
 
 
236
  output_image = gr.Image(
237
  label="Generated Image",
238
  type="pil",
 
241
  interactive=False
242
  )
243
 
 
244
  status = gr.Textbox(
245
  label="Status",
246
+ value="Ready to generate!",
247
  interactive=False,
248
  show_label=True
249
  )
250
 
 
251
  download_btn = gr.DownloadButton(
252
  "πŸ’Ύ Download Image",
253
  value=None,
 
255
  interactive=False
256
  )
257
 
 
258
  with gr.Accordion("πŸ’‘ Tips for Better Results", open=False):
259
  gr.Markdown(
260
  """
 
271
  """
272
  )
273
 
 
274
  with gr.Accordion("πŸ“ Example Prompts", open=False):
275
  gr.Markdown(
276
  """
 
286
  """
287
  )
288
 
 
 
 
289
  input_image.change(
290
  fn=update_dimensions_from_image,
291
  inputs=input_image,
 
293
  api_visibility="private"
294
  )
295
 
 
296
  random_seed_btn.click(
297
  fn=generate_random_seed,
298
  outputs=seed,
299
  api_visibility="private"
300
  )
301
 
 
302
  generate_btn.click(
303
  fn=process_image,
304
  inputs=[
 
314
  progress=gr.Progress()
315
  )
316
 
 
317
  def enable_download(img):
318
  if img is not None:
319
  return gr.DownloadButton(value=img, interactive=True)
 
326
  api_visibility="private"
327
  )
328
 
 
329
  def clear_all():
330
  return {
331
  input_image: None,
332
  prompt: "",
333
  output_image: None,
334
+ status: "Ready to generate!",
335
  download_btn: gr.DownloadButton(interactive=False)
336
  }
337
 
 
347
  api_visibility="private"
348
  )
349
 
 
350
  demo.launch(
351
  theme=custom_theme,
352
  css="""
 
369
  color: #ffd700 !important;
370
  text-decoration: underline;
371
  }
 
 
 
 
 
 
 
 
 
 
 
372
  #input-image, #output-image {
373
  border: 2px dashed var(--neutral-300);
374
  border-radius: var(--radius-lg);
 
380
  footer_links=[
381
  {"label": "Built with anycoder", "url": "https://huggingface.co/spaces/akhaliq/anycoder"},
382
  {"label": "GLM-Image Model", "url": "https://huggingface.co/zai-org/GLM-Image"},
 
383
  {"label": "Diffusers Library", "url": "https://github.com/huggingface/diffusers"}
384
  ],
385
  server_name="0.0.0.0",