CoolFace
Datasetpublic

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)

sourceHugging Faceapache-2.0updated 4mo agoView on Hugging Face
3likes1.6kdownloads
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)