Diffusers documentation

ControlNet

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