Ntdeseb commited on
Commit
aef1cc6
Β·
verified Β·
1 Parent(s): bf105f2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +310 -382
app.py CHANGED
@@ -4,160 +4,139 @@ import random
4
  import torch
5
  import gc
6
  from typing import Optional, Tuple
7
- import spaces # For ZeroGPU support
 
8
 
9
- # Image generation models
10
  from diffusers import (
11
- DiffusionPipeline, StableDiffusionPipeline,
12
- StableDiffusionXLPipeline, AutoPipelineForText2Image,
13
- AnimateDiffPipeline, DiffusionPipeline as VideoPipeline
 
14
  )
 
15
 
16
- device = "cuda" if torch.cuda.is_available() else "cpu"
 
 
17
  MAX_SEED = np.iinfo(np.int32).max
18
- MAX_IMAGE_SIZE = 1024
19
 
20
- # Model configurations optimized for different hardware
21
  MODEL_CONFIGS = {
22
- "Image Models": {
23
- "SDXL-Turbo (Fast)": {
24
- "repo_id": "stabilityai/sdxl-turbo",
25
- "pipeline_class": "auto",
26
- "cpu_friendly": True,
27
- "vram_usage": "Low",
28
- "default_steps": 2,
29
- "default_guidance": 0.0,
30
- "torch_dtype": torch.float16 if torch.cuda.is_available() else torch.float32
31
- },
32
- "SD 1.5 (CPU Optimized)": {
33
- "repo_id": "runwayml/stable-diffusion-v1-5",
34
- "pipeline_class": "sd15",
35
- "cpu_friendly": True,
36
- "vram_usage": "Low",
37
- "default_steps": 20,
38
- "default_guidance": 7.5,
39
- "torch_dtype": torch.float32
40
- },
41
- "SD 2.1 (Balanced)": {
42
- "repo_id": "stabilityai/stable-diffusion-2-1",
43
- "pipeline_class": "sd21",
44
- "cpu_friendly": False,
45
- "vram_usage": "Medium",
46
- "default_steps": 25,
47
- "default_guidance": 7.5,
48
- "torch_dtype": torch.float16 if torch.cuda.is_available() else torch.float32
49
- },
50
- "SDXL Base (High Quality)": {
51
- "repo_id": "stabilityai/stable-diffusion-xl-base-1.0",
52
- "pipeline_class": "sdxl",
53
- "cpu_friendly": False,
54
- "vram_usage": "High",
55
- "default_steps": 30,
56
- "default_guidance": 7.5,
57
- "torch_dtype": torch.float16 if torch.cuda.is_available() else torch.float32
58
- }
59
  },
60
- "Video Models": {
61
- "AnimateDiff (Motion)": {
62
- "repo_id": "guoyww/animatediff-motion-adapter-v1-5-2",
63
- "pipeline_class": "animatediff",
64
- "cpu_friendly": False,
65
- "vram_usage": "High",
66
- "default_steps": 25,
67
- "default_guidance": 7.5,
68
- "torch_dtype": torch.float16 if torch.cuda.is_available() else torch.float32
69
- },
70
- "Zeroscope v2 (Text-to-Video)": {
71
- "repo_id": "cerspense/zeroscope_v2_576w",
72
- "pipeline_class": "video",
73
- "cpu_friendly": False,
74
- "vram_usage": "Very High",
75
- "default_steps": 40,
76
- "default_guidance": 9.0,
77
- "torch_dtype": torch.float16 if torch.cuda.is_available() else torch.float32
78
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  }
80
  }
81
 
82
- # Global pipeline cache
83
  current_pipeline = None
84
  current_model_name = None
85
 
86
- def clear_pipeline():
87
- """Clear current pipeline to free memory"""
88
  global current_pipeline
89
  if current_pipeline is not None:
90
  del current_pipeline
91
  current_pipeline = None
92
- gc.collect()
93
- if torch.cuda.is_available():
94
- torch.cuda.empty_cache()
95
-
96
- def get_pipeline_class(pipeline_type: str):
97
- """Get the appropriate pipeline class"""
98
- if pipeline_type == "auto":
99
- return AutoPipelineForText2Image
100
- elif pipeline_type == "sd15":
101
- return StableDiffusionPipeline
102
- elif pipeline_type == "sd21":
103
- return StableDiffusionPipeline
104
- elif pipeline_type == "sdxl":
105
- return StableDiffusionXLPipeline
106
- elif pipeline_type == "animatediff":
107
- return AnimateDiffPipeline
108
- elif pipeline_type == "video":
109
- return VideoPipeline
110
- else:
111
- return DiffusionPipeline
112
 
113
- def load_model(model_name: str, model_type: str = "Image Models"):
114
- """Load a model with memory optimization"""
115
  global current_pipeline, current_model_name
116
 
 
117
  if current_model_name == model_name and current_pipeline is not None:
118
- return current_pipeline
119
 
120
  # Clear previous model
121
- clear_pipeline()
122
-
123
- config = MODEL_CONFIGS[model_type][model_name]
124
- pipeline_class = get_pipeline_class(config["pipeline_class"])
125
 
126
  try:
127
- # Load with optimizations
128
- pipe = pipeline_class.from_pretrained(
 
 
129
  config["repo_id"],
130
  torch_dtype=config["torch_dtype"],
131
- use_safetensors=True,
132
- variant="fp16" if torch.cuda.is_available() and config["torch_dtype"] == torch.float16 else None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  )
134
 
135
- # Apply optimizations based on hardware
136
- if torch.cuda.is_available():
137
- pipe = pipe.to(device)
138
- # Enable memory efficient attention
139
- if hasattr(pipe, 'enable_attention_slicing'):
140
- pipe.enable_attention_slicing()
141
- if hasattr(pipe, 'enable_xformers_memory_efficient_attention'):
142
- try:
143
- pipe.enable_xformers_memory_efficient_attention()
144
- except:
145
- pass
146
- else:
147
- pipe = pipe.to(device)
148
- # CPU optimizations
149
- if hasattr(pipe, 'enable_attention_slicing'):
150
- pipe.enable_attention_slicing()
151
 
152
  current_pipeline = pipe
153
  current_model_name = model_name
154
- return pipe
 
155
 
156
  except Exception as e:
157
- return f"Error loading model: {str(e)}"
158
 
159
- @spaces.GPU(duration=60) # ZeroGPU support
160
- def generate_image(
161
  model_name: str,
162
  prompt: str,
163
  negative_prompt: str,
@@ -171,28 +150,37 @@ def generate_image(
171
  ) -> Tuple[Optional[np.ndarray], int, str]:
172
 
173
  if not prompt.strip():
174
- return None, seed, "Please enter a prompt"
 
 
 
 
 
175
 
176
  try:
177
- # Load model
178
- pipe = load_model(model_name, "Image Models")
179
- if isinstance(pipe, str): # Error message
180
- return None, seed, pipe
181
-
182
  # Handle seed
183
  if randomize_seed:
184
  seed = random.randint(0, MAX_SEED)
185
 
186
- generator = torch.Generator(device=device).manual_seed(seed)
 
 
 
 
 
 
 
 
 
 
187
 
188
- # Adjust parameters for CPU
189
- if device == "cpu":
190
- width = min(width, 512)
191
- height = min(height, 512)
192
- num_inference_steps = min(num_inference_steps, 20)
193
 
194
- # Generate image
195
- with torch.autocast(device):
 
 
196
  result = pipe(
197
  prompt=prompt,
198
  negative_prompt=negative_prompt if negative_prompt.strip() else None,
@@ -201,295 +189,235 @@ def generate_image(
201
  width=width,
202
  height=height,
203
  generator=generator,
 
204
  )
205
 
206
  image = result.images[0]
207
- return image, seed, "βœ… Image generated successfully!"
208
-
209
- except Exception as e:
210
- error_msg = f"❌ Generation failed: {str(e)}"
211
- if "out of memory" in str(e).lower():
212
- error_msg += "\nπŸ’‘ Try: Lower resolution, fewer steps, or use a CPU-friendly model"
213
- return None, seed, error_msg
214
-
215
- @spaces.GPU(duration=120) # Longer duration for video
216
- def generate_video(
217
- model_name: str,
218
- prompt: str,
219
- negative_prompt: str,
220
- seed: int,
221
- randomize_seed: bool,
222
- num_frames: int,
223
- guidance_scale: float,
224
- num_inference_steps: int,
225
- progress=gr.Progress(track_tqdm=True),
226
- ) -> Tuple[Optional[str], int, str]:
227
-
228
- if not prompt.strip():
229
- return None, seed, "Please enter a prompt"
230
-
231
- if device == "cpu":
232
- return None, seed, "❌ Video generation requires GPU"
233
-
234
- try:
235
- # Load model
236
- pipe = load_model(model_name, "Video Models")
237
- if isinstance(pipe, str): # Error message
238
- return None, seed, pipe
239
 
240
- # Handle seed
241
- if randomize_seed:
242
- seed = random.randint(0, MAX_SEED)
243
-
244
- generator = torch.Generator(device=device).manual_seed(seed)
245
-
246
- # Generate video
247
- with torch.autocast(device):
248
- if "animatediff" in model_name.lower():
249
- result = pipe(
250
- prompt=prompt,
251
- negative_prompt=negative_prompt if negative_prompt.strip() else None,
252
- num_frames=num_frames,
253
- guidance_scale=guidance_scale,
254
- num_inference_steps=num_inference_steps,
255
- generator=generator,
256
- )
257
- # Save as GIF
258
- video_path = "output_video.gif"
259
- result.export_to_gif(video_path)
260
- else:
261
- result = pipe(
262
- prompt=prompt,
263
- negative_prompt=negative_prompt if negative_prompt.strip() else None,
264
- num_frames=num_frames,
265
- guidance_scale=guidance_scale,
266
- num_inference_steps=num_inference_steps,
267
- generator=generator,
268
- )
269
- # Save as MP4
270
- video_path = "output_video.mp4"
271
- result.export_to_video(video_path)
272
 
273
- return video_path, seed, "βœ… Video generated successfully!"
274
 
275
  except Exception as e:
276
- error_msg = f"❌ Video generation failed: {str(e)}"
277
- if "out of memory" in str(e).lower():
278
- error_msg += "\nπŸ’‘ Try: Fewer frames, lower steps, or switch to image generation"
279
  return None, seed, error_msg
280
 
281
- def get_model_defaults(model_name: str, model_type: str):
282
- """Get default values for selected model"""
283
- if model_type in MODEL_CONFIGS and model_name in MODEL_CONFIGS[model_type]:
284
- config = MODEL_CONFIGS[model_type][model_name]
285
- return config["default_steps"], config["default_guidance"]
286
- return 20, 7.5
287
-
288
- # Example prompts
289
- image_examples = [
290
- "A majestic dragon flying over a mystical forest, detailed, 8k",
291
- "Cyberpunk cityscape at night, neon lights, futuristic",
292
- "Portrait of a wise old wizard with glowing eyes",
293
- "Serene mountain lake at sunset, photorealistic"
294
  ]
295
 
296
- video_examples = [
297
- "A cat walking through a magical garden",
298
- "Ocean waves crashing on a beach at sunset",
299
- "A butterfly flying around colorful flowers",
300
- "Clouds moving across a blue sky"
301
- ]
302
-
303
- # CSS for better styling
304
  css = """
305
  #col-container {
306
  margin: 0 auto;
307
- max-width: 900px;
 
308
  }
309
- .model-info {
310
- padding: 10px;
311
- margin: 5px 0;
312
- border-radius: 5px;
313
- background-color: #f0f0f0;
 
314
  }
315
- .status-success {
316
- color: #28a745;
 
317
  }
318
- .status-error {
319
- color: #dc3545;
 
 
320
  }
 
 
 
321
  """
322
 
323
- # Main Gradio interface
324
- with gr.Blocks(css=css, title="Multi-Model AI Generator") as demo:
325
  with gr.Column(elem_id="col-container"):
326
- gr.Markdown("# 🎨 Multi-Model AI Generator")
327
- gr.Markdown("Generate images and videos using various AI models optimized for different hardware configurations.")
 
 
328
 
329
- # Hardware info
330
- hardware_info = f"πŸ–₯️ **Device**: {device.upper()}"
331
- if torch.cuda.is_available():
332
- gpu_name = torch.cuda.get_device_name(0)
333
- vram_gb = torch.cuda.get_device_properties(0).total_memory / 1e9
334
- hardware_info += f" ({gpu_name}, {vram_gb:.1f}GB VRAM)"
335
- gr.Markdown(hardware_info)
336
 
337
- with gr.Tabs():
338
- # IMAGE GENERATION TAB
339
- with gr.TabItem("πŸ–ΌοΈ Image Generation"):
340
- with gr.Row():
341
- with gr.Column(scale=3):
342
- img_prompt = gr.Text(
343
- label="Prompt",
344
- placeholder="Describe the image you want to generate...",
345
- lines=2
346
- )
347
- with gr.Column(scale=1):
348
- img_generate_btn = gr.Button("🎨 Generate Image", variant="primary", size="lg")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
 
350
  with gr.Row():
351
- with gr.Column(scale=2):
352
- img_model_dropdown = gr.Dropdown(
353
- choices=list(MODEL_CONFIGS["Image Models"].keys()),
354
- value="SDXL-Turbo (Fast)",
355
- label="Model",
356
- info="Choose based on your hardware capabilities"
357
- )
358
-
359
- # Model info display
360
- img_model_info = gr.Markdown("", elem_classes="model-info")
361
-
362
- with gr.Column(scale=3):
363
- img_result = gr.Image(label="Generated Image", height=400)
364
 
365
- with gr.Accordion("βš™οΈ Advanced Settings", open=False):
366
- with gr.Row():
367
- img_negative_prompt = gr.Text(
368
- label="Negative Prompt",
369
- placeholder="What you don't want in the image...",
370
- lines=1
371
- )
372
-
373
- with gr.Row():
374
- img_seed = gr.Slider(0, MAX_SEED, value=0, label="Seed")
375
- img_randomize_seed = gr.Checkbox(label="Random Seed", value=True)
376
-
377
- with gr.Row():
378
- img_width = gr.Slider(256, MAX_IMAGE_SIZE, value=512, step=64, label="Width")
379
- img_height = gr.Slider(256, MAX_IMAGE_SIZE, value=512, step=64, label="Height")
380
-
381
- with gr.Row():
382
- img_guidance = gr.Slider(0.0, 20.0, value=7.5, step=0.5, label="Guidance Scale")
383
- img_steps = gr.Slider(1, 50, value=20, step=1, label="Inference Steps")
384
-
385
- img_status = gr.Markdown("Ready to generate!", elem_classes="status-success")
386
- gr.Examples(examples=image_examples, inputs=[img_prompt])
387
-
388
- # VIDEO GENERATION TAB
389
- with gr.TabItem("🎬 Video Generation"):
390
  with gr.Row():
391
- with gr.Column(scale=3):
392
- vid_prompt = gr.Text(
393
- label="Prompt",
394
- placeholder="Describe the video you want to generate...",
395
- lines=2
396
- )
397
- with gr.Column(scale=1):
398
- vid_generate_btn = gr.Button("🎬 Generate Video", variant="primary", size="lg")
 
 
399
 
400
  with gr.Row():
401
- with gr.Column(scale=2):
402
- vid_model_dropdown = gr.Dropdown(
403
- choices=list(MODEL_CONFIGS["Video Models"].keys()),
404
- value="AnimateDiff (Motion)",
405
- label="Model",
406
- info="Video generation requires GPU"
407
- )
408
-
409
- vid_model_info = gr.Markdown("", elem_classes="model-info")
410
-
411
- with gr.Column(scale=3):
412
- vid_result = gr.Video(label="Generated Video", height=400)
413
-
414
- with gr.Accordion("βš™οΈ Video Settings", open=False):
415
- with gr.Row():
416
- vid_negative_prompt = gr.Text(
417
- label="Negative Prompt",
418
- placeholder="What you don't want in the video...",
419
- lines=1
420
- )
421
-
422
- with gr.Row():
423
- vid_seed = gr.Slider(0, MAX_SEED, value=0, label="Seed")
424
- vid_randomize_seed = gr.Checkbox(label="Random Seed", value=True)
425
-
426
- with gr.Row():
427
- vid_frames = gr.Slider(8, 64, value=16, step=8, label="Number of Frames")
428
- vid_guidance = gr.Slider(1.0, 20.0, value=7.5, step=0.5, label="Guidance Scale")
429
- vid_steps = gr.Slider(10, 50, value=25, step=1, label="Inference Steps")
430
-
431
- vid_status = gr.Markdown("Ready to generate!", elem_classes="status-success")
432
- gr.Examples(examples=video_examples, inputs=[vid_prompt])
433
 
434
- # Model info update functions
435
- def update_img_model_info(model_name):
436
- config = MODEL_CONFIGS["Image Models"][model_name]
437
- info = f"""
438
- **VRAM Usage**: {config['vram_usage']} | **CPU Friendly**: {'βœ…' if config['cpu_friendly'] else '❌'}
 
 
 
439
 
440
- **Recommended Settings**: {config['default_steps']} steps, {config['default_guidance']} guidance
441
- """
442
- steps, guidance = get_model_defaults(model_name, "Image Models")
443
- return info, steps, guidance
444
-
445
- def update_vid_model_info(model_name):
446
- config = MODEL_CONFIGS["Video Models"][model_name]
447
- info = f"""
448
- **VRAM Usage**: {config['vram_usage']} | **CPU Friendly**: {'βœ…' if config['cpu_friendly'] else '❌'}
449
 
450
- **Recommended Settings**: {config['default_steps']} steps, {config['default_guidance']} guidance
451
- """
452
- steps, guidance = get_model_defaults(model_name, "Video Models")
453
- return info, steps, guidance
 
454
 
455
- # Event handlers
456
- img_model_dropdown.change(
457
- update_img_model_info,
458
- inputs=[img_model_dropdown],
459
- outputs=[img_model_info, img_steps, img_guidance]
460
  )
461
 
462
- vid_model_dropdown.change(
463
- update_vid_model_info,
464
- inputs=[vid_model_dropdown],
465
- outputs=[vid_model_info, vid_steps, vid_guidance]
466
- )
467
-
468
- # Generation event handlers
469
- img_generate_btn.click(
470
- generate_image,
471
- inputs=[
472
- img_model_dropdown, img_prompt, img_negative_prompt,
473
- img_seed, img_randomize_seed, img_width, img_height,
474
- img_guidance, img_steps
475
- ],
476
- outputs=[img_result, img_seed, img_status]
477
- )
478
-
479
- vid_generate_btn.click(
480
- generate_video,
481
- inputs=[
482
- vid_model_dropdown, vid_prompt, vid_negative_prompt,
483
- vid_seed, vid_randomize_seed, vid_frames,
484
- vid_guidance, vid_steps
485
- ],
486
- outputs=[vid_result, vid_seed, vid_status]
487
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
488
 
489
  if __name__ == "__main__":
490
  demo.launch(
491
  share=True,
492
  server_name="0.0.0.0",
493
  server_port=7860,
494
- show_error=True
 
495
  )
 
4
  import torch
5
  import gc
6
  from typing import Optional, Tuple
7
+ import warnings
8
+ warnings.filterwarnings("ignore")
9
 
10
+ # CPU-optimized imports
11
  from diffusers import (
12
+ StableDiffusionPipeline,
13
+ DiffusionPipeline,
14
+ DDIMScheduler,
15
+ DPMSolverMultistepScheduler
16
  )
17
+ from transformers import CLIPTokenizer
18
 
19
+ # Force CPU usage and optimize for low-resource environment
20
+ device = "cpu"
21
+ torch.set_num_threads(2) # Match vCPU count
22
  MAX_SEED = np.iinfo(np.int32).max
 
23
 
24
+ # CPU-optimized model configurations
25
  MODEL_CONFIGS = {
26
+ "πŸš€ Tiny SD (Ultra Fast)": {
27
+ "repo_id": "nota-ai/bk-sdm-small",
28
+ "torch_dtype": torch.float32,
29
+ "max_resolution": 512,
30
+ "default_steps": 10,
31
+ "default_guidance": 6.0,
32
+ "memory_usage": "Very Low",
33
+ "speed": "Ultra Fast"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  },
35
+ "⚑ SD 1.4 (Fast)": {
36
+ "repo_id": "CompVis/stable-diffusion-v1-4",
37
+ "torch_dtype": torch.float32,
38
+ "max_resolution": 512,
39
+ "default_steps": 15,
40
+ "default_guidance": 7.5,
41
+ "memory_usage": "Low",
42
+ "speed": "Fast"
43
+ },
44
+ "🎨 SD 1.5 (Balanced)": {
45
+ "repo_id": "runwayml/stable-diffusion-v1-5",
46
+ "torch_dtype": torch.float32,
47
+ "max_resolution": 512,
48
+ "default_steps": 20,
49
+ "default_guidance": 7.5,
50
+ "memory_usage": "Medium",
51
+ "speed": "Medium"
52
+ },
53
+ "πŸ–ΌοΈ OpenJourney (Artistic)": {
54
+ "repo_id": "prompthero/openjourney",
55
+ "torch_dtype": torch.float32,
56
+ "max_resolution": 512,
57
+ "default_steps": 18,
58
+ "default_guidance": 8.0,
59
+ "memory_usage": "Medium",
60
+ "speed": "Medium"
61
+ },
62
+ "🌟 Dreamlike (Quality)": {
63
+ "repo_id": "dreamlike-art/dreamlike-diffusion-1.0",
64
+ "torch_dtype": torch.float32,
65
+ "max_resolution": 448,
66
+ "default_steps": 25,
67
+ "default_guidance": 8.0,
68
+ "memory_usage": "Medium-High",
69
+ "speed": "Slower"
70
  }
71
  }
72
 
73
+ # Global variables for memory management
74
  current_pipeline = None
75
  current_model_name = None
76
 
77
+ def clear_memory():
78
+ """Aggressive memory cleanup for CPU environment"""
79
  global current_pipeline
80
  if current_pipeline is not None:
81
  del current_pipeline
82
  current_pipeline = None
83
+
84
+ # Force garbage collection
85
+ gc.collect()
86
+
87
+ # Clear any cached models
88
+ torch.cuda.empty_cache() if torch.cuda.is_available() else None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
+ def load_model_cpu_optimized(model_name: str):
91
+ """Load model with maximum CPU optimization"""
92
  global current_pipeline, current_model_name
93
 
94
+ # Return cached pipeline if same model
95
  if current_model_name == model_name and current_pipeline is not None:
96
+ return current_pipeline, "βœ… Using cached model"
97
 
98
  # Clear previous model
99
+ clear_memory()
 
 
 
100
 
101
  try:
102
+ config = MODEL_CONFIGS[model_name]
103
+
104
+ # Load with CPU optimizations
105
+ pipe = StableDiffusionPipeline.from_pretrained(
106
  config["repo_id"],
107
  torch_dtype=config["torch_dtype"],
108
+ safety_checker=None, # Disable for speed
109
+ requires_safety_checker=False,
110
+ use_safetensors=False, # Faster loading on CPU
111
+ local_files_only=False
112
+ )
113
+
114
+ # Apply CPU-specific optimizations
115
+ pipe = pipe.to(device)
116
+
117
+ # Enable attention slicing for memory efficiency
118
+ pipe.enable_attention_slicing(1)
119
+
120
+ # Use memory efficient scheduler
121
+ pipe.scheduler = DPMSolverMultistepScheduler.from_config(
122
+ pipe.scheduler.config,
123
+ use_karras_sigmas=True,
124
+ algorithm_type="dpmsolver++"
125
  )
126
 
127
+ # Additional CPU optimizations
128
+ pipe.unet.to(memory_format=torch.channels_last)
129
+ pipe.vae.to(memory_format=torch.channels_last)
 
 
 
 
 
 
 
 
 
 
 
 
 
130
 
131
  current_pipeline = pipe
132
  current_model_name = model_name
133
+
134
+ return pipe, f"βœ… {model_name} loaded successfully!"
135
 
136
  except Exception as e:
137
+ return None, f"❌ Failed to load {model_name}: {str(e)}"
138
 
139
+ def generate_image_cpu(
 
140
  model_name: str,
141
  prompt: str,
142
  negative_prompt: str,
 
150
  ) -> Tuple[Optional[np.ndarray], int, str]:
151
 
152
  if not prompt.strip():
153
+ return None, seed, "⚠️ Please enter a prompt"
154
+
155
+ # Load model
156
+ pipe, status = load_model_cpu_optimized(model_name)
157
+ if pipe is None:
158
+ return None, seed, status
159
 
160
  try:
 
 
 
 
 
161
  # Handle seed
162
  if randomize_seed:
163
  seed = random.randint(0, MAX_SEED)
164
 
165
+ generator = torch.Generator().manual_seed(seed)
166
+
167
+ # CPU-specific constraints
168
+ config = MODEL_CONFIGS[model_name]
169
+ max_res = config["max_resolution"]
170
+ width = min(width, max_res)
171
+ height = min(height, max_res)
172
+
173
+ # Ensure dimensions are multiples of 8
174
+ width = (width // 8) * 8
175
+ height = (height // 8) * 8
176
 
177
+ # Limit steps for CPU
178
+ num_inference_steps = min(num_inference_steps, 30)
 
 
 
179
 
180
+ progress(0, desc="Starting generation...")
181
+
182
+ # Generate with CPU optimizations
183
+ with torch.no_grad():
184
  result = pipe(
185
  prompt=prompt,
186
  negative_prompt=negative_prompt if negative_prompt.strip() else None,
 
189
  width=width,
190
  height=height,
191
  generator=generator,
192
+ callback_on_step_end=lambda step, timestep, latents: progress(step/num_inference_steps)
193
  )
194
 
195
  image = result.images[0]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
 
197
+ # Memory cleanup after generation
198
+ del result
199
+ gc.collect()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
 
201
+ return image, seed, f"βœ… Generated {width}x{height} image in {num_inference_steps} steps"
202
 
203
  except Exception as e:
204
+ error_msg = f"❌ Generation failed: {str(e)}"
205
+ if "memory" in str(e).lower() or "out of" in str(e).lower():
206
+ error_msg += "\nπŸ’‘ Try: Smaller resolution (256x256), fewer steps (10-15), or Tiny SD model"
207
  return None, seed, error_msg
208
 
209
+ # Optimized example prompts for CPU generation
210
+ examples = [
211
+ "a cute cat sitting in a garden, digital art",
212
+ "mountain landscape at sunset, beautiful painting",
213
+ "portrait of a smiling person, photography",
214
+ "colorful flowers in a vase, oil painting",
215
+ "futuristic city skyline, concept art",
216
+ "peaceful forest path, nature photography"
 
 
 
 
 
217
  ]
218
 
219
+ # CSS optimized for CPU performance (minimal animations)
 
 
 
 
 
 
 
220
  css = """
221
  #col-container {
222
  margin: 0 auto;
223
+ max-width: 800px;
224
+ padding: 20px;
225
  }
226
+ .model-card {
227
+ padding: 15px;
228
+ margin: 10px 0;
229
+ border-radius: 8px;
230
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
231
+ border: 1px solid #ddd;
232
  }
233
+ .cpu-optimized {
234
+ background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
235
+ font-weight: bold;
236
  }
237
+ .status-text {
238
+ padding: 10px;
239
+ border-radius: 5px;
240
+ margin: 10px 0;
241
  }
242
+ .status-success { background-color: #d4edda; color: #155724; }
243
+ .status-error { background-color: #f8d7da; color: #721c24; }
244
+ .status-warning { background-color: #fff3cd; color: #856404; }
245
  """
246
 
247
+ # Main interface optimized for CPU
248
+ with gr.Blocks(css=css, title="CPU-Optimized AI Image Generator") as demo:
249
  with gr.Column(elem_id="col-container"):
250
+ gr.Markdown("""
251
+ # πŸ–₯️ CPU-Optimized AI Image Generator
252
+ ### Specially optimized for CPU Basic (2 vCPUs, 16GB RAM)
253
+ """)
254
 
255
+ # System info
256
+ gr.Markdown("""
257
+ <div class="model-card cpu-optimized">
258
+ πŸ“Š <strong>System Configuration:</strong> CPU Basic | 2 vCPUs | 16GB RAM | PyTorch CPU
259
+ </div>
260
+ """)
 
261
 
262
+ with gr.Row():
263
+ with gr.Column(scale=3):
264
+ prompt = gr.Textbox(
265
+ label="✨ Your Creative Prompt",
266
+ placeholder="Describe the image you want to create...",
267
+ lines=3,
268
+ max_lines=5
269
+ )
270
+ with gr.Column(scale=1):
271
+ generate_btn = gr.Button(
272
+ "🎨 Generate Image",
273
+ variant="primary",
274
+ size="lg",
275
+ elem_classes="generate-button"
276
+ )
277
+
278
+ # Model selection with detailed info
279
+ with gr.Row():
280
+ model_dropdown = gr.Dropdown(
281
+ choices=list(MODEL_CONFIGS.keys()),
282
+ value="πŸš€ Tiny SD (Ultra Fast)",
283
+ label="πŸ€– AI Model Selection",
284
+ info="Choose based on speed vs quality preference"
285
+ )
286
+
287
+ # Model info display
288
+ model_info_display = gr.Markdown("", elem_classes="model-card")
289
+
290
+ # Generated image display
291
+ result_image = gr.Image(
292
+ label="πŸ–ΌοΈ Generated Image",
293
+ height=400,
294
+ show_label=True
295
+ )
296
+
297
+ # Status display
298
+ status_display = gr.Markdown(
299
+ "πŸš€ Ready to generate! Select a model and enter your prompt.",
300
+ elem_classes="status-text status-success"
301
+ )
302
+
303
+ # Advanced settings in accordion
304
+ with gr.Accordion("βš™οΈ Advanced Settings", open=False):
305
+ with gr.Column():
306
+ negative_prompt = gr.Textbox(
307
+ label="🚫 Negative Prompt (Optional)",
308
+ placeholder="What you don't want in the image...",
309
+ lines=2
310
+ )
311
 
312
  with gr.Row():
313
+ seed = gr.Slider(0, MAX_SEED, value=0, label="🎲 Seed")
314
+ randomize_seed = gr.Checkbox(label="πŸ”„ Random Seed", value=True)
 
 
 
 
 
 
 
 
 
 
 
315
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316
  with gr.Row():
317
+ width = gr.Slider(
318
+ 256, 512, value=384, step=64,
319
+ label="πŸ“ Width",
320
+ info="Lower = faster generation"
321
+ )
322
+ height = gr.Slider(
323
+ 256, 512, value=384, step=64,
324
+ label="πŸ“ Height",
325
+ info="Lower = faster generation"
326
+ )
327
 
328
  with gr.Row():
329
+ guidance_scale = gr.Slider(
330
+ 1.0, 15.0, value=7.5, step=0.5,
331
+ label="🎯 Guidance Scale",
332
+ info="How closely to follow the prompt"
333
+ )
334
+ num_inference_steps = gr.Slider(
335
+ 5, 30, value=15, step=1,
336
+ label="πŸ”„ Steps",
337
+ info="More steps = better quality but slower"
338
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
339
 
340
+ # CPU Performance Tips
341
+ with gr.Accordion("πŸ’‘ CPU Optimization Tips", open=False):
342
+ gr.Markdown("""
343
+ ### πŸš€ For Fastest Generation:
344
+ - Use **Tiny SD** model
345
+ - Set resolution to **256x256** or **384x384**
346
+ - Use **10-15 steps**
347
+ - Keep guidance scale around **6-8**
348
 
349
+ ### 🎨 For Best Quality:
350
+ - Use **Dreamlike** or **SD 1.5** model
351
+ - Set resolution to **512x512** (max)
352
+ - Use **20-25 steps**
353
+ - Guidance scale **7-9**
 
 
 
 
354
 
355
+ ### ⚑ Memory Saving:
356
+ - Generate one image at a time
357
+ - Use shorter prompts when possible
358
+ - Avoid very high guidance scales (>12)
359
+ """)
360
 
361
+ # Examples
362
+ gr.Examples(
363
+ examples=examples,
364
+ inputs=[prompt],
365
+ label="πŸ’‘ Example Prompts (Click to try!)"
366
  )
367
 
368
+ # Footer with helpful info
369
+ gr.Markdown("""
370
+ ---
371
+ <div style="text-align: center; color: #666; font-size: 0.9em;">
372
+ πŸ–₯️ Optimized for CPU Basic | Generation time: 30s-3min depending on settings
373
+ </div>
374
+ """)
375
+
376
+ # Function to update model info
377
+ def update_model_info(model_name):
378
+ config = MODEL_CONFIGS[model_name]
379
+ info = f"""
380
+ <div class="model-card">
381
+ <strong>{model_name}</strong><br>
382
+ πŸ“Š <strong>Memory Usage:</strong> {config['memory_usage']} |
383
+ ⚑ <strong>Speed:</strong> {config['speed']}<br>
384
+ πŸ“ <strong>Max Resolution:</strong> {config['max_resolution']}px |
385
+ πŸ”„ <strong>Recommended Steps:</strong> {config['default_steps']}<br>
386
+ 🎯 <strong>Recommended Guidance:</strong> {config['default_guidance']}
387
+ </div>
388
+ """
389
+ return info, config['default_steps'], config['default_guidance']
390
+
391
+ # Event handlers
392
+ model_dropdown.change(
393
+ update_model_info,
394
+ inputs=[model_dropdown],
395
+ outputs=[model_info_display, num_inference_steps, guidance_scale]
396
+ )
397
+
398
+ # Generation handler
399
+ generate_btn.click(
400
+ generate_image_cpu,
401
+ inputs=[
402
+ model_dropdown, prompt, negative_prompt,
403
+ seed, randomize_seed, width, height,
404
+ guidance_scale, num_inference_steps
405
+ ],
406
+ outputs=[result_image, seed, status_display]
407
+ )
408
+
409
+ # Auto-trigger model info update on load
410
+ demo.load(
411
+ update_model_info,
412
+ inputs=[model_dropdown],
413
+ outputs=[model_info_display, num_inference_steps, guidance_scale]
414
+ )
415
 
416
  if __name__ == "__main__":
417
  demo.launch(
418
  share=True,
419
  server_name="0.0.0.0",
420
  server_port=7860,
421
+ show_error=True,
422
+ quiet=True
423
  )