ysn-rfd/text-dataset-tiny-code-script-py-format
USED of tahamajs/medicine_ds_persian for .parquet file USED of Alijafarixcs2/persian-it-llama2-2k for .parquet file USED of Abirate/english_quotes for .jsonl file NEW FILES (05/12/2025) NEW FILES (12/26/2025) NEW FILES (02/15/2026)
31.6k
1"""
2Complete Python code for CPU-optimized Stable Diffusion with Multiple LoRA Support and Image-to-Image
3Fixed: Proper implementation for loading and applying multiple LoRAs simultaneously
4Enhanced: Image-to-Image functionality with exact latent size and VAE-only quantization
5Code By: YSNRFD (Updated with Multiple LoRA and Image-to-Image improvements)
6Telegram: @ysnrfd
7Github: ysnrfd
8Huggingface: ysnrfd
9"""
10
11import torch
12from diffusers import StableDiffusionPipeline, StableDiffusionImg2ImgPipeline, LCMScheduler
13import time
14import random
15import gc
16import os
17import psutil
18from tqdm import tqdm
19import safetensors
20import sys
21import diffusers
22from PIL import Image
23import numpy as np
24
25def setup_cpu_memory_optimizations(pipe, skip_quantization=False, img2img_mode=False):
26 """
27 Apply all memory reduction methods specifically optimized for CPU usage
28
29 Parameters:
30 - pipe: Stable Diffusion pipeline
31 - skip_quantization: Whether to skip full quantization (needed for LoRA)
32 - img2img_mode: Whether in image-to-image mode (only VAE quantized)
33
34 Returns:
35 - pipe: Pipeline with applied CPU memory optimizations
36 """
37 print("\n" + "="*60)
38 print("Applying CPU-specific memory reduction methods")
39 print("="*60)
40
41 # تعیین حالت کوانتیزاسیون بر اساس نوع پردازش
42 if img2img_mode:
43 print("Image-to-Image mode detected: Only VAE will be quantized")
44 quantize_vae = True
45 quantize_unet = False
46 quantize_text_encoder = False
47 target_memory = "~1.2-1.8GB"
48 elif skip_quantization:
49 print("\nSkipping full quantization due to LoRA usage")
50 print("Quantizing VAE only to save memory...")
51 quantize_vae = True
52 quantize_unet = False
53 quantize_text_encoder = False
54 target_memory = "~1.8-2.0GB"
55 else:
56 print("Performing full 8-bit Quantization for CPU...")
57 quantize_vae = True
58 quantize_unet = True
59 quantize_text_encoder = True
60 target_memory = "~1.0-1.5GB"
61
62 # 1. 8-bit Quantization (50% memory reduction) - selective based on mode
63 if quantize_vae:
64 print("Performing 8-bit Quantization for VAE...")
65 pipe.vae = torch.quantization.quantize_dynamic(
66 pipe.vae,
67 {torch.nn.Linear, torch.nn.Conv2d},
68 dtype=torch.qint8
69 )
70 print("VAE quantized to 8-bit (saves ~150MB memory)")
71
72 if quantize_unet:
73 print("Performing 8-bit Quantization for UNet...")
74 pipe.unet = torch.quantization.quantize_dynamic(
75 pipe.unet,
76 {torch.nn.Linear, torch.nn.Conv2d},
77 dtype=torch.qint8
78 )
79 print("UNet quantized to 8-bit (50% memory reduction)")
80
81 if quantize_text_encoder:
82 print("Performing 8-bit Quantization for Text Encoder...")
83 pipe.text_encoder = torch.quantization.quantize_dynamic(
84 pipe.text_encoder,
85 {torch.nn.Linear},
86 dtype=torch.qint8
87 )
88 print("Text Encoder quantized to 8-bit (30% memory reduction)")
89
90 # 2. Attention Slicing
91 print("\nEnabling Attention Slicing for CPU...")
92 pipe.enable_attention_slicing("max")
93 print("Attention Slicing enabled (20-30% memory reduction)")
94
95 # 3. VAE Slicing
96 print("\nEnabling VAE Slicing for CPU...")
97 pipe.vae.enable_slicing()
98 print("VAE Slicing enabled (15-25% memory reduction)")
99
100 # 4. CPU-specific optimizations
101 print("\nConfiguring CPU-specific settings...")
102
103 # Disable progress bars to save memory
104 if hasattr(pipe, "set_progress_bar_config"):
105 pipe.set_progress_bar_config(disable=False)
106 print("Progress display enabled (Not affect)")
107
108 # Set optimal number of threads based on CPU cores
109 cpu_cores = max(1, os.cpu_count() // 1)
110 torch.set_num_threads(cpu_cores)
111 torch.set_num_interop_threads(1)
112 print(f"CPU thread settings optimized: {cpu_cores} threads")
113
114 # 5. Additional CPU optimizations
115 print("\nApplying additional CPU optimizations...")
116 torch.backends.cudnn.benchmark = False
117 torch.backends.cudnn.deterministic = True
118 print("CUDA backend settings adjusted for CPU compatibility")
119
120 print("\n" + "="*60)
121 print("All CPU memory reduction methods have been applied")
122 print(f"Target memory usage: {target_memory} (vs 2.1GB without optimizations)")
123 print("="*60)
124
125 return pipe
126
127def verify_memory_usage():
128 """Verify memory usage for CPU systems using psutil"""
129 print("\n" + "="*60)
130 print("Verifying memory usage after optimization")
131 print("="*60)
132
133 try:
134 process = psutil.Process()
135 memory_info = process.memory_info()
136 print(f"Process memory usage: {memory_info.rss / (1024**3):.2f} GB")
137
138 virtual_memory = psutil.virtual_memory()
139 print(f"System available memory: {virtual_memory.available / (1024**3):.2f} GB")
140 print(f"System total memory: {virtual_memory.total / (1024**3):.2f} GB")
141 print(f"Memory usage percentage: {virtual_memory.percent:.1f}%")
142 except Exception as e:
143 print(f"Memory verification error: {str(e)}")
144 print("Memory verification requires 'psutil' package (install with: pip install psutil)")
145
146 print("="*60)
147
148def generate_random_seed():
149 """Generate a random seed between 0 and 1000000000"""
150 return random.randint(0, 1000000000)
151
152def check_system_resources(skip_quantization=False):
153 """Check if system has enough resources to run Stable Diffusion on CPU"""
154 print("\n" + "="*60)
155 print("Checking system resources for CPU execution")
156 print("="*60)
157
158 # Check available memory
159 virtual_memory = psutil.virtual_memory()
160 available_gb = virtual_memory.available / (1024**3)
161
162 # Adjust required memory based on quantization
163 required_memory = 1.5 if not skip_quantization else 2.0
164
165 if available_gb < required_memory:
166 print(f"WARNING: Low available memory ({available_gb:.2f} GB).")
167 print(f"Stable Diffusion may fail (requires ~{required_memory:.1f}GB).")
168 print("Consider closing other applications before proceeding.")
169 else:
170 print(f"Sufficient memory available: {available_gb:.2f} GB")
171
172 # Check CPU cores
173 cpu_cores = os.cpu_count()
174 print(f"Detected CPU cores: {cpu_cores}")
175
176 if cpu_cores < 2:
177 print("WARNING: Very few CPU cores detected. Generation will be extremely slow.")
178 elif cpu_cores < 4:
179 print("Note: Few CPU cores detected. Generation will be slow but possible.")
180 else:
181 print("Sufficient CPU cores for reasonable generation speed.")
182
183 print("="*60)
184 return available_gb >= required_memory
185
186def load_multiple_loras(pipe, lora_paths, weights=None):
187 """
188 Load and apply multiple LoRA weights with individual strengths
189
190 Parameters:
191 - pipe: Stable Diffusion pipeline
192 - lora_paths: List of paths to LoRA weights files
193 - weights: List of strengths for each LoRA (0.1-1.5)
194
195 Returns:
196 - bool: Whether all LoRAs were loaded successfully
197 """
198 print("\n" + "="*60)
199 print("LOADING MULTIPLE LoRAs")
200 print("="*60)
201
202 if not lora_paths:
203 print("No LoRA paths provided")
204 return False
205
206 # تنظیم وزنها اگر مشخص نشده باشند
207 if weights is None:
208 weights = [1.0] * len(lora_paths)
209 elif isinstance(weights, (int, float)):
210 weights = [weights] * len(lora_paths)
211 elif len(weights) < len(lora_paths):
212 # اگر تعداد وزنها کمتر از تعداد LoRAها باشد، وزنهای باقیمانده را 1.0 قرار میدهیم
213 weights = weights + [1.0] * (len(lora_paths) - len(weights))
214
215 adapter_names = []
216 successful_loads = 0
217
218 # بارگذاری هر LoRA با نام منحصر به فرد
219 for i, (lora_path, weight) in enumerate(zip(lora_paths, weights)):
220 print(f"\n--- LoRA #{i+1} ---")
221 print(f"Path: {os.path.basename(lora_path)}")
222 print(f"Strength: {weight:.2f}")
223
224 # تولید نام منحصر به فرد برای هر آداپتور
225 adapter_name = f"lora_{i+1}"
226 adapter_names.append(adapter_name)
227
228 try:
229 # بررسی وجود فایل
230 if not os.path.exists(lora_path):
231 print(f"ERROR: File not found at {lora_path}")
232 continue
233
234 # بارگذاری LoRA با نام آداپتور مشخص
235 print(f"Loading LoRA as '{adapter_name}'...")
236 pipe.load_lora_weights(lora_path, adapter_name=adapter_name)
237
238 # دریافت لیست آداپتورها برای تأیید
239 try:
240 adapters_dict = pipe.get_list_adapters()
241 print(f"Current adapters: {adapters_dict}")
242 except:
243 pass
244
245 print(f"LoRA loaded successfully as '{adapter_name}'")
246 successful_loads += 1
247
248 except Exception as e:
249 print(f"ERROR loading LoRA: {str(e)}")
250 # اگر بارگذاری یک LoRA با خطا مواجه شد، نام آداپتور را حذف میکنیم
251 if adapter_name in adapter_names:
252 adapter_names.remove(adapter_name)
253
254 # فعالسازی همه LoRAهای موفق
255 if successful_loads > 0:
256 # فیلتر کردن وزنهای مربوط به LoRAهای موفق
257 valid_weights = []
258 for i, adapter_name in enumerate(adapter_names):
259 # پیدا کردن ایندکس اصلی برای وزن مربوطه
260 for j, path in enumerate(lora_paths):
261 if f"lora_{j+1}" == adapter_name:
262 valid_weights.append(weights[j])
263 break
264
265 print("\n" + "="*60)
266 print(f"Activating {successful_loads} LoRA(s) with strengths: {valid_weights}")
267 print("="*60)
268
269 try:
270 # فعالسازی همه آداپتورها با وزنهای مربوطه
271 pipe.set_adapters(adapter_names, valid_weights)
272 print(f"\nSUCCESS: {successful_loads} LoRA(s) ACTIVATED AND WORKING!")
273 print(f"Adapter names: {adapter_names}")
274 print(f"Strengths: {valid_weights}")
275 print("="*60)
276 return True
277 except Exception as e:
278 print(f"\nERROR activating adapters: {str(e)}")
279
280 print("\nFAILED to activate any LoRA")
281 print("="*60)
282 return False
283
284def load_image(image_path, target_size=None):
285 """Load and preprocess image for img2img with exact size handling"""
286 try:
287 # Load image
288 image = Image.open(image_path).convert("RGB")
289 original_width, original_height = image.size
290
291 if target_size is not None:
292 # Existing code for fixed size (for txt2img)
293 ratio = min(target_size[0] / original_width, target_size[1] / original_height)
294 new_size = (int(original_width * ratio), int(original_height * ratio))
295 image = image.resize(new_size, Image.LANCZOS)
296
297 # Center crop to target size
298 left = (new_size[0] - target_size[0]) // 2
299 top = (new_size[1] - target_size[1]) // 2
300 image = image.crop((left, top, left + target_size[0], top + target_size[1]))
301
302 print(f"Image loaded and preprocessed: {image_path}")
303 print(f"Original size: {original_width}x{original_height} -> Resized to: {target_size}")
304 else:
305 # NEW: For img2img - maintain original aspect ratio with exact multiple of 8
306 # Calculate dimensions divisible by 8
307 new_width = (original_width // 8) * 8
308 new_height = (original_height // 8) * 8
309
310 # Ensure minimum size (64x64 is VAE minimum)
311 new_width = max(64, new_width)
312 new_height = max(64, new_height)
313
314 # Center crop to exact multiple of 8
315 left = (original_width - new_width) // 2
316 top = (original_height - new_height) // 2
317 image = image.crop((left, top, left + new_width, top + new_height))
318
319 print(f"Image loaded and preprocessed: {image_path}")
320 print(f"Original size: {original_width}x{original_height} -> Cropped to: {new_width}x{new_height} (exact multiple of 8)")
321
322 return image
323 except Exception as e:
324 print(f"Error loading image: {str(e)}")
325 return None
326
327def generate_image_with_cpu_optimizations(pipe, prompt, lora_active=False):
328 """Generate image with all CPU-specific memory optimizations (text-to-image)"""
329 print("\n" + "="*60)
330 print(f"Generating image with CPU optimizations: '{prompt}'")
331 if lora_active:
332 print("LoRA: ACTIVE (enhancing style/quality)")
333 print("="*60)
334
335 # Optimized settings for CPU systems
336 generation_settings = {
337 "prompt": prompt,
338 "negative_prompt": "",
339 "width": 768,
340 "height": 768,
341 "num_inference_steps": 10, # Higher steps compensate for lower precision
342 "guidance_scale": 1.0,
343 "output_type": "pil",
344 "generator": torch.Generator(device="cpu").manual_seed(generate_random_seed())
345 }
346
347 print("CPU generation settings:")
348 print(f" - Image size: {generation_settings['width']}x{generation_settings['height']}")
349 print(f" - Inference steps: {generation_settings['num_inference_steps']}")
350 print(f" - Guidance scale: {generation_settings['guidance_scale']}")
351
352 # Clear memory before generation
353 gc.collect()
354 print("\nMemory cleared before generation")
355
356 # Verify memory before starting
357 verify_memory_usage()
358
359 print("\nStarting image generation...")
360 start_time = time.time()
361
362 try:
363 # Create a progress bar
364 print("\nGenerating image (this may take several minutes on CPU)...")
365 progress_bar = tqdm(total=generation_settings["num_inference_steps"], desc="Processing")
366
367 # Generate image with manual step tracking
368 output = pipe(**generation_settings)
369 image = output.images[0]
370
371 # Update progress bar
372 progress_bar.update(generation_settings["num_inference_steps"])
373 progress_bar.close()
374
375 # Save image
376 timestamp = int(time.time())
377 seed = generation_settings["generator"].initial_seed()
378 lora_suffix = "_multilora" if lora_active else ""
379 output_path = f"cpu_optimized_output{lora_suffix}_{timestamp}_{seed}.png"
380
381 image.save(output_path)
382
383 elapsed = time.time() - start_time
384 print(f"\nImage generated successfully! Time: {elapsed:.2f} seconds")
385 print(f"Settings: {generation_settings['width']}x{generation_settings['height']}")
386 print(f"Saved at: {output_path}")
387
388 # Verify memory after generation
389 verify_memory_usage()
390
391 return image
392
393 except RuntimeError as e:
394 if "out of memory" in str(e).lower():
395 print("\nMemory error: Insufficient RAM!")
396 print("Recommended solutions:")
397 print(" 1. Reduce image size to 384x384 or 256x256")
398 print(" 2. Increase num_inference_steps to 30-40")
399 print(" 3. Close all other applications to free memory")
400 print(" 4. Consider using a smaller model")
401 else:
402 print(f"Error during image generation: {str(e)}")
403 return None
404 except Exception as e:
405 print(f"Unexpected error: {str(e)}")
406 return None
407
408def generate_image_to_image_with_cpu_optimizations(pipe, prompt, image_path, strength=0.75, lora_active=False):
409 """Generate image with all CPU-specific memory optimizations (image-to-image)"""
410 print("\n" + "="*60)
411 print(f"Generating image from image with CPU optimizations: '{prompt}'")
412 print(f"Input image: {os.path.basename(image_path)}")
413 print(f"Transformation strength: {strength:.2f}")
414 if lora_active:
415 print("LoRA: ACTIVE (enhancing style/quality)")
416 print("="*60)
417
418 # Load and preprocess input image WITHOUT fixed size (maintain original dimensions)
419 init_image = load_image(image_path, target_size=None)
420 if init_image is None:
421 print("Failed to load input image. Aborting generation.")
422 return None
423
424 # Optimized settings for CPU systems - NO width/height parameters!
425 generation_settings = {
426 "prompt": prompt,
427 "negative_prompt": "",
428 "image": init_image,
429 "strength": strength,
430 "num_inference_steps": 10, # Higher steps compensate for lower precision
431 "guidance_scale": 1.0,
432 "output_type": "pil",
433 "generator": torch.Generator(device="cpu").manual_seed(generate_random_seed())
434 }
435
436 print("CPU image-to-image settings:")
437 print(f" - Input image size: {init_image.size} (exact multiple of 8)")
438 print(f" - Transformation strength: {generation_settings['strength']}")
439 print(f" - Inference steps: {generation_settings['num_inference_steps']}")
440 print(f" - Guidance scale: {generation_settings['guidance_scale']}")
441
442 # Clear memory before generation
443 gc.collect()
444 print("\nMemory cleared before generation")
445
446 # Verify memory before starting
447 verify_memory_usage()
448
449 print("\nStarting image-to-image generation...")
450 start_time = time.time()
451
452 try:
453 # Create a progress bar
454 print("\nGenerating image (this may take several minutes on CPU)...")
455 progress_bar = tqdm(total=generation_settings["num_inference_steps"], desc="Processing")
456
457 # Generate image with manual step tracking
458 output = pipe(**generation_settings)
459 image = output.images[0]
460
461 # Update progress bar
462 progress_bar.update(generation_settings["num_inference_steps"])
463 progress_bar.close()
464
465 # Save image
466 timestamp = int(time.time())
467 seed = generation_settings["generator"].initial_seed()
468 lora_suffix = "_multilora" if lora_active else ""
469 img2img_suffix = "_img2img"
470 output_path = f"cpu_optimized_output{img2img_suffix}{lora_suffix}_{timestamp}_{seed}.png"
471
472 image.save(output_path)
473
474 elapsed = time.time() - start_time
475 print(f"\nImage generated successfully! Time: {elapsed:.2f} seconds")
476 print(f"Input image: {os.path.basename(image_path)}")
477 print(f"Transformation strength: {strength:.2f}")
478 print(f"Output size: {image.size} (matches input size)")
479 print(f"Saved at: {output_path}")
480
481 # Verify memory after generation
482 verify_memory_usage()
483
484 return image
485
486 except RuntimeError as e:
487 if "out of memory" in str(e).lower():
488 print("\nMemory error: Insufficient RAM!")
489 print("Recommended solutions:")
490 print(" 1. Reduce image size to 384x384 or 256x256")
491 print(" 2. Decrease transformation strength (try 0.5-0.6)")
492 print(" 3. Increase num_inference_steps to 30-40")
493 print(" 4. Close all other applications to free memory")
494 print(" 5. Consider using a smaller model")
495 else:
496 print(f"Error during image generation: {str(e)}")
497 return None
498 except Exception as e:
499 print(f"Unexpected error: {str(e)}")
500 return None
501
502def main():
503 """Main function to run the CPU-optimized Stable Diffusion"""
504 print("="*60)
505 print("CPU-Optimized Stable Diffusion Pipeline with MULTIPLE LoRA Support")
506 print("Fully supports loading and applying multiple LoRAs simultaneously")
507 print("Enhanced: Image-to-Image with exact latent size and VAE-only quantization")
508 print("="*60)
509
510 # 1. Determine generation mode
511 print("\nSelect generation mode:")
512 print("1. Text-to-Image (generate from text prompt)")
513 print("2. Image-to-Image (modify existing image)")
514 mode = input("Enter choice (1 or 2): ").strip()
515
516 if mode not in ['1', '2']:
517 print("Invalid choice. Defaulting to Text-to-Image.")
518 mode = '1'
519
520 is_img2img = (mode == '2')
521
522 # 2. Check for LoRA support requirement
523 print("\nDo you want to use LoRA (Low-Rank Adaptation) for style enhancement? (y/n)")
524 use_lora = input().strip().lower() == 'y'
525 lora_paths = []
526 lora_weights = []
527 skip_quantization = False
528
529 if use_lora:
530 print("\nHow many LoRAs do you want to use? (1-5, default: 1)")
531 num_loras_input = input().strip()
532 num_loras = 1
533 if num_loras_input:
534 try:
535 num_loras = max(1, min(5, int(num_loras_input)))
536 print(f"Configuring for {num_loras} LoRA(s)")
537 except:
538 print("Invalid number, using default: 1")
539
540 # گرفتن اطلاعات هر LoRA
541 for i in range(num_loras):
542 print(f"\n--- LoRA #{i+1} Configuration ---")
543 print(f"Enter path to LoRA weights (safetensors file #{i+1}):")
544 lora_path = input().strip()
545 if not lora_path:
546 if i == 0:
547 print("Main LoRA path not provided. Continuing WITHOUT LoRA.")
548 use_lora = False
549 continue
550 lora_paths.append(lora_path)
551
552 print(f"Enter LoRA #{i+1} strength (0.1-1.5, default 1.0):")
553 weight_input = input().strip()
554 if weight_input:
555 try:
556 weight = float(weight_input)
557 weight = max(0.1, min(1.5, weight))
558 lora_weights.append(weight)
559 print(f"Using LoRA #{i+1} strength: {weight:.2f}")
560 except:
561 print("Invalid value. Using default 1.0")
562 lora_weights.append(1.0)
563 else:
564 lora_weights.append(1.0)
565
566 if lora_paths:
567 skip_quantization = True # Required for LoRA compatibility
568
569 # 3. Check system resources
570 if not check_system_resources(skip_quantization=skip_quantization):
571 print("\nWARNING: System may not have sufficient resources.")
572 print("Continue anyway? (y/n)")
573 if input().lower() != 'y':
574 print("Operation cancelled by user.")
575 return
576
577 # 4. Setup CPU device
578 device = torch.device("cpu")
579 print(f"\nCPU mode initialized: {device}")
580
581 # 5. Model configuration
582 print("\n" + "="*60)
583 print("Model Configuration")
584 print("="*60)
585
586 # Default model path - user should update this
587 default_model_path = "ds_lcm.safetensors"
588 print(f"Default model path: {default_model_path}")
589 print("Enter model path (or press Enter to use default):")
590 model_path = input().strip()
591 if not model_path:
592 model_path = default_model_path
593
594 print(f"Using model path: {model_path}")
595
596 # Verify model file exists
597 if not os.path.exists(model_path):
598 print(f"\nERROR: Model file not found at: {model_path}")
599 print("Please check the path and try again.")
600 return
601
602 # 6. Load model - different pipeline for img2img
603 print("\n" + "="*60)
604 print("Loading Stable Diffusion model")
605 print("="*60)
606
607 try:
608 print("Attempting to load model in float32 mode (CPU compatible)...")
609
610 # Select appropriate pipeline based on mode
611 if is_img2img:
612 print("Loading Image-to-Image pipeline...")
613 pipe = StableDiffusionImg2ImgPipeline.from_single_file(
614 model_path,
615 torch_dtype=torch.float32,
616 use_safetensors=True,
617 safety_checker=None,
618 requires_safety_checker=False
619 )
620 else:
621 print("Loading Text-to-Image pipeline...")
622 pipe = StableDiffusionPipeline.from_single_file(
623 model_path,
624 torch_dtype=torch.float32,
625 use_safetensors=True,
626 safety_checker=None,
627 requires_safety_checker=False
628 )
629
630 # Set LCM Scheduler for faster generation
631 pipe.scheduler = LCMScheduler.from_config(pipe.scheduler.config)
632 pipe = pipe.to(device)
633
634 print("Model loaded successfully in float32 mode")
635 except Exception as e:
636 print(f"\nERROR: Failed to load model: {str(e)}")
637 print("\nTroubleshooting steps:")
638 print("1. Verify the model file exists at the specified path")
639 print("2. Ensure the model is compatible with diffusers library")
640 print("3. Check if you have sufficient disk space")
641 print("4. Try a different model file if available")
642 return
643
644 # 7. Load LoRA weights CORRECTLY if requested
645 lora_loaded = False
646 if use_lora and lora_paths:
647 lora_loaded = load_multiple_loras(pipe, lora_paths, lora_weights)
648
649 # 8. Apply CPU memory optimizations
650 # NEW: img2img mode forces VAE-only quantization
651 optimized_pipe = setup_cpu_memory_optimizations(
652 pipe,
653 skip_quantization=skip_quantization,
654 img2img_mode=is_img2img
655 )
656
657 # 9. Verify memory usage after optimizations
658 verify_memory_usage()
659
660 # 10. Get user input for prompt and image (if img2img)
661 print("\n" + "="*60)
662 print("Generation Parameters")
663 print("="*60)
664
665 print("Enter your prompt (or press Enter for default):")
666 default_prompt = "A beautiful landscape with mountains and a lake, ultra-detailed, realistic, 4k"
667 user_prompt = input().strip()
668 if not user_prompt:
669 user_prompt = default_prompt
670
671 print(f"Using prompt: {user_prompt}")
672
673 # Additional parameters for img2img
674 img2img_strength = 0.75
675 if is_img2img:
676 print("\nEnter path to input image:")
677 image_path = input().strip()
678
679 if not os.path.exists(image_path):
680 print(f"ERROR: Image file not found at: {image_path}")
681 print("Using default image path: input.jpg")
682 image_path = "input.jpg"
683
684 if not os.path.exists(image_path):
685 print("Default image not found. Aborting.")
686 return
687
688 print("\nEnter transformation strength (0.1-1.0, default 0.75):")
689 strength_input = input().strip()
690 if strength_input:
691 try:
692 img2img_strength = float(strength_input)
693 img2img_strength = max(0.1, min(1.0, img2img_strength))
694 print(f"Using transformation strength: {img2img_strength:.2f}")
695 except:
696 print("Invalid value. Using default 0.75")
697
698 # 11. Generate image with CPU optimizations
699 if is_img2img:
700 generate_image_to_image_with_cpu_optimizations(
701 optimized_pipe,
702 user_prompt,
703 image_path,
704 strength=img2img_strength,
705 lora_active=lora_loaded
706 )
707 else:
708 generate_image_with_cpu_optimizations(optimized_pipe, user_prompt, lora_active=lora_loaded)
709
710 # 12. Final cleanup
711 print("\n" + "="*60)
712 print("Cleaning up resources")
713 print("="*60)
714
715 # Clear CUDA cache (even though we're on CPU, just in case)
716 if torch.cuda.is_available():
717 torch.cuda.empty_cache()
718
719 # Garbage collection
720 gc.collect()
721 print("Memory cleanup completed")
722
723 print("\n" + "="*60)
724 print("CPU-Optimized Stable Diffusion Process Completed")
725 print("Note: This implementation is designed for systems with limited resources")
726 print("Multiple LoRA is applied correctly using Diffusers built-in methods")
727 print("No need to add <lora:lora_name> in prompts - LoRA is applied programmatically")
728 print("="*60)
729
730 # نمایش وضعیت نهایی LoRA
731 if lora_loaded:
732 print("\nSUCCESS: Multiple LoRAs were applied CORRECTLY and are affecting your images")
733 print(f"Total LoRAs active: {len(lora_paths)}")
734 else:
735 print("\nWARNING: LoRA(s) were NOT applied - check error messages above")
736 print("="*60)
737
738if __name__ == "__main__":
739 try:
740 main()
741 except KeyboardInterrupt:
742 print("\n\nProcess interrupted by user. Exiting gracefully...")
743 gc.collect()
744 print("Cleanup completed. Exiting.")
745 except Exception as e:
746 print(f"\nFATAL ERROR: {str(e)}")
747 print("\nMOST LIKELY CAUSE:")
748 print("- Outdated diffusers library (MUST be >=0.18.0)")
749 print("- Incompatible LoRA file(s)")
750 print("- Invalid image path for img2img")
751 print("\nSOLUTION:")
752 print("1. UPGRADE diffusers: pip install --upgrade diffusers")
753 print("2. Verify LoRA compatibility with your model")
754 print("3. Check image path for img2img mode")
755 sys.exit(1)