ControlNet ile Karakter Pozisyonu Kontrol Etme

Stable Diffusion ile çalışırken en çok canımı sıkan şey, aynı karakteri farklı pozisyonlarda üretmeye çalışmaktı. “Şu kızı otururken çiz” diyorsun, sistem sana ayakta duran bir şey veriyor. Tekrar deniyorsun, bu sefer garip bir açıyla geliyor. ControlNet’i keşfedene kadar bu döngüde kaç saat harcadığımı sormayın.

ControlNet, Stable Diffusion’a iskelet ve poz bilgisi vermenin en güvenilir yolu. Ama doğru yapılandırılmadığında ya hiçbir şey değişmiyor ya da output tamamen mahvoluyor. Bu yazıda gerçekten işe yarayan konfigürasyonları, hangi modelin ne zaman kullanılacağını ve production benzeri pipeline’larda ControlNet’i nasıl entegre edeceğinizi paylaşacağım.

ControlNet Nedir ve Neden Gerekli

Temel Stable Diffusion bir prompt’tan görüntü üretir. Bu süreçte modelin “ne üreteyim” sorusuna verdiği cevap büyük ölçüde rastgeleliğe dayanır. Seed sabitleyebilirsiniz, prompt’u uzun uzun yazabilirsiniz ama karakterin kolunu tam istediğiniz açıya getirmek için onlarca deneme yapmanız gerekebilir.

ControlNet bu sorunu şöyle çözer: Görüntü üretim sürecine ek bir “koşul” katmanı ekler. Bu koşul bir iskelet haritası, kenar tespiti, derinlik haritası veya normal map olabilir. Model artık sadece “şunu üret” değil, “bunu üretirken şu yapıya uy” talimatını alıyor.

Poz kontrolü için en çok kullanılan format OpenPose‘dur. OpenPose, insan vücudunun 18 temel eklem noktasını (baş, boyun, omuzlar, dirsekler, bilekler, kalçalar, dizler, bilekler, gözler, kulaklar) tespit eder ve bu noktaları bir tel iskelet haritasına dönüştürür. Siz bu haritayı modele verdiğinizde, çıktı o iskelet yapısına uyar.

Kurulum

WebUI için ControlNet Eklentisi

AUTOMATIC1111 kullanıyorsanız, ControlNet eklentisini Extensions sekmesinden yükleyebilirsiniz. Ama manuel kurulum her zaman daha güvenilir:

cd /opt/stable-diffusion-webui/extensions
git clone https://github.com/Mikubill/sd-webui-controlnet.git
cd sd-webui-controlnet
pip install -r requirements.txt

Ardından ControlNet modellerini indirmeniz gerekiyor. Bu modeller ana Stable Diffusion checkpoint’inden bağımsız, ayrı dosyalar:

mkdir -p /opt/stable-diffusion-webui/models/ControlNet
cd /opt/stable-diffusion-webui/models/ControlNet

# OpenPose modeli - poz kontrolü için temel model
wget https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11p_sd15_openpose.pth

# Config dosyası da gerekli
wget https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11p_sd15_openpose.yaml

SD 1.5 yerine SDXL kullanıyorsanız, SDXL uyumlu ControlNet modellerine ihtiyacınız var:

# SDXL için OpenPose
wget https://huggingface.co/thibaud/controlnet-openpose-sdxl-1.0/resolve/main/OpenPoseXL2.safetensors

ComfyUI Kurulumu

ComfyUI tercih ediyorsanız, ControlNet node’ları zaten built-in geliyor. Modeli doğru dizine koymanız yeterli:

mkdir -p /opt/comfyui/models/controlnet
cd /opt/comfyui/models/controlnet

wget https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11p_sd15_openpose.pth

ComfyUI’de workflow JSON’ını komut satırından test etmek için:

python main.py --listen 0.0.0.0 --port 8188 --enable-cors-header

OpenPose Haritası Oluşturma

Poz kontrolünün kalbi burada. İki yol var: mevcut bir görselden poz çıkarmak ya da sıfırdan editor ile poz oluşturmak.

Mevcut Görselden Poz Çıkarma

Elinizdeki bir fotoğraftan iskelet çıkarmak için controlnet preprocessor’ı kullanabilirsiniz:

#!/usr/bin/env python3
# pose_extractor.py - Görselden OpenPose haritası çıkarır

import cv2
import numpy as np
from controlnet_aux import OpenposeDetector

