Stable Diffusion คืออะไรและ Prompt ทำงานอย่างไร
Stable Diffusion เป็น AI Model สำหรับสร้างภาพจากข้อความ (Text-to-Image) พัฒนาโดย Stability AI เป็น Open Source ที่สามารถรันบนเครื่องส่วนตัวได้โดยไม่ต้องพึ่ง cloud service ทำให้ไม่มีค่าใช้จ่ายรายเดือนและข้อมูลไม่ถูกส่งไปยังเซิร์ฟเวอร์ภายนอก
Prompt คือข้อความที่ใช้บอก AI ว่าต้องการสร้างภาพแบบไหน Stable Diffusion ใช้ CLIP Text Encoder แปลง prompt เป็น vector แล้วใช้ U-Net Diffusion Model สร้างภาพจาก noise ทีละขั้นตอน คุณภาพของภาพขึ้นอยู่กับวิธีเขียน prompt เป็นหลัก
Stable Diffusion มีหลายเวอร์ชันได้แก่ SD 1.5 ที่เป็นเวอร์ชันพื้นฐานมี community models มากที่สุด, SDXL ที่สร้างภาพ 1024x1024 คุณภาพสูงกว่า, SD 3 ที่ปรับปรุง text rendering และ SD 3.5 ที่เป็นเวอร์ชันล่าสุดรองรับหลาย aspect ratio
เครื่องมือสำหรับใช้งาน Stable Diffusion ได้แก่ AUTOMATIC1111 WebUI ที่เป็น UI ยอดนิยมมีฟีเจอร์ครบ, ComfyUI ที่ใช้ node-based workflow เหมาะสำหรับ advanced users, Fooocus ที่ใช้งานง่ายเหมือน Midjourney และ InvokeAI ที่เน้น professional workflow
ติดตั้ง Stable Diffusion WebUI บน Local Machine
ติดตั้ง AUTOMATIC1111 Stable Diffusion WebUI บนเครื่องที่มี GPU NVIDIA
# ตรวจสอบ GPU
nvidia-smi
# ต้องมี VRAM อย่างน้อย 4GB สำหรับ SD 1.5
# แนะนำ 8GB+ สำหรับ SDXL
# ติดตั้ง Python 3.10
sudo apt install python3.10 python3.10-venv git wget -y
# Clone AUTOMATIC1111 WebUI
git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git
cd stable-diffusion-webui
# ดาวน์โหลด Model (SD 1.5)
wget -P models/Stable-diffusion/ \
"https://huggingface.co/runwayml/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.safetensors"
# ดาวน์โหลด SDXL Model
wget -P models/Stable-diffusion/ \
"https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/resolve/main/sd_xl_base_1.0.safetensors"
# รัน WebUI
./webui.sh --listen --api --xformers
# เข้าถึงที่ http://localhost:7860
# สำหรับ Windows
# git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git
# cd stable-diffusion-webui
# webui-user.bat
# เพิ่ม COMMANDLINE_ARGS=--listen --api --xformers ใน webui-user.bat
# สำหรับเครื่องที่มี VRAM น้อย (4GB)
./webui.sh --listen --api --medvram --opt-split-attention
# ติดตั้ง ComfyUI (ทางเลือก)
git clone https://github.com/comfyanonymous/ComfyUI.git
cd ComfyUI
pip install -r requirements.txt
python main.py --listen 0.0.0.0 --port 8188
เทคนิคเขียน Prompt ให้ได้ภาพคุณภาพสูง
โครงสร้าง Prompt ที่ดีควรประกอบด้วย Subject, Medium, Style, Details, Quality Modifiers และ Artist Reference
# โครงสร้าง Prompt
# [Subject] + [Medium] + [Style] + [Details] + [Quality] + [Lighting]
# ตัวอย่าง Prompt สำหรับภาพแนว Photorealistic
# Prompt:
# "a professional photograph of a Thai temple at sunset,
# golden hour lighting, warm tones, shot on Canon EOS R5,
# 85mm f/1.4, shallow depth of field, bokeh background,
# architectural photography, ultra detailed, 8k resolution"
# Negative Prompt:
# "blurry, low quality, deformed, ugly, oversaturated,
# watermark, text, logo, signature, cropped"
# ตัวอย่าง Prompt แนว Digital Art
# "epic fantasy landscape, floating islands above clouds,
# crystal waterfalls, bioluminescent plants, volumetric fog,
# dramatic lighting, concept art style, trending on artstation,
# highly detailed, cinematic composition, 4k"
# ตัวอย่าง Prompt แนว Anime
# "1girl, long silver hair, blue eyes, wearing white dress,
# standing in flower field, cherry blossoms falling,
# golden hour, soft lighting, masterpiece, best quality,
# detailed face, detailed eyes, anime style"
# Prompt Weight Syntax (AUTOMATIC1111)
# (keyword:1.5) = เพิ่มน้ำหนัก 1.5 เท่า
# (keyword:0.5) = ลดน้ำหนักเหลือ 0.5 เท่า
# [keyword] = ลดน้ำหนัก
# ((keyword)) = เพิ่มน้ำหนัก 1.21x (1.1^2)
# ตัวอย่างการใช้ Weight
# "(detailed landscape:1.3), (volumetric lighting:1.2),
# epic mountains, (crystal clear water:1.4), sunset,
# (photorealistic:1.5), 8k uhd, dslr"
# Prompt Scheduling (เปลี่ยน prompt ระหว่าง steps)
# [cat:dog:0.5] = ใช้ "cat" 50% แรก แล้วเปลี่ยนเป็น "dog"
# [sketch:oil painting:0.3] = sketch 30% แรก แล้วเปลี่ยนเป็น oil painting
Parameters ที่สำคัญสำหรับการสร้างภาพ
# Parameters ที่ต้องรู้
#
# Steps (Sampling Steps): 20-50
# - น้อยกว่า 20: ภาพยังไม่ converge
# - 20-30: เหมาะสำหรับส่วนใหญ่
# - 30-50: รายละเอียดมากขึ้นแต่ช้าลง
#
# CFG Scale (Classifier Free Guidance): 5-15
# - 1-5: อิสระมาก ไม่ค่อยตาม prompt
# - 7-9: สมดุลดี (แนะนำ)
# - 10-15: ตาม prompt มากแต่อาจเกิด artifacts
# - มากกว่า 15: oversaturated, artifacts
#
# Sampler:
# - Euler a: เร็ว, ดีสำหรับทดสอบ
# - DPM++ 2M Karras: คุณภาพดี, แนะนำ
# - DPM++ SDE Karras: รายละเอียดสูง, ช้ากว่า
# - DDIM: เสถียร, ดีสำหรับ img2img
#
# Resolution:
# - SD 1.5: 512x512, 512x768, 768x512
# - SDXL: 1024x1024, 1024x1536, 1536x1024
#
# Seed: ค่าสุ่มสำหรับ reproduce ภาพเดิม
# - -1: สุ่มทุกครั้ง
# - ใส่ตัวเลข: ได้ภาพเดิมถ้า prompt และ settings เหมือนกัน
#
# Batch Count/Size:
# - Count: จำนวนรอบที่สร้าง
# - Size: จำนวนภาพต่อรอบ (ใช้ VRAM มากขึ้น)
ใช้ Stable Diffusion API สร้างภาพแบบ Automated
AUTOMATIC1111 WebUI มี API ที่ใช้สร้างภาพแบบ programmatic ได้
#!/usr/bin/env python3
# sd_api_client.py — Stable Diffusion API Client
import requests
import base64
import json
import os
from datetime import datetime
SD_URL = os.getenv("SD_URL", "http://localhost:7860")
def txt2img(prompt, negative_prompt="", width=512, height=512, steps=25, cfg_scale=7, seed=-1, sampler="DPM++ 2M Karras"):
payload = {
"prompt": prompt,
"negative_prompt": negative_prompt,
"width": width,
"height": height,
"steps": steps,
"cfg_scale": cfg_scale,
"seed": seed,
"sampler_name": sampler,
"batch_size": 1,
"n_iter": 1,
"restore_faces": True,
"enable_hr": False,
}
r = requests.post(f"{SD_URL}/sdapi/v1/txt2img", json=payload)
if r.status_code != 200:
print(f"Error: {r.status_code} {r.text[:200]}")
return None
result = r.json()
images = result.get("images", [])
info = json.loads(result.get("info", "{}"))
print(f"Seed: {info.get('seed')}")
print(f"Sampler: {info.get('sampler_name')}")
# บันทึกภาพ
output_dir = "outputs"
os.makedirs(output_dir, exist_ok=True)
saved_files = []
for i, img_b64 in enumerate(images):
filename = f"{output_dir}/{datetime.now().strftime('%Y%m%d_%H%M%S')}_{i}.png"
with open(filename, "wb") as f:
f.write(base64.b64decode(img_b64))
saved_files.append(filename)
print(f"Saved: {filename}")
return saved_files
def img2img(init_image_path, prompt, denoising_strength=0.5, **kwargs):
with open(init_image_path, "rb") as f:
init_image_b64 = base64.b64encode(f.read()).decode()
payload = {
"init_images": [init_image_b64],
"prompt": prompt,
"denoising_strength": denoising_strength,
"width": kwargs.get("width", 512),
"height": kwargs.get("height", 512),
"steps": kwargs.get("steps", 25),
"cfg_scale": kwargs.get("cfg_scale", 7),
"sampler_name": kwargs.get("sampler", "DPM++ 2M Karras"),
}
r = requests.post(f"{SD_URL}/sdapi/v1/img2img", json=payload)
result = r.json()
for i, img_b64 in enumerate(result.get("images", [])):
filename = f"outputs/img2img_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{i}.png"
with open(filename, "wb") as f:
f.write(base64.b64decode(img_b64))
print(f"Saved: {filename}")
def get_models():
r = requests.get(f"{SD_URL}/sdapi/v1/sd-models")
for model in r.json():
print(f" {model['model_name']} ({model['hash'][:8]})")
def set_model(model_name):
payload = {"sd_model_checkpoint": model_name}
requests.post(f"{SD_URL}/sdapi/v1/options", json=payload)
print(f"Model set to: {model_name}")
if __name__ == "__main__":
print("Available models:")
get_models()
files = txt2img(
prompt="a beautiful mountain landscape at golden hour, photorealistic, 8k, detailed",
negative_prompt="blurry, low quality, watermark, text",
width=768, height=512, steps=30, cfg_scale=7
)
Negative Prompt และ Advanced Parameters
Negative Prompt บอก AI ว่าไม่ต้องการอะไรในภาพ ช่วยลดปัญหาที่พบบ่อย
# Negative Prompt Templates
# สำหรับภาพ Photorealistic
NEGATIVE_PHOTO = """
(worst quality:1.4), (low quality:1.4), (normal quality:1.4),
lowres, bad anatomy, bad hands, text, error, missing fingers,
extra digit, fewer digits, cropped, jpeg artifacts, signature,
watermark, username, blurry, artist name, deformed,
oversaturated, ugly, duplicate, morbid, mutilated,
out of frame, extra fingers, mutated hands, poorly drawn hands,
poorly drawn face, mutation, extra limbs, extra legs, extra arms,
disfigured, gross proportions, malformed limbs, missing arms,
missing legs, extra arms, extra legs, fused fingers,
too many fingers, long neck
"""
# สำหรับ Anime Style
NEGATIVE_ANIME = """
(worst quality:1.4), (low quality:1.4), lowres, bad anatomy,
bad hands, missing fingers, extra digit, fewer digits,
blurry, watermark, text, cropped, error,
normal quality, jpeg artifacts, signature, username,
poorly drawn, bad proportions, gross proportions,
deformed iris, deformed pupils, extra eyes,
bad face, ugly face, asymmetric face
"""
# Embedding สำหรับ Negative Prompt (ดาวน์โหลดแยก)
# EasyNegative: เอา artifact ออกอัตโนมัติ
# bad-hands-5: แก้ปัญหามือผิดรูป
# verybadimagenegative_v1.3: ลด low quality artifacts
# ใช้: "EasyNegative, bad-hands-5" ใน negative prompt
# Hires Fix Settings (สร้างภาพ high resolution)
# 1. สร้างภาพ base ที่ 512x512 ก่อน
# 2. Upscale ด้วย Hires Fix
# Upscaler: R-ESRGAN 4x+ หรือ 4x-UltraSharp
# Hires steps: 10-20
# Denoising strength: 0.3-0.5 (ค่าน้อย = เปลี่ยนน้อย)
# Upscale by: 2x (ได้ 1024x1024)
# ADetailer (After Detailer) — แก้ใบหน้าอัตโนมัติ
# ติดตั้ง Extension: https://github.com/Bing-su/adetailer
# ตั้งค่า:
# Model: face_yolov8n.pt
# Confidence: 0.3
# Mask blur: 4
# Denoising strength: 0.4
# Inpaint padding: 32
ControlNet และ img2img สำหรับควบคุมผลลัพธ์
ControlNet ช่วยควบคุมท่าทาง โครงสร้าง และ composition ของภาพที่สร้าง
# ติดตั้ง ControlNet Extension
# ใน WebUI: Extensions > Install from URL
# URL: https://github.com/Mikubill/sd-webui-controlnet
# ดาวน์โหลด ControlNet Models
cd stable-diffusion-webui/models/ControlNet/
# Canny (ตรวจจับขอบ)
wget "https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11p_sd15_canny.pth"
# OpenPose (ท่าทางคน)
wget "https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11p_sd15_openpose.pth"
# Depth (ความลึก)
wget "https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11f1p_sd15_depth.pth"
# ใช้ ControlNet ผ่าน API
python3 << 'PYEOF'
import requests
import base64
import json
SD_URL = "http://localhost:7860"
# อ่านภาพ reference
with open("reference_pose.png", "rb") as f:
ref_image = base64.b64encode(f.read()).decode()
payload = {
"prompt": "a woman in red dress, standing pose, professional photo, studio lighting",
"negative_prompt": "low quality, blurry, deformed",
"width": 512,
"height": 768,
"steps": 30,
"cfg_scale": 7,
"sampler_name": "DPM++ 2M Karras",
"alwayson_scripts": {
"controlnet": {
"args": [
{
"enabled": True,
"input_image": ref_image,
"module": "openpose",
"model": "control_v11p_sd15_openpose",
"weight": 1.0,
"resize_mode": "Crop and Resize",
"control_mode": "Balanced",
"pixel_perfect": True
}
]
}
}
}
r = requests.post(f"{SD_URL}/sdapi/v1/txt2img", json=payload)
result = r.json()
for i, img in enumerate(result.get("images", [])):
with open(f"controlnet_output_{i}.png", "wb") as f:
f.write(base64.b64decode(img))
print(f"Saved controlnet_output_{i}.png")
PYEOF
Batch Processing สำหรับสร้างภาพจำนวนมาก
#!/usr/bin/env python3
# batch_generate.py — สร้างภาพจำนวนมากด้วย Stable Diffusion API
import requests
import base64
import json
import os
import time
from concurrent.futures import ThreadPoolExecutor
SD_URL = "http://localhost:7860"
OUTPUT_DIR = "batch_outputs"
os.makedirs(OUTPUT_DIR, exist_ok=True)
PROMPTS = [
{"prompt": "sunset over ocean, dramatic clouds, golden light, landscape photography", "name": "sunset"},
{"prompt": "modern city skyline at night, neon lights, rain reflections, cyberpunk", "name": "city"},
{"prompt": "ancient forest with morning mist, sunbeams through trees, mystical", "name": "forest"},
{"prompt": "snow-capped mountain with alpine lake, reflection, clear sky", "name": "mountain"},
{"prompt": "cherry blossom garden in spring, pink petals falling, serene", "name": "sakura"},
]
NEGATIVE = "low quality, blurry, watermark, text, ugly, deformed"
def generate_image(prompt_config, index):
payload = {
"prompt": prompt_config["prompt"] + ", masterpiece, best quality, 8k uhd",
"negative_prompt": NEGATIVE,
"width": 768,
"height": 512,
"steps": 25,
"cfg_scale": 7,
"seed": -1,
"sampler_name": "DPM++ 2M Karras",
"batch_size": 4,
}
try:
r = requests.post(f"{SD_URL}/sdapi/v1/txt2img", json=payload, timeout=300)
result = r.json()
info = json.loads(result.get("info", "{}"))
for i, img_b64 in enumerate(result.get("images", [])):
filename = f"{OUTPUT_DIR}/{prompt_config['name']}_{index}_{i}_seed{info.get('all_seeds', [0])[i]}.png"
with open(filename, "wb") as f:
f.write(base64.b64decode(img_b64))
print(f"[OK] {filename}")
except Exception as e:
print(f"[ERROR] {prompt_config['name']}: {e}")
for idx, prompt in enumerate(PROMPTS):
print(f"\nGenerating: {prompt['name']} ({idx+1}/{len(PROMPTS)})")
generate_image(prompt, idx)
time.sleep(1)
print(f"\nDone! Generated images in {OUTPUT_DIR}/")
FAQ คำถามที่พบบ่อย
Q: GPU ขั้นต่ำสำหรับรัน Stable Diffusion คือเท่าไหร่?
A: สำหรับ SD 1.5 ต้องการ NVIDIA GPU ที่มี VRAM อย่างน้อย 4GB (เช่น GTX 1650) แต่แนะนำ 8GB ขึ้นไป (RTX 3060/4060) สำหรับ SDXL ต้องการ VRAM อย่างน้อย 8GB แนะนำ 12GB ขึ้นไป สำหรับ AMD GPU สามารถใช้ได้ผ่าน DirectML หรือ ROCm แต่ประสิทธิภาพต่ำกว่า NVIDIA
Q: Stable Diffusion กับ Midjourney ต่างกันอย่างไร?
A: Stable Diffusion เป็น Open Source รันบนเครื่องตัวเองได้ ปรับแต่งได้ทุกอย่างและไม่มีค่าใช้จ่ายรายเดือน ส่วน Midjourney ใช้ผ่าน Discord เท่านั้น คุณภาพภาพดีโดยไม่ต้อง tune มาก แต่มีค่าบริการรายเดือนและปรับแต่งได้น้อยกว่า สำหรับคนที่ต้องการ full control และ privacy แนะนำ Stable Diffusion
Q: LoRA และ Checkpoint Model ต่างกันอย่างไร?
A: Checkpoint Model เป็น full model ขนาด 2-7GB ที่ train มาจาก base model ทั้งหมด ส่วน LoRA (Low-Rank Adaptation) เป็น model ขนาดเล็ก 10-200MB ที่เพิ่มความสามารถเฉพาะด้านเช่น style หรือ character ให้กับ base model LoRA ใช้ VRAM น้อยกว่าและสามารถรวมหลาย LoRA พร้อมกันได้
Q: จะปรับปรุงคุณภาพมือและนิ้วที่ผิดรูปได้อย่างไร?
A: ใช้หลายวิธีร่วมกันคือ เพิ่ม negative prompt เกี่ยวกับมือ ใช้ ADetailer extension สำหรับแก้มืออัตโนมัติ ใช้ ControlNet OpenPose เพื่อกำหนดท่าทางมือ หรือใช้ Inpainting วาดมือใหม่เฉพาะส่วน model ใหม่ๆเช่น SDXL และ SD 3 แก้ปัญหามือได้ดีขึ้นมากเมื่อเทียบกับ SD 1.5
