DALL-E API ile Programatik Görsel Üretme

Görsel üretimini manuel olarak yapmak, özellikle içerik yoğun projelerde ciddi bir zaman kaybına dönüşebiliyor. Blog için kapak görseli üret, sosyal medya paylaşımı için banner hazırla, ürün kataloguna görsel ekle… Her birini tek tek Midjourney veya benzeri araçlara girmek yerine, bunu tamamen otomatikleştirmek mümkün. OpenAI’ın DALL-E API’si tam burada devreye giriyor ve doğru kullanıldığında ciddi bir verimlilik artışı sağlıyor.

DALL-E API’ye Genel Bakış

OpenAI, DALL-E modelini iki farklı versiyon olarak API üzerinden sunuyor. DALL-E 2 ve DALL-E 3 arasındaki farklar sadece görsel kalitesiyle sınırlı değil. DALL-E 3, prompt’u daha iyi anlıyor, metin içeren görseller üretebiliyor ve genel olarak çok daha tutarlı sonuçlar veriyor. Ancak fiyatlandırma açısından da fark var, bu yüzden hangi kullanım senaryosu için hangisini tercih edeceğini bilmek önemli.

API erişimi için önce OpenAI hesabı oluşturup API anahtarı almanız gerekiyor. Bunu zaten ChatGPT veya GPT-4 API’si için kullanıyorsanız, aynı anahtar DALL-E için de geçerli.

Başlamadan önce gerekli kütüphaneleri kuralım:

pip install openai requests pillow python-dotenv

Ortam değişkenlerini .env dosyasında tutmak en temiz yaklaşım:

# .env dosyası
OPENAI_API_KEY=sk-your-api-key-here
OUTPUT_DIR=/home/user/generated_images
DEFAULT_SIZE=1024x1024

İlk Görsel: Basit Bir Üretim Script’i

Herhangi bir otomasyona geçmeden önce temel akışı anlamak için sade bir script ile başlayalım. Bu script, verilen bir prompt’tan görsel üretip diske kaydediyor:

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

import os
import requests
from openai import OpenAI
from dotenv import load_dotenv
from datetime import datetime
import sys

load_dotenv()

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
output_dir = os.getenv("OUTPUT_DIR", "./output")
os.makedirs(output_dir, exist_ok=True)

def generate_image(prompt, size="1024x1024", quality="standard", model="dall-e-3"):
    """Verilen prompt ile görsel üretip diske kaydeder."""
    try:
        response = client.images.generate(
            model=model,
            prompt=prompt,
            size=size,
            quality=quality,
            n=1
        )
        
        image_url = response.data[0].url
        revised_prompt = response.data[0].revised_prompt
        
        print(f"[INFO] Revised prompt: {revised_prompt}")
        
        # Görseli indir
        img_response = requests.get(image_url, timeout=30)
        img_response.raise_for_status()
        
        # Zaman damgalı dosya adı oluştur
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = f"{output_dir}/image_{timestamp}.png"
        
        with open(filename, "wb") as f:
            f.write(img_response.content)
        
        print(f"[OK] Görsel kaydedildi: {filename}")
        return filename
        
    except Exception as e:
        print(f"[ERROR] Görsel üretilemedi: {e}")
        sys.exit(1)

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Kullanim: python3 generate_image.py 'prompt metni'")
        sys.exit(1)
    
    prompt = sys.argv[1]
    generate_image(prompt)

Script’i şu şekilde çalıştırırsınız:

python3 generate_image.py "A futuristic server room with blue neon lights, ultra realistic, 4K"

Burada dikkat etmek gereken önemli bir nokta var: DALL-E 3, gönderdiğiniz prompt’u kendi anladığı şekilde revize edebiliyor. revised_prompt alanı bu revizyonu gösteriyor. Üretilen görsel beklenmedik bir şekilde çıkıyorsa, bu alana bakmak sorunun kaynağını anlamamıza yardımcı oluyor.

Parametre Detayları

DALL-E API’nin parametrelerini doğru kullanmak hem kalite hem de maliyet açısından kritik. Önemli parametreleri ele alalım:

  • model: dall-e-2 veya dall-e-3 seçenekleri mevcut. Prodüksiyon için genellikle dall-e-3 tercih edilir.
  • prompt: Görsel açıklaması. DALL-E 3 ile 4000 karakter, DALL-E 2 ile 1000 karakter sınırı var.
  • size: Boyut seçeneği. DALL-E 3 için 1024x1024, 1792x1024 veya 1024x1792 destekleniyor. DALL-E 2 için 256x256, 512x512, 1024x1024 seçenekleri var.
  • quality: Sadece DALL-E 3’te mevcut. standard veya hd seçenekleri arasından hd, daha fazla detay ve tutarlılık sağlıyor ama maliyeti iki katına çıkarıyor.
  • style: DALL-E 3’e özel. vivid daha dramatik ve sinematik görseller, natural daha gerçekçi ve az abartılı görseller üretiyor.
  • n: Üretilecek görsel sayısı. DALL-E 3 ile en fazla 1, DALL-E 2 ile en fazla 10 görsel tek seferde üretilebiliyor.
  • response_format: url veya b64_json. URL’ler 60 dakika geçerli, bu yüzden üretim ortamında b64_json kullanmak daha güvenli.

