Перейти к содержанию
Публикация AiManual

ControlNet в Diffusers: полный гайд по управлению генерацией изображений

Управляйте композицией генерации: карты глубины, скетчи, OpenPose и сегментация в StableDiffusionControlNetPipeline. Практический код, комбинирование ControlNet

Коротко

Что будет в материале

  1. 01

    Введение: что такое ControlNet и зачем он нужен

  2. 02

    Установка и настройка окружения

  3. 03

    Базовое использование StableDiffusionControlNetPipeline

  4. 04

    Препроцессоры ControlNet: Canny, OpenPose и другие

ControlNet решает главную проблему диффузионных моделей: текстовый промпт задаёт содержание, но не композицию. Вы не можете гарантировать позу человека, точные границы объекта или глубину сцены. ControlNet добавляет второй вход для управляющего сигнала: карты глубины, сегментации, скетча, ключевых точек. В Diffusers эта функциональность реализована через пайплайн StableDiffusionControlNetPipeline. Он принимает управляющее изображение и текстовый промпт, а на выходе выдаёт изображение, которое сохраняет пространственную структуру условия.

Практические сценарии: генерация по скетчу, перенос позы с одного фото на другое, контроль глубины сцены для архитектурных визуализаций, удержание границ объекта при изменении стиля. В этом гайде разберём установку, препроцессоры, комбинирование нескольких ControlNet, ускорение инференса и связку с DreamBooth.

Если вы только начинаете работать с библиотекой Diffusers, полезно сначала посмотреть обзор эволюции Diffusers за первый год: там разобраны базовые архитектурные изменения и появление ключевых пайплайнов.

Введение: что такое ControlNet и зачем он нужен

Стандартная модель Stable Diffusion генерирует изображение из шума, опираясь только на текстовый энкодер. Проблема: текст описывает «что», но не «где». Фраза «человек стоит на фоне гор» даёт модели свободу в расположении фигуры, ракурсе и пропорциях. ControlNet добавляет обучаемую копию энкодера UNet, которая принимает дополнительное условие: карту глубины, сегментацию, скетч, ключевые точки OpenPose. Эта копия обучается на парах «исходное изображение - управляющий сигнал» и передаёт пространственные ограничения в основную модель.

Архитектурно ControlNet подключается к UNet через zero-convolution слои. На старте обучения эти слои инициализируются нулями, чтобы не разрушать предобученные веса. Постепенно модель учится учитывать управляющий сигнал, не теряя качества генерации. Такой подход позволяет дообучать ControlNet на относительно небольших датасетах.

В Diffusers пайплайн StableDiffusionControlNetPipeline оборачивает эту логику в единый интерфейс. Вы передаёте управляющее изображение, промпт и получаете результат. Пайплайн поддерживает один или несколько ControlNet одновременно, что открывает путь к комбинированному контролю.

Установка и настройка окружения

Для работы нужен Python 3.10 или новее, GPU с поддержкой CUDA и не менее 8 ГБ видеопамяти для базовых сценариев. На CPU генерация возможна, но медленная: один проход может занимать минуты вместо секунд.

Необходимые зависимости

Установите пакеты через pip:

pip install diffusers transformers accelerate torch opencv-python controlnet-aux

Назначение пакетов:

  • diffusers - библиотека пайплайнов и моделей от Hugging Face, версия 0.27.0 или новее.
  • transformers - текстовый энкодер CLIP для обработки промптов.
  • accelerate - оптимизация загрузки моделей и распределение по устройствам.
  • torch - вычислительный бэкенд, версия 2.0+ для поддержки torch.compile.
  • opencv-python - препроцессор Canny и другие операции с изображениями.
  • controlnet-aux - вспомогательные препроцессоры: OpenPose, MiDaS, HED, MLSD.

Проверка установки

Запустите скрипт для проверки импортов и доступности GPU:

import torch
import diffusers
import transformers
import cv2
print(f"PyTorch: {torch.__version__}")
print(f"Diffusers: {diffusers.__version__}")
print(f"Transformers: {transformers.__version__}")
print(f"CUDA available: {torch.cuda.is_available()}")
if torch.cuda.is_available():
    print(f"GPU: {torch.cuda.get_device_name(0)}")

Если CUDA available возвращает False, проверьте установку драйверов NVIDIA и соответствие версии torch вашей CUDA. Для Apple Silicon используйте устройство mps вместо cuda.

Базовое использование StableDiffusionControlNetPipeline

Минимальный рабочий пример состоит из трёх шагов: загрузка моделей, подготовка управляющего изображения, запуск генерации.

Загрузка моделей

Нужны две модели: базовая Stable Diffusion и ControlNet. Базовая модель задаёт стиль и качество генерации, ControlNet - пространственный контроль. Используйте torch_dtype=torch.float16 для экономии видеопамяти:

from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
import torch

controlnet = ControlNetModel.from_pretrained(
    "lllyasviel/sd-controlnet-canny",
    torch_dtype=torch.float16
)
pipe = StableDiffusionControlNetPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    controlnet=controlnet,
    torch_dtype=torch.float16
)
pipe.to("cuda")

