Run locally with 24 GB VRAM some GPU's, gradio script sharing and suggestions.

#25
by NovaYear - opened

Hi guys !
There is no need to give much detail, Once you examine the script, you will understand everything.
Total Usage : 16 GB VRAM + 70 GB RAM (32GB DDR4 + 50GB nvmeRAMdisk) // (my gpu rtx3090)
Times : all model files loading 145 sec // one image edit or generate 95 sec.
Can further adjustments or improvements be made? We can consider this.

CODES :::

import os
import time
import torch
import datetime
import numpy as np
import gradio as gr
from PIL import Image
from transformers import BitsAndBytesConfig as TransformersBitsAndBytesConfig
from transformers import Qwen2_5_VLForConditionalGeneration
from diffusers import BitsAndBytesConfig as DiffusersBitsAndBytesConfig
from diffusers import QwenImageEditPipeline, QwenImageTransformer2DModel

# ========================================================
# MODEL YÜKLEME FONKSİYONLARI
# ========================================================

def load_model():
    start_time = time.time()
    print("🔄 Model yükleniyor... Bu biraz zaman alabilir, ortalama 2 dakika 30 saniye.")

    model_id = "Qwen/Qwen-Image-Edit"
    torch_dtype = torch.bfloat16

    # 1. Görsel Transformer (4-bit)
    print("1/6 - Görsel transformer yükleniyor...")
    quantization_config_diffusers = DiffusersBitsAndBytesConfig(
        load_in_4bit=True,
        bnb_4bit_quant_type="nf4",
        bnb_4bit_compute_dtype=torch.bfloat16,
        llm_int8_skip_modules=["transformer_blocks.0.img_mod"],
    )
    transformer = QwenImageTransformer2DModel.from_pretrained(
        model_id,
        subfolder="transformer",
        quantization_config=quantization_config_diffusers,
        torch_dtype=torch_dtype,
    )
    transformer = transformer.to("cpu")

    # 2. Metin Encoder (4-bit)
    print("2/6 - Metin encoder yükleniyor...")
    quantization_config_transformers = TransformersBitsAndBytesConfig(
        load_in_4bit=True,
        bnb_4bit_quant_type="nf4",
        bnb_4bit_compute_dtype=torch.bfloat16,
    )
    text_encoder = Qwen2_5_VLForConditionalGeneration.from_pretrained(
        model_id,
        subfolder="text_encoder",
        quantization_config=quantization_config_transformers,
        torch_dtype=torch_dtype,
    )
    text_encoder = text_encoder.to("cpu")

    # 3. Pipeline
    print("3/6 - Pipeline oluşturuluyor...")
    pipe = QwenImageEditPipeline.from_pretrained(
        model_id,
        transformer=transformer,
        text_encoder=text_encoder,
        torch_dtype=torch_dtype,
    )

    # 4. LoRA yükle
    print("4/6 - LoRA ağırlıkları yükleniyor...")
    try:
        pipe.load_lora_weights(
            "lightx2v/Qwen-Image-Lightning",
            weight_name="Qwen-Image-Lightning-8steps-V1.1.safetensors"
        )
    except Exception as e:
        print(f"⚠️ LoRA yüklenemedi: {e}")

    # 5. CPU offload
    print("5/6 - CPU offload etkinleştiriliyor...") 
    pipe.enable_model_cpu_offload()

    # 7. İNFERENCE (Görsel düzenleme)
    
    print("6/6 - Inference yükleniyor...")
    print("="*60)

    generator = torch.Generator(device="cuda" if torch.cuda.is_available() else "cpu").manual_seed(42)
    elapsed = time.time() - start_time
    print(f"✅ Model başarıyla yüklendi ve hazır! Bu işlemler ({elapsed:.2f} saniye sürdü.")
   
    
    return pipe

# ========================================================
# GLOBALE PIPELINE (Uygulama başlangıcında bir kez yükle)
# ========================================================

try:
    pipe = load_model()
except Exception as e:
    print(f"❌ Model yüklenemedi: {e}")
    raise

# ========================================================
# RESİM BOYUTU KONTROL VE ÖLÇEKLENDİRME FONKSİYONU
# ========================================================