Gerçek Dünya Senaryosu: Blog Kapak Görseli Otomasyonu

Bir teknoloji blogu yönettiğinizi düşünün. Her yazı için kapak görseli hazırlamanız gerekiyor. Bu süreci tamamen otomatikleştirmek için şöyle bir yapı kurabilirsiniz: Yazının başlığı ve kategori bilgisini girdi olarak alıp, uygun bir prompt oluşturun ve görseli üretin.

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

import os
import json
import hashlib
from openai import OpenAI
from dotenv import load_dotenv
import requests
from pathlib import Path

load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

PROMPT_TEMPLATES = {
    "linux": "Professional dark-themed Linux/Unix terminal interface, {topic}, minimalist tech aesthetic, purple and green colors, high quality",
    "network": "Network infrastructure visualization, {topic}, dark background with glowing connections, professional tech illustration",
    "security": "Cybersecurity concept art, {topic}, dark blue tones, shield and lock motifs, professional and clean",
    "cloud": "Cloud computing abstract concept, {topic}, blue and white color scheme, modern flat design style",
    "devops": "DevOps pipeline visualization, {topic}, dark theme, CI/CD workflow elements, modern tech aesthetic"
}

def generate_blog_cover(title, category, output_path=None):
    """Blog yazısı için kapak görseli üretir."""
    
    # Cache kontrolü - aynı başlık için tekrar üretme
    cache_key = hashlib.md5(f"{title}_{category}".encode()).hexdigest()[:8]
    if output_path is None:
        output_path = f"./covers/{cache_key}_{category}.png"
    
    if Path(output_path).exists():
        print(f"[CACHE] Görsel zaten mevcut: {output_path}")
        return output_path
    
    os.makedirs(os.path.dirname(output_path), exist_ok=True)
    
    # Kategori bazlı prompt oluştur
    template = PROMPT_TEMPLATES.get(category, PROMPT_TEMPLATES["linux"])
    prompt = template.format(topic=title)
    prompt += ", wide landscape format, suitable for blog header, no text in image"
    
    print(f"[INFO] Görsel üretiliyor: {title}")
    
    response = client.images.generate(
        model="dall-e-3",
        prompt=prompt,
        size="1792x1024",  # Blog kapağı için landscape format
        quality="standard",
        style="vivid",
        n=1,
        response_format="b64_json"  # URL yerine base64 - daha güvenli
    )
    
    import base64
    image_data = base64.b64decode(response.data[0].b64_json)
    
    with open(output_path, "wb") as f:
        f.write(image_data)
    
    # Metadata kaydet
    meta_path = output_path.replace(".png", "_meta.json")
    with open(meta_path, "w") as f:
        json.dump({
            "title": title,
            "category": category,
            "revised_prompt": response.data[0].revised_prompt,
            "generated_at": str(Path(output_path).stat().st_mtime)
        }, f, ensure_ascii=False, indent=2)
    
    print(f"[OK] Kapak görseli kaydedildi: {output_path}")
    return output_path


# Test
if __name__ == "__main__":
    articles = [
        ("Nginx ile Reverse Proxy Kurulumu", "linux"),
        ("Kubernetes Security Best Practices", "security"),
        ("Terraform ile AWS Altyapı Otomasyonu", "cloud")
    ]
    
    for title, category in articles:
        generate_blog_cover(title, category)

Bu script’te dikkat etmek gereken nokta, response_format="b64_json" kullanımı. URL formatında dönen görseller 60 dakika sonra geçersiz hale geliyor. Eğer asenkron bir pipeline kurduysanız ve görseli hemen indirmezseniz, URL expire olmuş olabilir. Base64 formatı bu riski ortadan kaldırıyor.

Görsel Düzenleme ve Varyasyon Üretme

DALL-E API sadece sıfırdan görsel üretmekle sınırlı değil. Var olan bir görsele ek yapabilir veya varyasyonlar oluşturabilirsiniz. Bu özellik özellikle A/B testleri veya sosyal medya içerik serisi üretirken işe yarıyor.

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

import os
from openai import OpenAI
from dotenv import load_dotenv
from PIL import Image
import io
import base64

load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

