Tutorial Stable Diffusion: Generative AI untuk Pembuatan Gambar

# Stable Diffusion: Tutorial Komprehensif ## Daftar Isi 1. [Pendahuluan](#pendahuluan) 2. [Prasyarat](#prasyarat) 3. [Memahami Arsitektur Stable Diffusion](#memahami-arsitektur-stable-diffusion) 4....

By Ruby Abdullah · · tutorial
Stable DiffusionGenerative AIDiffusersControlNetDreamBoothImage Generation

Stable Diffusion: Tutorial Komprehensif

Daftar Isi

  • Pendahuluan
  • Prasyarat
  • Memahami Arsitektur Stable Diffusion
  • Generasi Gambar dari Teks
  • Teknik Prompt Engineering untuk Gambar
  • Generasi Image-to-Image
  • Inpainting
  • ControlNet untuk Generasi Terkontrol
  • Fine-tuning dengan DreamBooth
  • Strategi Deployment
  • Praktik Terbaik
  • Kesimpulan

  • Pendahuluan

    Stable Diffusion adalah model difusi laten yang menghasilkan gambar berkualitas tinggi dari deskripsi teks. Berbeda dengan model difusi sebelumnya yang beroperasi langsung di ruang piksel, Stable Diffusion bekerja di ruang laten yang terkompresi, sehingga jauh lebih cepat dan hemat memori. Tutorial ini membahas segala hal mulai dari generasi teks-ke-gambar dasar hingga teknik lanjutan seperti ControlNet dan fine-tuning DreamBooth.

    Library Diffusers dari Hugging Face menyediakan antarmuka yang paling mudah untuk bekerja dengan model Stable Diffusion, dan tutorial ini menggunakannya secara ekstensif.


    Prasyarat

    # Instal paket yang diperlukan
    

    pip install diffusers transformers accelerate torch torchvision

    pip install safetensors xformers

    pip install Pillow numpy

    pip install peft # Untuk fine-tuning dengan LoRA/DreamBooth

    Persyaratan sistem:
    • Python 3.9 atau lebih tinggi
    • GPU NVIDIA dengan minimal 8 GB VRAM (16 GB direkomendasikan untuk fine-tuning)
    • CUDA 11.8 atau lebih tinggi
    • Minimal 16 GB RAM sistem

    Verifikasi setup Anda:

    import torch
    

    import diffusers

    print(f"Versi PyTorch: {torch.version}")

    print(f"CUDA tersedia: {torch.cuda.isavailable()}")

    print(f"GPU: {torch.cuda.getdevicename(0) if torch.cuda.isavailable() else 'Tidak ada'}")

    print(f"Versi Diffusers: {diffusers.version}")


    Memahami Arsitektur Stable Diffusion

    Stable Diffusion terdiri dari tiga komponen utama:

  • VAE (Variational Autoencoder): Mengkodekan gambar ke ruang laten dan mendekodekan laten kembali menjadi gambar. Ruang laten berukuran 8x lebih kecil dari ruang piksel, yang memungkinkan pemrosesan lebih efisien.
  • U-Net: Jaringan denoising yang secara iteratif menghilangkan noise dari representasi laten, dikondisikan oleh embedding teks. U-Net inilah yang menjadi "otak" dari proses generasi.
  • Text Encoder (CLIP): Mengubah prompt teks menjadi embedding yang memandu U-Net selama proses denoising. CLIP memahami hubungan semantik antara teks dan gambar.
  • Proses generasi bekerja sebagai berikut: dimulai dengan noise acak di ruang laten, kemudian secara iteratif menghilangkan noise menggunakan U-Net, dipandu oleh conditioning teks dari CLIP.


    Generasi Gambar dari Teks

    Generasi Dasar

    from diffusers import StableDiffusionPipeline, DPMSolverMultistepScheduler
    

    import torch

    def buatpipeline(modelid="stabilityai/stable-diffusion-2-1"):

    """Inisialisasi pipeline Stable Diffusion."""

    pipe = StableDiffusionPipeline.frompretrained(

    modelid,

    torchdtype=torch.float16,

    safetychecker=None,

    )

    pipe.scheduler = DPMSolverMultistepScheduler.fromconfig(pipe.scheduler.config)

    pipe = pipe.to("cuda")

    # Aktifkan optimasi memori

    pipe.enableattentionslicing()

    return pipe

    pipe = buatpipeline()

    Hasilkan gambar

    prompt = "Pemandangan gunung yang tenang saat matahari terbenam, fotorealistis, resolusi 8k"

    negativeprompt = "buram, kualitas rendah, terdistorsi, cacat"

    gambar = pipe(

    prompt=prompt,

    negativeprompt=negativeprompt,

    numinferencesteps=30,

    guidancescale=7.5,

    width=768,

    height=768,

    ).images[0]

    gambar.save("gunungsunset.png")

    Generasi Batch dengan Seed Berbeda

    import torch
    
    

    def hasilkanvariasi(pipe, prompt, negativeprompt="", jumlahgambar=4, seedawal=42):

    """Menghasilkan beberapa variasi dari prompt yang sama dengan seed berbeda."""

    gambarlist = []

    for i in range(jumlahgambar):

    generator = torch.Generator(device="cuda").manualseed(seedawal + i)

    gambar = pipe(

    prompt=prompt,

    negativeprompt=negativeprompt,

    numinferencesteps=30,

    guidancescale=7.5,

    generator=generator,

    ).images[0]

    gambarlist.append(gambar)

    gambar.save(f"variasi{seedawal + i}.png")

    return gambarlist

    gambarlist = hasilkanvariasi(

    pipe,

    prompt="Kota futuristik dengan mobil terbang, gaya cyberpunk",

    negativeprompt="jelek, buram, kualitas rendah",

    jumlahgambar=4

    )

    Menggunakan SDXL untuk Kualitas Lebih Tinggi

    from diffusers import StableDiffusionXLPipeline, StableDiffusionXLRefinerPipeline
    
    

    def buatpipelinesdxl():

    """Membuat pipeline SDXL dengan refiner opsional."""

    base = StableDiffusionXLPipeline.frompretrained(

    "stabilityai/stable-diffusion-xl-base-1.0",

    torchdtype=torch.float16,

    variant="fp16",

    usesafetensors=True,

    ).to("cuda")

    refiner = StableDiffusionXLRefinerPipeline.frompretrained(

    "stabilityai/stable-diffusion-xl-refiner-1.0",

    torchdtype=torch.float16,

    variant="fp16",

    usesafetensors=True,

    ).to("cuda")

    return base, refiner

    def hasilkandenganrefiner(base, refiner, prompt, negativeprompt=""):

    """Generasi dua tahap: model base + refiner untuk kualitas optimal."""

    # Generasi base (jalankan untuk 80% langkah)

    gambar = base(

    prompt=prompt,

    negativeprompt=negativeprompt,

    numinferencesteps=40,

    denoisingend=0.8,

    outputtype="latent",

    ).images

    # Refiner (jalankan untuk 20% langkah sisanya)

    hasilrefined = refiner(

    prompt=prompt,

    negativeprompt=negativeprompt,

    numinferencesteps=40,

    denoisingstart=0.8,

    image=gambar,

    ).images[0]

    return hasilrefined


    Teknik Prompt Engineering untuk Gambar

    Prompt yang efektif sangat penting untuk menghasilkan gambar berkualitas tinggi. Berikut adalah strategi-strategi kunci yang perlu dikuasai:

    Struktur Prompt

    # Template: [Subjek] [Gaya] [Detail] [Modifier kualitas]
    
    

    prompts = {

    "potret": (

    "Potret seorang wanita muda dengan rambut cokelat kemerahan, "

    "pencahayaan alami yang lembut, depth of field dangkal, "

    "Canon EOS R5, lensa 85mm, f/1.4, "

    "fotorealistis, sangat detail, 8k"

    ),

    "lanskap": (

    "Padang rumput alpine yang luas dengan bunga liar di latar depan, "

    "gunung bersalju di latar belakang, pencahayaan golden hour, "

    "awan dramatis, fotografi lanskap, "

    "gaya National Geographic, sangat tajam, 4k"

    ),

    "conceptart": (

    "Kuil kuno yang melayang di atas awan, "

    "ukiran batu yang rumit, atmosfer mistis, "

    "pencahayaan volumetrik, concept art, "

    "trending di ArtStation, lukisan digital"

    ),

    }

    Template negative prompt

    negativeprompts = {

    "umum": (

    "jelek, cacat, berisik, buram, kontras rendah, "

    "watermark, teks, tanda tangan, keluar bingkai, "

    "kualitas terburuk, kualitas rendah, artefak jpeg"

    ),

    "potret": (

    "jelek, cacat, berisik, buram, anatomi buruk, "

    "anggota tubuh tambahan, jari tambahan, tangan yang digambar buruk, "

    "wajah yang digambar buruk, mutasi, terdistorsi"

    ),

    }

    Penekanan pada Prompt (Prompt Weighting)

    from diffusers import StableDiffusionPipeline
    

    from compel import Compel

    pipe = StableDiffusionPipeline.frompretrained(

    "stabilityai/stable-diffusion-2-1",

    torchdtype=torch.float16

    ).to("cuda")

    compel = Compel(tokenizer=pipe.tokenizer, textencoder=pipe.textencoder)

    Gunakan ++ untuk penekanan lebih kuat, -- untuk lebih lemah

    prompt = "kucing merah++ duduk di kursi biru--, fotorealistis"

    conditioning = compel(prompt)

    gambar = pipe(

    promptembeds=conditioning,

    numinferencesteps=30,

    ).images[0]


    Generasi Image-to-Image

    Transformasi gambar yang sudah ada menggunakan panduan teks:

    from diffusers import StableDiffusionImg2ImgPipeline
    

    from PIL import Image

    def transformasiimg2img(inputpath, prompt, strength=0.75):

    """

    Mentransformasi gambar yang ada berdasarkan prompt teks.

    strength: Seberapa banyak transformasi (0.0 = tanpa perubahan, 1.0 = regenerasi penuh)

    """

    pipe = StableDiffusionImg2ImgPipeline.frompretrained(

    "stabilityai/stable-diffusion-2-1",

    torchdtype=torch.float16,

    ).to("cuda")

    gambarawal = Image.open(inputpath).convert("RGB")

    gambarawal = gambarawal.resize((768, 768))

    hasil = pipe(

    prompt=prompt,

    image=gambarawal,

    strength=strength,

    guidancescale=7.5,

    numinferencesteps=30,

    ).images[0]

    return hasil

    Contoh: Ubah sketsa menjadi lukisan

    hasil = transformasiimg2img(

    "sketsa.png",

    prompt="Lukisan minyak hutan yang indah, mahakarya, sapuan kuas detail",

    strength=0.6

    )

    hasil.save("lukisandarisketsa.png")


    Inpainting

    Mengganti atau memodifikasi area tertentu dari sebuah gambar:

    from diffusers import StableDiffusionInpaintPipeline
    

    from PIL import Image

    import numpy as np

    def inpaintarea(imagepath, maskpath, prompt):

    """

    Inpaint area yang di-mask pada sebuah gambar.

    Piksel putih pada mask menandakan area yang akan diregenerasi.

    """

    pipe = StableDiffusionInpaintPipeline.frompretrained(

    "stabilityai/stable-diffusion-2-inpainting",

    torchdtype=torch.float16,

    ).to("cuda")

    gambar = Image.open(imagepath).convert("RGB").resize((512, 512))

    mask = Image.open(maskpath).convert("RGB").resize((512, 512))

    hasil = pipe(

    prompt=prompt,

    image=gambar,

    maskimage=mask,

    guidancescale=7.5,

    numinferencesteps=30,

    ).images[0]

    return hasil

    def buatmaskprogramatis(ukurangambar, arealist):

    """Membuat mask inpainting dari area persegi panjang."""

    mask = np.zeros((ukurangambar, 3), dtype=np.uint8)

    for x1, y1, x2, y2 in arealist:

    mask[y1:y2, x1:x2] = 255

    return Image.fromarray(mask)

    Contoh penggunaan

    mask = buatmaskprogramatis((512, 512), [(100, 100, 300, 300)])

    mask.save("mask.png")

    hasil = inpaintarea("foto.png", "mask.png", "Anjing golden retriever duduk di sini")

    hasil.save("hasilinpaint.png")


    ControlNet untuk Generasi Terkontrol

    ControlNet menambahkan conditioning spasial ke Stable Diffusion, memungkinkan kontrol yang presisi terhadap struktur output yang dihasilkan.

    from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
    

    import cv2

    import numpy as np

    from PIL import Image

    def buatpipelinecannycontrolnet():

    """Membuat pipeline ControlNet yang dikondisikan oleh tepi Canny."""

    controlnet = ControlNetModel.frompretrained(

    "lllyasviel/sd-controlnet-canny",

    torchdtype=torch.float16,

    )

    pipe = StableDiffusionControlNetPipeline.frompretrained(

    "runwayml/stable-diffusion-v1-5",

    controlnet=controlnet,

    torchdtype=torch.float16,

    ).to("cuda")

    pipe.enableattentionslicing()

    return pipe

    def hasilkandaritepi(pipe, imagepath, prompt):

    """Menghasilkan gambar yang dipandu oleh tepi Canny dari input."""

    # Ekstrak tepi Canny

    gambar = cv2.imread(imagepath)

    tepi = cv2.Canny(gambar, 100, 200)

    tepi = np.stack([tepi] 3, axis=-1)

    gambarkontrol = Image.fromarray(tepi)

    hasil = pipe(

    prompt=prompt,

    image=gambarkontrol,

    numinferencesteps=30,

    guidancescale=7.5,

    ).images[0]

    return hasil

    Penggunaan

    pipe = buatpipelinecannycontrolnet()

    hasil = hasilkandaritepi(

    pipe,

    "arsitektur.jpg",

    "Rumah modern yang indah, fotorealistis, pencahayaan golden hour"

    )

    hasil.save("outputterkontrol.png")

    Menggunakan Beberapa ControlNet Sekaligus

    from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
    
    

    def pipelinemulticontrolnet():

    """Menggabungkan ControlNet tepi Canny dan kedalaman."""

    controlnets = [

    ControlNetModel.frompretrained(

    "lllyasviel/sd-controlnet-canny", torchdtype=torch.float16

    ),

    ControlNetModel.frompretrained(

    "lllyasviel/sd-controlnet-depth", torchdtype=torch.float16

    ),

    ]

    pipe = StableDiffusionControlNetPipeline.frompretrained(

    "runwayml/stable-diffusion-v1-5",

    controlnet=controlnets,

    torchdtype=torch.float16,

    ).to("cuda")

    return pipe


    Fine-tuning dengan DreamBooth

    DreamBooth memungkinkan Anda mempersonalisasi Stable Diffusion dengan mengajarkan konsep baru hanya dari beberapa gambar. Ini sangat berguna untuk membuat model yang mengenali subjek spesifik.

    # Skrip pelatihan DreamBooth (jalankan dari command line)
    

    """

    accelerate launch traindreambooth.py \

    --pretrainedmodelnameorpath="stabilityai/stable-diffusion-2-1" \

    --instancedatadir="./fotoanjingsaya" \

    --outputdir="./modeldreambooth" \

    --instanceprompt="foto dari anjing sks" \

    --resolution=512 \

    --trainbatchsize=1 \

    --gradientaccumulationsteps=1 \

    --learningrate=5e-6 \

    --lrscheduler="constant" \

    --lrwarmupsteps=0 \

    --maxtrainsteps=800 \

    --use8bitadam \

    --mixedprecision="fp16"

    """

    Menggunakan DreamBooth dengan LoRA untuk fine-tuning yang efisien

    """

    accelerate launch traindreamboothlora.py \

    --pretrainedmodelnameorpath="stabilityai/stable-diffusion-2-1" \

    --instancedatadir="./fotoanjingsaya" \

    --outputdir="./dreamboothlora" \

    --instanceprompt="foto dari anjing sks" \

    --resolution=512 \

    --trainbatchsize=1 \

    --gradientaccumulationsteps=1 \

    --learningrate=1e-4 \

    --lrscheduler="constant" \

    --lrwarmupsteps=0 \

    --maxtrainsteps=500 \

    --rank=4

    """

    Memuat dan menggunakan model yang sudah di-fine-tune

    from diffusers import StableDiffusionPipeline

    def muatmodeldreambooth(modelpath):

    """Memuat model yang sudah di-fine-tune dengan DreamBooth."""

    pipe = StableDiffusionPipeline.frompretrained(

    modelpath,

    torchdtype=torch.float16,

    ).to("cuda")

    return pipe

    def muatmodellora(basemodelid, lorapath):

    """Memuat model yang sudah di-fine-tune dengan LoRA."""

    pipe = StableDiffusionPipeline.frompretrained(

    basemodelid,

    torchdtype=torch.float16,

    ).to("cuda")

    pipe.loadloraweights(lorapath)

    return pipe

    Hasilkan gambar dengan model fine-tuned

    pipe = muatmodellora(

    "stabilityai/stable-diffusion-2-1",

    "./dreamboothlora"

    )

    gambar = pipe("foto dari anjing sks memakai topi ulang tahun").images[0]

    gambar.save("anjingsayaultah.png")


    Strategi Deployment

    Server FastAPI

    from fastapi import FastAPI, HTTPException
    

    from pydantic import BaseModel

    from diffusers import StableDiffusionPipeline

    import torch

    import io

    import base64

    app = FastAPI()

    Muat model saat startup

    pipe = StableDiffusionPipeline.frompretrained(

    "stabilityai/stable-diffusion-2-1",

    torchdtype=torch.float16,

    ).to("cuda")

    class PermintaanGenerasi(BaseModel):

    prompt: str

    negativeprompt: str = ""

    numinferencesteps: int = 30

    guidancescale: float = 7.5

    width: int = 512

    height: int = 512

    seed: int = -1

    @app.post("/generate")

    async def hasilkangambar(request: PermintaanGenerasi):

    try:

    generator = None

    if request.seed >= 0:

    generator = torch.Generator(device="cuda").manualseed(request.seed)

    gambar = pipe(

    prompt=request.prompt,

    negativeprompt=request.negativeprompt,

    numinferencesteps=request.numinferencesteps,

    guidancescale=request.guidancescale,

    width=request.width,

    height=request.height,

    generator=generator,

    ).images[0]

    # Konversi ke base64

    buffer = io.BytesIO()

    gambar.save(buffer, format="PNG")

    imgstr = base64.b64encode(buffer.getvalue()).decode()

    return {"image": imgstr, "seed": request.seed}

    except Exception as e:

    raise HTTPException(status_code=500, detail=str(e))


    Praktik Terbaik

  • Gunakan torch.float16 untuk inferensi guna mengurangi penggunaan memori setengahnya dan meningkatkan kecepatan tanpa penurunan kualitas yang terlihat.
  • Aktifkan optimasi memori seperti attention slicing, VAE slicing, dan xformers saat menjalankan pada GPU konsumer.
  • Gunakan negative prompt secara konsisten. Negative prompt secara signifikan meningkatkan kualitas output dengan mengarahkan model menjauhi artefak umum.
  • Mulai dengan langkah yang lebih sedikit (20-30) saat eksperimen, lalu tingkatkan ke 40-50 untuk output berkualitas tinggi final.
  • Guidance scale antara 7 dan 12 bekerja paling baik untuk kebanyakan prompt. Nilai lebih rendah menghasilkan hasil yang lebih kreatif; nilai lebih tinggi mengikuti prompt lebih ketat tetapi bisa menghasilkan artefak.
  • Gunakan LoRA sebagai pengganti full DreamBooth saat fine-tuning pada hardware terbatas. Bobot LoRA hanya beberapa MB dibandingkan beberapa GB untuk checkpoint model penuh.
  • Selalu set seed untuk reprodusibilitas selama pengembangan dan pengujian.
  • Gunakan SDXL untuk output kualitas produksi. SDXL menghasilkan hasil yang jauh lebih baik daripada SD 1.5/2.1, terutama untuk rendering teks dan komposisi kompleks.

  • Kesimpulan

    Stable Diffusion adalah alat yang serbaguna dan powerful untuk generasi gambar, dengan aplikasi mulai dari pembuatan konten kreatif hingga desain industri dan prototyping. Tutorial ini membahas teknik-teknik inti: generasi dasar, prompt engineering, img2img, inpainting, ControlNet, dan fine-tuning DreamBooth. Dengan menggabungkan teknik-teknik ini dan mengikuti strategi deployment yang diuraikan, Anda dapat membangun sistem generasi gambar yang siap untuk produksi.

    Bidang ini berkembang dengan sangat cepat. Pantau terus arsitektur model yang lebih baru seperti Stable Diffusion 3, FLUX, dan mekanisme kontrol yang terus bermunculan untuk hasil yang lebih baik lagi.

    Artikel Terkait

    Tutorial ComfyUI: Workflow Berbasis Node untuk Stable Diffusion

    ComfyUI: Workflow Berbasis Node untuk Stable Diffusion ComfyUI adalah lingkungan grafis berbasis node untuk menjalankan ...

    fal.ai: Panduan Lengkap Serverless Inference untuk Generative Media

    fal.ai: Panduan Lengkap Serverless Inference untuk Generative Media (Image, Video, Audio) Halo temen-temen, ketemu lagi ...

    Tutorial Lengkap Azure OpenAI Service: GPT dan LLM di Azure

    Tutorial Lengkap Azure OpenAI Service: Enterprise AI dengan Model GPT Azure OpenAI Service menyediakan akses REST API ke...

    Tutorial Lengkap AWS Bedrock: Foundation Models di AWS

    Tutorial Lengkap AWS Bedrock: Managed Generative AI di AWS Amazon Bedrock adalah layanan terkelola penuh yang menyediaka...