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 guide un modèle de diffusion préentraîné à l’aide d’une image de contrôle structurelle (contours, profondeur ou pose) tout en conservant un prompt texte. Il fige le modèle de base et ajoute un réseau parallèle dont les résidus orientent le débruiteur, offrant ainsi un contrôle de la mise en page sans réentraînement.

Chargez un ControlNet adapté au contrôle dont vous avez besoin (par exemple canny), puis transmettez-le en tant que controlnet= à from_pretrained(). Utilisez controlnet_conditioning_scale pour définir l’intensité avec laquelle le contrôle oriente la génération.

Les ControlNets sont disponibles pour de nombreux modèles tels que Flux, Hunyuan-DiT, Stable Diffusion 3, et d'autres. Les exemples de ce guide utilisent Flux et Stable Diffusion XL. Pour T2I-Adapter sur les anciens points de contrôle de la famille Stable Diffusion, consultez Legacy adapters. Privilégiez ControlNet pour les nouveaux travaux de génération contrôlable.

Les onglets ci-dessous présentent ControlNet pour la génération d'images à partir de texte, la transformation d'image en image et l'inpainting.

texte-vers-image
image-vers-image
inpainting

Générer une image canny avec opencv-python.

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)

Transmettez l’image canny au pipeline. Utilisez le paramètre controlnet_conditioning_scale pour déterminer le poids à accorder au contrôle.

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)
image d'origine
Control image (Canny edges)
image canny
Generated image (ControlNet + prompt)
image générée

Multi-ControlNet

Composez plusieurs ControlNets (par exemple canny + depth) en les passant sous forme de liste. Masquez les régions qui se chevauchent lorsque c’est possible, et ajustez chaque controlnet_conditioning_scale. Construisez canny_image avec OpenCV Canny (comme pour le texte-vers-image) et depth_image avec DPT (comme pour l’image-vers-image). Redimensionnez chaque image de contrôle à la taille du pipeline avant d’appeler le pipeline.

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
image canny
Depth map
carte de profondeur
Generated image (ControlNet + prompt)
image générée

Mode de devinette

Le mode Guess génère une image à partir d’un seul contrôle lorsque vous n’avez pas (ou ne souhaitez pas) de prompt texte. Il met à l’échelle les résidus de ControlNet en fonction de la profondeur des blocs, passant d’environ 0.1 dans les blocs de descente initiaux jusqu’à 1.0 au niveau du bloc intermédiaire. Cela empêche les couches initiales de sur-contraindre, tout en permettant aux blocs plus profonds de conserver la structure.

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
image canny
Generated image (Guess mode)
image générée
Update on GitHub