def resize_image_if_needed(image, max_pixels=1048576):
    """
    Resim boyutunu kontrol eder ve gerekirse proporsiyon koruyarak ölçeklendirir.
    
    Args:
        image: PIL Image objesi
        max_pixels: Maksimum pixel sayısı (varsayılan: 1024x1024=1048576)
    
    Returns:
        PIL Image: Ölçeklendirilmiş resim (gerekirse)
    """
    width, height = image.size
    current_pixels = width * height
    
    print(f"🔍 Resim boyutu kontrol ediliyor: {width}x{height} = {current_pixels:,} pixel")
    
    if current_pixels <= max_pixels:
        print("✅ Resim boyutu uygun, ölçeklendirme gerekli değil.")
        return image
    
    # Ölçeklendirme oranını hesapla
    scale_factor = (max_pixels / current_pixels) ** 0.5
    new_width = int(width * scale_factor)
    new_height = int(height * scale_factor)

    # Yeni boyutları 4'ün katı olacak şekilde ayarla (model gereksinimi)
    new_width = new_width - (new_width % 4)
    new_height = new_height - (new_height % 4)

    # Eğer resim çözünürlüğü çok küçükse, En az 256x256 olacak şekilde güncelle
    new_width = max(new_width, 256)
    new_height = max(new_height, 256)
    
    print(f"📏 Resim ölçeklendiriliyor: {width}x{height} -> {new_width}x{new_height}")
    print(f"📊 Ölçeklendirme oranı: {scale_factor:.3f}")
    
    # Resmi ölçeklendir (LANCZOS en kaliteli yeniden örnekleme)
    resized_image = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
    
    new_pixels = new_width * new_height
    print(f"✅ Ölçeklendirme tamamlandı: {new_pixels:,} pixel")
    
    return resized_image


# ========================================================
# GRADIO RESİM DÜZENLEME FONKSİYONU
# ========================================================

def edit_image(input_image, prompt):
    steps=8
    #seed=42
    if input_image is None:
        return None, "Lütfen bir görüntü yükleyin."
    if not prompt.strip():
        return None, "Lütfen bir açıklama (prompt) girin."

    try:

        # Resim boyutu kontrolü ve ölçeklendirme
        processed_image = resize_image_if_needed(input_image)

        start_time = time.time()
        result = pipe(
            image=processed_image,
            prompt=prompt,
            num_inference_steps=int(steps)
        ).images[0]

        print(f"🔄 Resim düzenlemeleri {steps} Adımda uygulanacak...")

        elapsed = time.time() - start_time

        print(f"✅ Resim Başarıyla düzenlendi! ({elapsed:.2f} saniye)")

        # Otomatik kaydet
        save_status = save_image_locally(result)
        print(save_status)  # log için

        return result, f"✅ Başarıyla düzenlendi! ({elapsed:.2f} saniye)\n{save_status}"

    except Exception as e:
        return None, f"❌ Hata oluştu: {str(e)}"
    
def save_image_locally(image_input):
    if image_input is None:
        return "❌ Kaydedilecek görsel yok!"

    try:
        # Dönüştür: numpy -> PIL
        if isinstance(image_input, np.ndarray):
            image_pil = Image.fromarray(np.clip(image_input, 0, 255).astype(np.uint8))
        elif isinstance(image_input, Image.Image):
            image_pil = image_input
        else:
            return "❌ Desteklenmeyen görsel formatı."

        # RGB'ye dön
        if image_pil.mode != "RGB":
            image_pil = image_pil.convert("RGB")
            

        # Dosya adı
        timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
        output_dir = "outputs"
        os.makedirs(output_dir, exist_ok=True)
        output_path = os.path.join(output_dir, f"qwie_{timestamp}.png")
        

        # Kaydet
        image_pil.save(output_path)
        abs_path = os.path.abspath(output_path)

        return f"✅ Kaydedildi: {abs_path}"
    except Exception as e:
        return f"❌ Hata: {str(e)}"
    
# ========================================================
# GRADIO ARAYÜZ
# ========================================================

with gr.Blocks(title="🎨 Qwen-Image Edit - Yerel Uygulama") as demo:
    gr.Markdown("""
    # 🎨 Qwen-Image Edit ile Görsel Düzenleme
    Görsellerinizi metinle düzenleyin! (Örn: 'Kazak kırmızı çizgili olsun')
    """)

    with gr.Row():
        with gr.Column():
            input_image = gr.Image(type="pil", label="Girdi Görseli", elem_id="input_img")
            prompt = gr.Textbox(label="Prompt (Açıklama)", placeholder="Kazak kırmızı çizgili olsun", value="change the color of clothes to pink")
            
            btn = gr.Button("🎨 Düzenlemeyi Uygula", variant="primary")

        with gr.Column():
            output_image = gr.Image(label="Çıktı Görseli", elem_id="output_img")
            status = gr.Textbox(label="Durum", value="Hazır")

    btn.click(
        fn=edit_image,
        inputs=[input_image, prompt],
        outputs=[output_image, status]
    )

    gr.Markdown("""
    <br>
    <small>
    Model: <a href="https://huggingface.co/Qwen/Qwen-Image-Edit" target="_blank">Qwen/Qwen-Image-Edit</a> |
    LoRA: <a href="https://huggingface.co/lightx2v/Qwen-Image-Lightning" target="_blank">lightx2v/Qwen-Image-Lightning</a><br>
    Not: İlk çalışma biraz uzun sürer (model yüklenir). Sonraki çalıştırmalar hızlı olur. <br> Ortalama bir resmin düzenlenip oluşturulması 1 dakika 30 saniye civarı sürer.
    </small>
    """)