def prepare_image_for_api(image_path, target_size=(1024, 1024)):
    """Görseli API için hazırlar - boyut ve format düzenlemesi yapar."""
    with Image.open(image_path) as img:
        # RGBA'ya çevir (API PNG ve RGBA bekliyor)
        if img.mode != "RGBA":
            img = img.convert("RGBA")
        
        # Kare formata getir
        img = img.resize(target_size, Image.LANCZOS)
        
        buffer = io.BytesIO()
        img.save(buffer, format="PNG")
        buffer.seek(0)
        return buffer

def create_variations(source_image_path, count=3, output_dir="./variations"):
    """Kaynak görselden varyasyonlar üretir."""
    os.makedirs(output_dir, exist_ok=True)
    
    print(f"[INFO] {count} varyasyon üretiliyor: {source_image_path}")
    
    prepared_image = prepare_image_for_api(source_image_path)
    
    # DALL-E 2 varyasyon için gerekli (DALL-E 3 desteklemiyor)
    response = client.images.create_variation(
        image=prepared_image,
        n=count,
        size="1024x1024",
        response_format="b64_json"
    )
    
    generated_files = []
    for i, image_data in enumerate(response.data):
        output_path = f"{output_dir}/variation_{i+1}.png"
        img_bytes = base64.b64decode(image_data.b64_json)
        
        with open(output_path, "wb") as f:
            f.write(img_bytes)
        
        generated_files.append(output_path)
        print(f"[OK] Varyasyon {i+1} kaydedildi: {output_path}")
    
    return generated_files

def edit_image_with_mask(source_path, mask_path, edit_prompt, output_path="./edited_image.png"):
    """Mask kullanarak görselin belirli bölgesini düzenler."""
    source_img = prepare_image_for_api(source_path)
    mask_img = prepare_image_for_api(mask_path)
    
    response = client.images.edit(
        image=source_img,
        mask=mask_img,
        prompt=edit_prompt,
        n=1,
        size="1024x1024",
        response_format="b64_json"
    )
    
    img_bytes = base64.b64decode(response.data[0].b64_json)
    with open(output_path, "wb") as f:
        f.write(img_bytes)
    
    print(f"[OK] Düzenlenmiş görsel kaydedildi: {output_path}")
    return output_path

Toplu Görsel Üretimi ve Rate Limiting

Gerçek prodüksiyonda genellikle tek seferde çok sayıda görsel üretmeniz gerekiyor. Ancak OpenAI API’sinin rate limit politikası var ve bu limitlere takılmadan işlemi tamamlamak için doğru bir kuyruk mekanizması kurmanız şart.

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

import time
import csv
import asyncio
from openai import AsyncOpenAI
from dotenv import load_dotenv
import os
import base64
from pathlib import Path
import logging

load_dotenv()

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s"
)
log = logging.getLogger(__name__)

client = AsyncOpenAI(api_key=os.getenv("OPENAI_API_KEY"))

# Dakikada maksimum istek sayısı (tier'a göre ayarla)
RATE_LIMIT_RPM = 5
DELAY_BETWEEN_REQUESTS = 60 / RATE_LIMIT_RPM

async def generate_single(prompt, output_path, semaphore):
    """Tekil görsel üretimi, semaphore ile rate limiting."""
    async with semaphore:
        try:
            log.info(f"Üretiliyor: {output_path}")
            
            response = await client.images.generate(
                model="dall-e-3",
                prompt=prompt,
                size="1024x1024",
                quality="standard",
                n=1,
                response_format="b64_json"
            )
            
            img_bytes = base64.b64decode(response.data[0].b64_json)
            Path(output_path).parent.mkdir(parents=True, exist_ok=True)
            
            with open(output_path, "wb") as f:
                f.write(img_bytes)
            
            log.info(f"Tamamlandi: {output_path}")
            
            # Rate limiting için bekle
            await asyncio.sleep(DELAY_BETWEEN_REQUESTS)
            return True
            
        except Exception as e:
            log.error(f"Hata: {output_path} - {e}")
            return False