def extract_pose(image_path: str, output_path: str) -> None:
    """
    Verilen görselden OpenPose iskelet haritası çıkarır.
    
    Args:
        image_path: Kaynak görsel yolu
        output_path: Çıktı harita yolu
    """
    detector = OpenposeDetector.from_pretrained("lllyasviel/Annotators")
    
    image = cv2.imread(image_path)
    image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    
    # Poz haritasını çıkar
    pose_map = detector(image_rgb, hand_and_face=True)
    
    # Kaydet
    pose_map.save(output_path)
    print(f"Poz haritası kaydedildi: {output_path}")

if __name__ == "__main__":
    import sys
    if len(sys.argv) != 3:
        print("Kullanim: python pose_extractor.py <kaynak_gorsel> <cikti_dosyasi>")
        sys.exit(1)
    
    extract_pose(sys.argv[1], sys.argv[2])

Bağımlılıkları kurmak için:

pip install controlnet-aux opencv-python pillow
python pose_extractor.py referans_foto.jpg poz_haritasi.png

API Üzerinden Toplu Poz Çıkarma

Birden fazla görselden toplu poz çıkarmanız gerekiyorsa, WebUI’nin API’sini kullanabilirsiniz:

#!/bin/bash
# batch_pose_extract.sh - Klasördeki tüm görsellerden poz çıkarır

INPUT_DIR="./input_images"
OUTPUT_DIR="./pose_maps"
API_URL="http://localhost:7860"

mkdir -p "$OUTPUT_DIR"