# ========================================================
# UYGULAMAYI BAŞLAT
# ========================================================

if __name__ == "__main__":
    demo.launch(
        server_name="127.0.0.1",
        server_port=7860,
        share=False,  # True yaparsan dışarıdan erişilebilir link verir
        inbrowser=True  # Otomatik tarayıcıyı açar
    )

SAMPLE LOGS :::

============================================================.
🔄 Model yükleniyor... Bu biraz zaman alabilir, ortalama 2 dakika 30 saniye.
------------------------------------------------------------------------------------------------.
1/6 - Görsel transformer yükleniyor...
Fetching 9 files: 100%|██████████████████████████████████████████████████████| 9/9 [00:00<?, ?it/s]
Loading checkpoint shards: 100%|█████████████████████████████████████| 9/9 [00:56<00:00,  6.23s/it]
2/6 - Metin encoder yükleniyor...
Loading checkpoint shards: 100%|█████████████████████████████████████| 4/4 [00:58<00:00, 14.53s/it]
3/6 - Pipeline oluşturuluyor...
Loading pipeline components...: 100%|████████████████████████████████| 6/6 [00:01<00:00,  3.78it/s]
4/6 - LoRA ağırlıkları yükleniyor...
5/6 - CPU offload etkinleştiriliyor...
6/6 - Inference yükleniyor...
============================================================.
✅ Model başarıyla yüklendi ve hazır! Bu işlemler (144.80 saniye sürdü.
============================================================.

* Running on local URL:  http://127.0.0.1:7860
* To create a public link, set `share=True` in `launch()`.

============================================================.
🔍 Resim boyutu kontrol ediliyor: 1088x1440 = 1,566,720 pixel
📏 Resim ölçeklendiriliyor: 1088x1440 -> 888x1176
📊 Ölçeklendirme oranı: 0.818
✅ Ölçeklendirme tamamlandı: 1,044,288 pixel
100%|████████████████████████████████████████████████████████████████| 8/8 [01:04<00:00,  8.10s/it]
🔄 Resim düzenlemeleri 8 Adımda uygulanacak...
✅ Resim Başarıyla düzenlendi! (96.51 saniye)
✅ Kaydedildi: E:\ai\aya\q-img-edt\outputs\qwie_2025-08-26_21-18-30.png
🔍 Resim boyutu kontrol ediliyor: 2880x3840 = 11,059,200 pixel
📏 Resim ölçeklendiriliyor: 2880x3840 -> 884x1180
📊 Ölçeklendirme oranı: 0.308
✅ Ölçeklendirme tamamlandı: 1,043,120 pixel
100%|████████████████████████████████████████████████████████████████| 8/8 [01:04<00:00,  8.08s/it]
🔄 Resim düzenlemeleri 8 Adımda uygulanacak...
✅ Resim Başarıyla düzenlendi! (100.50 saniye)
✅ Kaydedildi: E:\ai\aya\q-img-edt\outputs\qwie_2025-08-26_21-22-43.png
🔍 Resim boyutu kontrol ediliyor: 819x1024 = 838,656 pixel
✅ Resim boyutu uygun, ölçeklendirme gerekli değil.
100%|████████████████████████████████████████████████████████████████| 8/8 [01:04<00:00,  8.08s/it]
🔄 Resim düzenlemeleri 8 Adımda uygulanacak...
✅ Resim Başarıyla düzenlendi! (94.06 saniye)
✅ Kaydedildi: E:\ai\aya\q-img-edt\outputs\qwie_2025-08-26_21-27-59.png

Impossibile farlo girare su un Notebook -MSI VECTOR 16 HXAIA2XWIG-040IT - 64 GB RAM - Nvidia 5080 con 16GB VRam ?
Grazie per le cortesi risposte

Sign up or log in to comment