Модель lllyasviel/sd-controlnet-canny обучена на картах границ Canny. Для других типов условий загружайте соответствующие модели: sd-controlnet-openpose, sd-controlnet-depth, sd-controlnet-seg.

Подготовка управляющего изображения

Для Canny используйте OpenCV. Загрузите исходное изображение, примените детектор границ с порогами 100 и 200, преобразуйте в PIL Image:

import cv2
from PIL import Image
import numpy as np

image = cv2.imread("input.jpg")
image = cv2.Canny(image, 100, 200)
image = image[:, :, None]
image = np.concatenate([image, image, image], axis=2)
canny_image = Image.fromarray(image)

Пороги low_threshold=100 и high_threshold=200 дают сбалансированный результат для большинства фото. Увеличивайте нижний порог, чтобы отсечь шум и мелкие детали, уменьшайте - чтобы сохранить больше контуров.

Запуск генерации

Передайте промпт, управляющее изображение и параметры сэмплирования:

prompt = "a cozy cabin in the mountains, snow, sunset, highly detailed"
output = pipe(
    prompt,
    image=canny_image,
    num_inference_steps=30,
    guidance_scale=7.5
).images[0]
output.save("output.png")

num_inference_steps=30 - разумный баланс качества и скорости. guidance_scale=7.5 - стандартное значение для Stable Diffusion: выше - сильнее следование промпту, но возможны артефакты; ниже - больше свободы модели.

Препроцессоры ControlNet: Canny, OpenPose и другие

Препроцессор преобразует исходное изображение в управляющий сигнал, понятный ControlNet. Выбор препроцессора определяет, какой аспект композиции вы контролируете.

Canny: выделение границ

Canny извлекает контуры объектов. Это универсальный инструмент: он подходит для архитектуры, предметной съёмки, пейзажей. Модель сохраняет форму и расположение объектов, но свободно меняет текстуры и освещение.

Ключевые параметры OpenCV:

  • low_threshold - нижний порог. Пиксели с градиентом ниже отбрасываются.
  • high_threshold - верхний порог. Пиксели выше гарантированно считаются границей.

Для фото с чёткими контурами используйте 100/200. Для рисунков и скетчей снизьте до 50/150, чтобы захватить слабые линии. Для зашумлённых изображений поднимите до 150/250.

OpenPose: контроль позы человека

OpenPose извлекает скелет человека: ключевые точки тела, лица и пальцев. Это решает задачу генерации человека в заданной позе без необходимости описывать позу словами. Промпт «танцор в прыжке» даёт случайную позу; OpenPose фиксирует её точно.

Используйте библиотеку controlnet-aux:

from controlnet_aux import OpenposeDetector

openpose = OpenposeDetector.from_pretrained("lllyasviel/ControlNet")
pose_image = openpose(image)

Ограничение: OpenPose теряет информацию об одежде, текстурах и фоне. Модель получает только скелет, поэтому детали внешности задаются промптом. Для сложных поз с перекрытием конечностей качество детекции может падать.

Другие препроцессоры

  • Depth (MiDaS) - карта глубины. Контролирует перспективу и расстояние между объектами. Полезна для интерьеров и сцен с выраженной глубиной.
  • Segmentation (ADE20K) - семантическая сегментация. Каждому классу объектов присваивается цвет. Позволяет задать, где будет небо, дерево, здание, человек.
  • HED - мягкие границы. В отличие от Canny, сохраняет плавные переходы и подходит для художественных стилей.
  • MLSD - прямые линии. Оптимален для архитектуры и интерьеров с чёткими геометрическими формами.

Можно использовать собственные препроцессоры: любой скрипт, который выдаёт изображение в нужном формате, подходит для передачи в пайплайн.

Комбинирование нескольких ControlNet

Один ControlNet контролирует один аспект. Для сложных сценариев нужно несколько: например, Canny для границ и OpenPose для позы человека. Diffusers поддерживает передачу списка моделей и списка управляющих изображений.

Пример: Canny + OpenPose

Загрузите две модели ControlNet и передайте их в пайплайн:

from diffusers import StableDiffusionControlNetPipeline, ControlNetModel

controlnet_canny = ControlNetModel.from_pretrained(
    "lllyasviel/sd-controlnet-canny",
    torch_dtype=torch.float16
)
controlnet_openpose = ControlNetModel.from_pretrained(
    "lllyasviel/sd-controlnet-openpose",
    torch_dtype=torch.float16
)
pipe = StableDiffusionControlNetPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    controlnet=[controlnet_canny, controlnet_openpose],
    torch_dtype=torch.float16
)
pipe.to("cuda")

output = pipe(
    prompt,
    image=[canny_image, pose_image],
    num_inference_steps=30,
    guidance_scale=7.5
).images[0]

Порядок списков важен: первый ControlNet получает первое изображение, второй - второе.

Настройка весов ControlNet

Параметр controlnet_conditioning_scale управляет влиянием каждого ControlNet. Значение 1.0 - полное влияние, 0.0 - ControlNet отключён. Для комбинированных сценариев передавайте список весов:

output = pipe(
    prompt,
    image=[canny_image, pose_image],
    controlnet_conditioning_scale=[0.7, 0.9],
    num_inference_steps=30,
    guidance_scale=7.5
).images[0]

Рекомендации: начните с 0.7–0.8 для каждого ControlNet. Если композиция слишком жёсткая, снизьте веса. Если модель игнорирует один из сигналов, увеличьте его вес до 1.0. Для одиночного ControlNet стандартное значение - 1.0.

Ускорение инференса и оптимизация памяти

Базовая генерация с ControlNet на GPU с 8 ГБ видеопамяти занимает 10–20 секунд. Оптимизация сокращает время в 2–4 раза и позволяет запускать модели на более слабом железе.

Быстрые планировщики

Стандартный планировщик PNDM требует 50 шагов для качественного результата. DPM-Solver++ и UniPC дают сопоставимое качество за 20–25 шагов. Замена планировщика:

from diffusers import DPMSolverMultistepScheduler

pipe.scheduler = DPMSolverMultistepScheduler.from_config(
    pipe.scheduler.config
)

После замены уменьшите num_inference_steps до 20–25. Это сокращает время генерации примерно вдвое без видимой потери качества.

Оптимизация памяти

Три метода для GPU с ограниченной памятью:

  • enable_attention_slicing() - обрабатывает attention-слои по частям. Снижает пиковое потребление памяти на 20–30%, незначительно увеличивая время.
  • enable_model_cpu_offload() - переносит компоненты модели на CPU, когда они не используются. Позволяет запускать пайплайн на GPU с 4–6 ГБ, но увеличивает время генерации в 1.5–2 раза.
  • enable_vae_slicing() - декодирует изображение по частям. Экономит память на финальном этапе.

Пример включения всех оптимизаций:

pipe.enable_attention_slicing()
pipe.enable_model_cpu_offload()
pipe.enable_vae_slicing()

Для GPU с 8 ГБ и более достаточно attention slicing. CPU offload используйте только при нехватке памяти, так как он замедляет генерацию. torch.compile может дать дополнительное ускорение на 10–20%, но требует совместимости модели и увеличивает время первой компиляции.

Если вы работаете с большими моделями на ограниченном железе, посмотрите разбор производительности GLM-5.2 на 8× GB10: там описаны приёмы квантования и распределения памяти, применимые и к diffusion-моделям.

ControlNet + DreamBooth: перенос объектов в любые сцены

DreamBooth дообучает модель на нескольких изображениях конкретного объекта: вашей собаки, продукта, персонажа. После дообучения модель генерирует этот объект в новых контекстах. ControlNet добавляет пространственный контроль: вы задаёте позу, окружение или композицию.

Подготовка модели DreamBooth

Для дообучения нужно 5–20 изображений объекта с разных ракурсов. Используйте официальный скрипт train_dreambooth из репозитория Diffusers. Обучение на GPU с 16 ГБ занимает 20–40 минут. Результат - модель, которая понимает уникальный идентификатор, например «sks dog».

Совместное использование

Загрузите дообученную модель как базовую и подключите ControlNet:

from diffusers import StableDiffusionControlNetPipeline, ControlNetModel

controlnet = ControlNetModel.from_pretrained(
    "lllyasviel/sd-controlnet-openpose",
    torch_dtype=torch.float16
)
pipe = StableDiffusionControlNetPipeline.from_pretrained(
    "./dreambooth-model",
    controlnet=controlnet,
    torch_dtype=torch.float16
)
pipe.to("cuda")

output = pipe(
    "a photo of sks dog sitting on a beach",
    image=pose_image,
    num_inference_steps=30,
    guidance_scale=7.5
).images[0]

Связка работает так: DreamBooth отвечает за идентичность объекта, ControlNet - за позу и композицию. Вы получаете свою собаку в позе из OpenPose на любом фоне, заданном промптом.

Для более сложных сценариев с несколькими объектами и точным контролем границ комбинируйте DreamBooth с несколькими ControlNet. Например, Canny для контуров сцены и OpenPose для позы персонажа.

Заключение и дополнительные ресурсы

ControlNet в Diffusers даёт точный контроль над композицией: от простых скетчей до комбинированных сценариев с несколькими управляющими сигналами. Ключевые выводы:

  • StableDiffusionControlNetPipeline принимает управляющее изображение и текстовый промпт, сохраняя пространственную структуру условия.
  • Препроцессоры Canny, OpenPose, Depth, Segmentation покрывают основные сценарии контроля.
  • Комбинирование нескольких ControlNet с весами controlnet_conditioning_scale даёт гибкость для сложных задач.
  • Быстрые планировщики и оптимизация памяти сокращают время генерации и снижают требования к железу.
  • Связка с DreamBooth переносит конкретные объекты в любые сцены с заданной позой.

Экспериментируйте с весами, препроцессорами и промптами. Начните с простого Canny, затем добавьте OpenPose, затем попробуйте DreamBooth. Официальная документация Diffusers и репозиторий ControlNet содержат дополнительные примеры и предобученные модели.

Подписаться на канал