async def batch_generate_from_csv(csv_path, output_dir="./batch_output"):
    """CSV dosyasından toplu görsel üretimi yapar."""
    tasks_data = []
    
    with open(csv_path, "r", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        for row in reader:
            tasks_data.append({
                "prompt": row["prompt"],
                "filename": row.get("filename", f"image_{len(tasks_data)}.png")
            })
    
    log.info(f"Toplam {len(tasks_data)} görsel üretilecek")
    
    # Eş zamanlı maksimum 2 istek
    semaphore = asyncio.Semaphore(2)
    
    tasks = [
        generate_single(
            item["prompt"],
            f"{output_dir}/{item['filename']}",
            semaphore
        )
        for item in tasks_data
    ]
    
    results = await asyncio.gather(*tasks)
    
    success = sum(1 for r in results if r)
    fail = len(results) - success
    log.info(f"Tamamlandi: {success} basarili, {fail} basarisiz")

if __name__ == "__main__":
    asyncio.run(batch_generate_from_csv("./prompts.csv"))

CSV dosyası şu formatta olmalı:

# prompts.csv örneği
cat > prompts.csv << 'EOF'
prompt,filename
"Linux penguin mascot in cyberpunk style, dark neon background",linux_cover.png
"Docker whale logo concept, minimalist blue and white",docker_cover.png
"Kubernetes cluster visualization, network nodes glowing",k8s_cover.png
"Ansible automation robot, friendly design, orange color scheme",ansible_cover.png
EOF

Hata Yönetimi ve Retry Mekanizması

Prodüksiyonda görsel üretim script’lerinde hata yönetimi kritik önem taşıyor. API timeout’ları, geçici bağlantı sorunları veya içerik politikasına takılan promptlar için sağlam bir retry mekanizması kurmanız gerekiyor:

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

import time
import openai
from openai import OpenAI
from dotenv import load_dotenv
import os
import base64
import logging
from functools import wraps

load_dotenv()
log = logging.getLogger(__name__)
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

def retry_with_backoff(max_retries=3, base_delay=5, exceptions=(openai.RateLimitError, openai.APITimeoutError)):
    """Exponential backoff ile retry decorator."""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_retries):
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    if attempt == max_retries - 1:
                        raise
                    delay = base_delay * (2 ** attempt)
                    log.warning(f"Hata ({type(e).__name__}), {delay}s sonra tekrar denenecek. Deneme: {attempt+1}/{max_retries}")
                    time.sleep(delay)
                except openai.BadRequestError as e:
                    # Content policy ihlali - retry etme
                    log.error(f"Prompt reddedildi (content policy): {e}")
                    return None
        return wrapper
    return decorator

@retry_with_backoff(max_retries=3)
def safe_generate(prompt, output_path, size="1024x1024"):
    """Retry mekanizmalı güvenli görsel üretimi."""
    response = client.images.generate(
        model="dall-e-3",
        prompt=prompt,
        size=size,
        quality="standard",
        n=1,
        response_format="b64_json"
    )
    
    img_bytes = base64.b64decode(response.data[0].b64_json)
    
    os.makedirs(os.path.dirname(output_path) or ".", exist_ok=True)
    with open(output_path, "wb") as f:
        f.write(img_bytes)
    
    return output_path

Prompt Mühendisliği: Tutarlı Görseller Üretmek

DALL-E ile çalışırken prompt kalitesi doğrudan çıktı kalitesini belirliyor. Teknik konular için etkili prompt yapıları şunlar:

  • Stil belirleyiciler: “professional photograph”, “flat design illustration”, “isometric 3D render”, “minimalist vector art”
  • Teknik detaylar: “high resolution”, “sharp focus”, “ultra realistic”, “8K quality”
  • Kompozisyon: “wide angle”, “centered composition”, “dark background”, “rule of thirds”
  • Negatif yönlendirme: “no text”, “no watermark”, “no people” gibi istemediğiniz unsurları belirtmek
  • Renk paleti: Marka uyumu için “using only blue and white colors”, “dark theme with purple accents” gibi kısıtlamalar ekleyin
  • Bağlam: “suitable for blog header”, “professional presentation slide”, “social media post”

Sistem görselleri için genellikle şu yapı iyi sonuç veriyor: [Konu], [Stil], [Renk paleti], [Işık/Atmosfer], [Teknik kalite], [İstenmeyen unsurlar]

Sonuç

DALL-E API, doğru kullanıldığında görsel üretim iş yükünüzü büyük ölçüde otomatize edebiliyor. Temel generate çağrısından başlayıp, toplu üretim pipeline’ları, retry mekanizmaları ve kategori bazlı şablonlara kadar katmanlı bir yapı kurabilirsiniz.

Pratikte dikkat etmeniz gereken birkaç kritik nokta var: Rate limit politikalarını baştan öğrenin ve script’lerinizi buna göre yazın, b64_json formatını URL’ye tercih edin, prompt’larınızı şablonlaştırın böylece tutarlılık sağlarsınız, ve üretilen görselleri mutlaka metadata ile birlikte saklayın. Özellikle revised_prompt alanı, DALL-E’nin prompt’unuzu nasıl yorumladığını anlamak için son derece değerli bir bilgi.

Maliyeti optimize etmek için DALL-E 2’yi prototipleme ve test aşamasında, DALL-E 3’ü ise prodüksiyon görsellerinde kullanmak mantıklı bir strateji. Aynı prompt için DALL-E 2 yaklaşık 10 kat daha ucuz, bu yüzden prompt’larınızı geliştirme aşamasında önce DALL-E 2 ile test edin.

Bir yanıt yazın

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