for img in "$INPUT_DIR"/*.{jpg,png,jpeg}; do
    [ -f "$img" ] || continue
    
    filename=$(basename "$img" | sed 's/.[^.]*$//')
    
    echo "İşleniyor: $img"
    
    # Base64 encode
    img_b64=$(base64 -w 0 "$img")
    
    # API isteği
    response=$(curl -s -X POST "$API_URL/controlnet/detect" 
        -H "Content-Type: application/json" 
        -d "{
            "controlnet_input_images": ["$img_b64"],
            "controlnet_module": "openpose_full",
            "controlnet_processor_res": 512
        }")
    
    # Sonucu kaydet
    echo "$response" | python3 -c "
import json, sys, base64
data = json.load(sys.stdin)
img_data = base64.b64decode(data['images'][0])
with open('$OUTPUT_DIR/${filename}_pose.png', 'wb') as f:
    f.write(img_data)
print('Kaydedildi: $OUTPUT_DIR/${filename}_pose.png')
"
done

echo "Tüm işlemler tamamlandı."

ControlNet Parametrelerini Anlamak

WebUI’de ControlNet sekmesini açtığınızda çok sayıda parametre görürsünüz. Her birinin ne işe yaradığını bilmeden iyi sonuç almak zor.

Temel Parametreler

Weight (Ağırlık): ControlNet’in üretim sürecine ne kadar müdahale ettiğini belirler. 0-2 arasında değer alır. 1.0 standart değer, daha yüksek değerler poz uyumunu artırır ama kaliteyi düşürebilir. Genellikle 0.8-1.2 arasında tutun.

Starting Control Step: Üretim sürecinin hangi adımından itibaren ControlNet devreye girsin. 0.0 başlangıçtan itibaren anlamına gelir. Detayları korumak için 0.0 kullanın.

Ending Control Step: ControlNet’in kaçıncı adımda etkisini kesmesi gerektiği. 0.8-1.0 arasında tutmak genellikle yeterli. 1.0 sonuna kadar aktif demek.

Preprocessor: Girdi görseline uygulanacak ön işlem. Zaten bir poz haritanız varsa “none” seçin. Ham görsel veriyorsanız “openpose_full” kullanın.

Model: Hangi ControlNet modelinin kullanılacağı. Preprocessor ile uyumlu olmalı.

Control Mode: Üç seçenek var:

  • Balanced: ControlNet ve prompt eşit ağırlıkta
  • My prompt is more important: Prompt öncelikli, poz ikincil
  • ControlNet is more important: Poz öncelikli, prompt ikincil

Karakter pozisyonu için çoğu durumda “ControlNet is more important” veya “Balanced” kullanın.

Resize Mode: Poz haritası ile çıktı boyutu farklıysa nasıl ölçeklensin:

  • Just Resize: Direkt ölçekle
  • Crop and Resize: Kırp ve ölçekle
  • Resize and Fill: Ölçekle ve doldur

API ile ControlNet Kullanımı

Otomatik pipeline’larda ControlNet’i kullanmak için API’yi Python ile yönetmek en esnek yol:

#!/usr/bin/env python3
# controlnet_generate.py

import requests
import base64
import json
from pathlib import Path


def load_image_as_b64(image_path: str) -> str:
    """Görsel dosyasını base64 stringine çevirir."""
    with open(image_path, "rb") as f:
        return base64.b64encode(f.read()).decode("utf-8")


def generate_with_pose(
    prompt: str,
    pose_image_path: str,
    output_path: str,
    negative_prompt: str = "bad anatomy, wrong anatomy, extra limb, missing limb",
    width: int = 512,
    height: int = 768,
    steps: int = 28,
    cfg_scale: float = 7.0,
    controlnet_weight: float = 1.0,
    api_url: str = "http://localhost:7860"
) -> None:
    """
    Poz haritası kullanarak karakter görüntüsü üretir.
    
    Args:
        prompt: Pozitif prompt
        pose_image_path: OpenPose harita dosya yolu
        output_path: Çıktı dosya yolu
        negative_prompt: Negatif prompt
        width: Çıktı genişliği
        height: Çıktı yüksekliği
        steps: Sampling adım sayısı
        cfg_scale: CFG ölçeği
        controlnet_weight: ControlNet ağırlığı
        api_url: WebUI API adresi
    """
    
    pose_b64 = load_image_as_b64(pose_image_path)
    
    payload = {
        "prompt": prompt,
        "negative_prompt": negative_prompt,
        "width": width,
        "height": height,
        "steps": steps,
        "cfg_scale": cfg_scale,
        "sampler_name": "DPM++ 2M Karras",
        "alwayson_scripts": {
            "controlnet": {
                "args": [
                    {
                        "enabled": True,
                        "image": pose_b64,
                        "module": "none",
                        "model": "control_v11p_sd15_openpose [cab727d4]",
                        "weight": controlnet_weight,
                        "starting_control_step": 0.0,
                        "ending_control_step": 1.0,
                        "control_mode": 0,
                        "resize_mode": 1,
                        "pixel_perfect": True
                    }
                ]
            }
        }
    }
    
    response = requests.post(
        f"{api_url}/sdapi/v1/txt2img",
        json=payload,
        timeout=120
    )
    
    if response.status_code != 200:
        raise RuntimeError(f"API hatası: {response.status_code} - {response.text}")
    
    result = response.json()
    image_data = base64.b64decode(result["images"][0])
    
    output_file = Path(output_path)
    output_file.parent.mkdir(parents=True, exist_ok=True)
    
    with open(output_file, "wb") as f:
        f.write(image_data)
    
    print(f"Görüntü üretildi: {output_path}")


if __name__ == "__main__":
    generate_with_pose(
        prompt="a young woman sitting on a wooden chair, casual clothing, natural lighting, photorealistic",
        pose_image_path="./pose_maps/sitting_pose.png",
        output_path="./output/character_sitting.png",
        controlnet_weight=1.0
    )

Gerçek Dünya Senaryosu: Ürün Kataloğu için Karakter Serileri

Diyelim ki bir e-ticaret projesi için aynı karakteri 10 farklı pozisyonda üretmeniz gerekiyor. Bunu elle tek tek yapmak yerine otomatize edin:

#!/usr/bin/env python3
# character_series.py - Karakter serisi üretimi

import json
from pathlib import Path
from controlnet_generate import generate_with_pose

# Poz konfigürasyonları
POSE_CONFIGS = [
    {
        "name": "standing_front",
        "pose_file": "poses/standing_front.png",
        "prompt_suffix": "standing facing forward, arms at sides"
    },
    {
        "name": "sitting_casual",
        "pose_file": "poses/sitting_chair.png",
        "prompt_suffix": "sitting on chair, relaxed posture, legs crossed"
    },
    {
        "name": "walking",
        "pose_file": "poses/walking.png",
        "prompt_suffix": "walking forward, natural stride"
    },
    {
        "name": "arms_raised",
        "pose_file": "poses/arms_up.png",
        "prompt_suffix": "arms raised above head, joyful expression"
    }
]

BASE_PROMPT = "professional model, white background, full body shot, studio lighting, high quality, 8k"
BASE_NEGATIVE = "bad anatomy, deformed, blurry, low quality, extra limbs, missing limbs, watermark"

OUTPUT_DIR = Path("./output/character_series")
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)

for config in POSE_CONFIGS:
    pose_path = Path(config["pose_file"])
    
    if not pose_path.exists():
        print(f"Uyarı: Poz dosyası bulunamadı: {pose_path}, atlanıyor.")
        continue
    
    full_prompt = f"{BASE_PROMPT}, {config['prompt_suffix']}"
    output_file = OUTPUT_DIR / f"{config['name']}.png"
    
    print(f"Üretiliyor: {config['name']}")
    
    try:
        generate_with_pose(
            prompt=full_prompt,
            pose_image_path=str(pose_path),
            output_path=str(output_file),
            negative_prompt=BASE_NEGATIVE,
            width=512,
            height=768,
            controlnet_weight=1.1
        )
    except Exception as e:
        print(f"Hata: {config['name']} üretilemedi: {e}")

print("Seri tamamlandı.")

Bu script’i cron ile çalıştırabilir ya da bir web hook’a bağlayabilirsiniz.

Sorun Giderme

Poz Uyumu Zayıf

Üretilen görüntü poz haritasına uymuyorsa:

  • ControlNet weight değerini 1.2-1.5’e çıkarın
  • Control Mode’u “ControlNet is more important” yapın
  • Preprocessor’ı “openpose_full” yerine “openpose” deneyin, bazen daha stabil sonuç verir
  • Poz haritasının çözünürlüğünün çıktıyla aynı aspect ratio’da olduğundan emin olun

Görüntü Kalitesi Düşüyor

Yüksek weight değerlerinde kalite düşüşü görülüyorsa:

  • Ending Control Step’i 0.8’e çekin, model son adımlarda serbest bıraksın
  • Starting Control Step’i 0.1-0.2 yapın, ilk birkaç adımda genel kompozisyon oluşsun
  • CFG Scale değerini biraz düşürün (7.0’dan 6.0’a)

“Model not found” Hatası

# Mevcut ControlNet modellerini listele
ls -la /opt/stable-diffusion-webui/models/ControlNet/

# Model hash'ini kontrol et - API'de model adı hash içermeli
curl -s http://localhost:7860/controlnet/model_list | python3 -m json.tool

API payload’ındaki model adını bu listeden aldığınız gerçek isimle eşleştirin.

Bellek Sorunları

8GB VRAM’de birden fazla ControlNet birleştirince OOM hatası alabilirsiniz:

# WebUI'yi düşük VRAM modunda başlat
python launch.py --medvram --opt-split-attention

# Veya çok düşük VRAM için
python launch.py --lowvram --opt-split-attention-v1

Birden Fazla ControlNet Kullanmak

Sadece poz değil, aynı anda derinlik haritası veya kenar tespiti de ekleyebilirsiniz. Bu, hem pozisyon hem de genel kompozisyonu kontrol etmenizi sağlar:

# Çoklu ControlNet payload örneği
"alwayson_scripts": {
    "controlnet": {
        "args": [
            {
                # Birinci: Poz kontrolü
                "enabled": True,
                "image": pose_map_b64,
                "module": "none",
                "model": "control_v11p_sd15_openpose [cab727d4]",
                "weight": 1.0,
                "starting_control_step": 0.0,
                "ending_control_step": 1.0
            },
            {
                # İkinci: Derinlik kontrolü
                "enabled": True,
                "image": depth_map_b64,
                "module": "none",
                "model": "control_v11f1p_sd15_depth [cfd03158]",
                "weight": 0.6,
                "starting_control_step": 0.0,
                "ending_control_step": 0.8
            }
        ]
    }
}

Burada dikkat edilecek nokta: iki ControlNet birlikte çalışırken weight değerlerinin toplamı genellikle 2.0’ı geçmemeli. Yoksa model çelişkili sinyallerle boğuşur ve çıktı bozulur.

Sonuç

ControlNet ile poz kontrolü, doğru parametreler ve net bir workflow olmadan gerçekten sinir bozucu olabiliyor. Ama bir kez çalışan bir pipeline kurduğunuzda, aynı karakteri onlarca farklı pozisyonda tutarlı biçimde üretmek mümkün hale geliyor.

Pratikte en çok işe yarayan yaklaşım şu: Referans fotoğraflardan poz haritalarını toplu çıkarın, bunları bir kütüphanede saklayın, ardından karakter üretimini API üzerinden script haline getirin. Böylece hem tekrar kullanılabilirlik hem de kalite tutarlılığı sağlanıyor.

Weight değerleri için başlangıç noktanız 1.0 olsun, control step aralığı için 0.0-1.0. Sorun çıktıkça bu değerleri ince ayar yapın. Her model, her checkpoint farklı davranabiliyor; bu yüzden kendi notlarınızı tutun ve hangi ayarların hangi durumda işe yaradığını kaydedin.

Son olarak, poz haritalarını sıfırdan çizmek yerine mevcut fotoğraflardan çıkarmak çok daha hızlı ve güvenilir sonuç veriyor. İnternetteki stok fotoğraflar bu iş için biçilmiş kaftan, referans kütüphanenizi onlarla oluşturmaya bakın.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir