Diffusers documentation

ControlNet

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v0.43.0).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

ControlNet

ControlNet は、テキストプロンプトを維持したまま、構造制御画像(エッジ、深度、ポーズなど)を用いて事前学習済み拡散モデルを制御します。ベースモデルを凍結し、デノイザーを導く残差を出力する並列ネットワークを追加することで、ファインチューニングなしにレイアウトの制御を実現します。

必要なコントロール(canny など)に対応する ControlNet を読み込み、from_pretrained() に controlnet= として渡します。controlnet_conditioning_scale を使用して、コントロールが生成をどの程度強く誘導するかを設定します。

ControlNetは、Flux、Hunyuan-DiT、Stable Diffusion 3など、多くのモデルで利用可能です。本ガイドの例では、FluxとStable Diffusion XLを使用しています。旧来のStable Diffusion系チェックポイントにおけるT2I-Adapterについては、レガシーアダプターを参照してください。新しい制御可能な生成タスクには、ControlNetの使用を推奨します。

以下のタブには、テキストから画像生成、画像から画像生成、インペインティング用のControlNetが表示されています。

テキストから画像
image-to-image
inpainting

opencv-pythonを使用してCannyエッジ画像を生成します。

import cv2
import numpy as np
from PIL import Image
from diffusers.utils import load_image

original_image = load_image(
    "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/non-enhanced-prompt.png"
)

image = np.array(original_image)

low_threshold = 100
high_threshold = 200

image = cv2.Canny(image, low_threshold, high_threshold)
image = image[:, :, None]
image = np.concatenate([image, image, image], axis=2)
canny_image = Image.fromarray(image)

Canny画像をパイプラインに渡します。controlnet_conditioning_scale パラメータを使用して、コントロールに割り当てる重みを決定します。

import torch
from diffusers.utils import load_image
from diffusers import FluxControlNetPipeline, FluxControlNetModel

controlnet = FluxControlNetModel.from_pretrained(
    "InstantX/FLUX.1-dev-Controlnet-Canny", dtype=torch.bfloat16
)
pipeline = FluxControlNetPipeline.from_pretrained(
    "black-forest-labs/FLUX.1-dev", controlnet=controlnet, dtype=torch.bfloat16
).to("cuda")  # or "mps", "xpu", "cpu"

prompt = """
A photorealistic overhead image of a cat reclining sideways in a flamingo pool floatie holding a margarita. 
The cat is floating leisurely in the pool and completely relaxed and happy.
"""

pipeline(
    prompt, 
    control_image=canny_image,
    controlnet_conditioning_scale=0.5,
    num_inference_steps=50, 
    guidance_scale=3.5,
).images[0]
Generated image (prompt only)
元の画像
Control image (Canny edges)
canny image
Generated image (ControlNet + prompt)
生成された画像

Multi-ControlNet

controlnet_conditioning_scale を個別に調整し、重複する領域にはマスクを適用できる場合は適用して、複数の ControlNet(例: canny + depth)をリストとして渡すことで合成します。canny_image は OpenCV の Canny(テキストから画像生成の場合と同様)、depth_image は DPT(画像から画像生成の場合と同様)で生成します。パイプラインを呼び出す前に、すべてのコントロール画像をパイプラインのサイズにリサイズしてください。

import cv2
import torch
import numpy as np
from PIL import Image
from transformers import DPTImageProcessor, DPTForDepthEstimation
from diffusers import StableDiffusionXLControlNetPipeline, ControlNetModel, AutoencoderKL
from diffusers.utils import load_image

original_image = load_image(
    "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/non-enhanced-prompt.png"
)
image = np.array(original_image)
canny_image = Image.fromarray(
    np.concatenate([cv2.Canny(image, 100, 200)[:, :, None]] * 3, axis=2)
)

device = "cuda"  # or "mps", "xpu", "cpu"
depth_estimator = DPTForDepthEstimation.from_pretrained("Intel/dpt-hybrid-midas").to(device)
feature_extractor = DPTImageProcessor.from_pretrained("Intel/dpt-hybrid-midas")

def get_depth_map(image):
    image = feature_extractor(images=image, return_tensors="pt").pixel_values.to(device)
    with torch.no_grad(), torch.autocast(device):
        depth_map = depth_estimator(image).predicted_depth

    depth_map = torch.nn.functional.interpolate(
        depth_map.unsqueeze(1),
        size=(1024, 1024),
        mode="bicubic",
        align_corners=False,
    )
    depth_min = torch.amin(depth_map, dim=[1, 2, 3], keepdim=True)
    depth_max = torch.amax(depth_map, dim=[1, 2, 3], keepdim=True)
    depth_map = (depth_map - depth_min) / (depth_max - depth_min)
    image = torch.cat([depth_map] * 3, dim=1)
    image = image.permute(0, 2, 3, 1).cpu().numpy()[0]
    image = Image.fromarray((image * 255.0).clip(0, 255).astype(np.uint8))
    return image

depth_image = get_depth_map(original_image.resize((1024, 1024)))

controlnets = [
    ControlNetModel.from_pretrained(
        "diffusers/controlnet-canny-sdxl-1.0", dtype=torch.float16
    ),
    ControlNetModel.from_pretrained(
        "diffusers/controlnet-depth-sdxl-1.0-small", dtype=torch.float16
    ),
]

vae = AutoencoderKL.from_pretrained("madebyollin/sdxl-vae-fp16-fix", dtype=torch.float16)
pipeline = StableDiffusionXLControlNetPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0", controlnet=controlnets, vae=vae, dtype=torch.float16
).to(device)

prompt = """
a relaxed rabbit sitting on a striped towel next to a pool with a tropical drink nearby, 
bright sunny day, vacation scene, 35mm photograph, film, professional, 4k, highly detailed
"""
negative_prompt = "lowres, bad anatomy, worst quality, low quality, deformed, ugly"

images = [canny_image.resize((1024, 1024)), depth_image.resize((1024, 1024))]

pipeline(
    prompt,
    negative_prompt=negative_prompt,
    image=images,
    num_inference_steps=100,
    controlnet_conditioning_scale=[0.5, 0.5],
).images[0]
Canny image
canny image
Depth map
深度マップ
Generated image (ControlNet + prompt)
生成された画像

推測モード

Guess modeは、テキストプロンプトがない場合(または不要な場合)に、コントロールのみから画像を生成します。ControlNetの残差はブロックの深さに応じてスケーリングされ、初期のダウンブロックでは0.1程度から、ミッドブロックでは1.0まで変化します。これにより、初期レイヤーが過度に制約をかけるのを防ぎつつ、深いブロックが構造を保持できるようにします。

import torch
from diffusers.utils import load_image
from diffusers import StableDiffusionXLControlNetPipeline, ControlNetModel

controlnet = ControlNetModel.from_pretrained(
  "diffusers/controlnet-canny-sdxl-1.0", dtype=torch.float16
)
pipeline = StableDiffusionXLControlNetPipeline.from_pretrained(
  "stabilityai/stable-diffusion-xl-base-1.0",
  controlnet=controlnet,
  dtype=torch.float16
).to("cuda")  # or "mps", "xpu", "cpu"

canny_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/canny-cat.png")
pipeline(
  "",
  image=canny_image,
  guess_mode=True
).images[0]
Canny image
canny image
Generated image (Guess mode)
生成された画像
Update on GitHub