推荐阅读列表:
扩散模型实战(一):基本原理介绍
扩散模型实战(二):扩散模型的发展
扩散模型实战(三):扩散模型的应用
扩散模型实战(四):从零构建扩散模型
扩散模型实战(五):采样过程
扩散模型实战(六):Diffusers DDPM初探
扩散模型实战(七):Diffusers蝴蝶图像生成实战
扩散模型实战(八):微调扩散模型
扩散模型实战(九):使用CLIP模型引导和控制扩散模型
扩散模型实战(十):Stable Diffusion文本条件生成图像大模型
扩散模型实战(十一):剖析Stable Diffusion Pipeline各个组件
扩散模型实战(十二):使用调度器DDIM反转来优化图像编辑
经过前面的学习,我们已经可以熟练的使用文本Prompt来生成一副精美的图片了。通常来说,文本Prompt准确性越高,描述越丰富,生成的图像越符合用户的预期,然而,有些事物很难通过文本Prompt来指导Stable Diffusion模型,比如人物四肢的角度、背景中物体的位置、每一缕光线的角度,因为文字的表达能力是有限的。
一、ControlNet介绍
本文分享的ControlNet是一种能够嵌入任意已经训练好的扩散模型,并通过图像Prompt来引入图像特征更加精细的控制扩散模型的生成过程,ControlNet的基本结构如下图所示:
从上图可以看出,ControlNet的基本结构由一个对应的原先网络的神经网络模块和两个”零卷积“层组成。在训练过程中,会”锁死“原先网络的权重,只更新ControlNet基本结构中的网络”副本“和零卷积层的权重。这些可训练的网络”副本“将学会如何让模型按照新的控制条件来生成结果,而被”锁死“的网络会保留原先网络已经学会的所有知识。零卷积层是一些权重和偏置被初始化为0的1X1卷积层。训练刚开始的时候,无论新添加的控制条件是什么,这些零卷积层都只输出0,ControlNet不会对扩散模型的生成结果造成任何影响。随着训练过程的深入,ControlNet将逐渐调整扩展模型原先的生成过程,使得生成的图像逐渐向新添加的控制条件靠近。
二、ControlNet训练
将上面描述的ControlNet block堆叠14次就得到完整的ControlNet,如下图所示:
训练一个附加到某个Stable Diffusion模型上的ControlNet的过程大致如下所示:
-
收集想要对其附加控制条件的数据集和对应的Prompt。假如想训练一个通过人体关键点来对扩散模型的人体进行姿态控制的ControlNet,则首先需要收集一批人物图片,并标注好这批人物图片的Prompt以及对应的人体关键点的位置;
-
将Prompt输入被”锁死“的Stable Diffusion模型,并将标注好的图像控制条件(如人体关键点的标注结果)输入ControlNet,然后按照Stable Diffusion模型的训练过程迭代ControlNet block权重;
-
在训练过程中,随机将50%的文本Prompt替换为空白字符串,这样做的目的是”强制“网络从图像控制条件中学习更多的语义信息;
-
训练结束后,便可以使用ControlNet对应的图像控制条件(如输入的人体关键点)来控制扩散模型生成符合条件的图像
三、ControlNet示例
本小节将介绍一些已经训练好的ControlNet示例,都来自Huggingface。
3.1 ControlNet与Canny Edge
Canny Edege是一种多阶段的边缘检测算法,该算法可以从不同的视觉对象中提取有用的结构信息,从而显著降低图像处理过程中的数据处理量,ControlNet与Canny Edge结合使用的效果如下图所示:
3.2 ControlNet与M-LSD Lines
M-LSD Lines是另一种轻量化的边缘检测算法,擅长提取图像中的直线线条。训练在M-LSD Lines上的ControlNet适合室内环境方面的图片。ControlNet与M-LSD Lines结合使用的效果,如下图所示:
3.3 ControlNet与HED Boundary
HED Boundary可以保持输入图像更多信息,训练在HED Boundary上的ControlNet适合用来重新上色和进行风格重构。ControlNet与HED Boundary结合使用的效果,如下图所示:
四、ControlNet实战
下面以Canny Edge为例,展示如何在Diffusers中使用StableDiffusionControlNetPipeline生成图像。
安装需要使用的库
!pip install -q diffusers==0.14.0 transformers xformers git+https://
github.com/huggingface/accelerate.git
为了对应选择ControlNet,需要安装两个依赖库对图像进行处理,以提取不同的图像控制条件
!pip install -q opencv-contrib-python
!pip install -q controlnet_aux
以知名画作《戴珍珠耳环的少女》为例进行展示ControlNet效果
from diffusers import StableDiffusionControlNetPipeline
from diffusers.utils import load_image
image = load_image(
"https://hf.co/datasets/huggingface/documentation-images/
resolve/main/diffusers/input_image_vermeer.png"
)
image
首先将这张图片交给Canny Edge边缘提取器进行预处理
import cv2
from PIL import Image
import numpy as np
image = np.array(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_image
边缘提取效果,如下图所示:
可以看到Canny Edge能够识别出图像中物体的边缘线条。接下来,需要导入runwaylml/stable-diffusion-v1-5模型以及能够处理Canny Edge的ControlNet模型。为了加快推理速度,采用半精度(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
)
使用当前速度最快的扩散模型调度器-UniPCMultistepScheduler,迭代20次就可以达到之前默认调度器50次迭代的效果。
from diffusers import UniPCMultistepScheduler
pipe.scheduler = UniPCMultistepScheduler.from_config(pipe.scheduler.
config)
使用Canny Edge来控制所生成图像中物体的确切位置和边缘轮廓。下面代码旨在生成一些人物的肖像,这些人物钧摆出与《戴珍珠耳环的少女》相同的姿势。在ControlNet和Canny Edge下,我们只需要在Prompt中提到这些人的姓名就可以了,代码如下:
def image_grid(imgs, rows, cols):
assert len(imgs) == rows * cols
w, h = imgs[0].size
grid = Image.new("RGB", size=(cols * w, rows * h))
grid_w, grid_h = grid.size
for i, img in enumerate(imgs):
grid.paste(img, box=(i % cols * w, i // cols * h))
return grid
prompt = ", best quality, extremely detailed"
prompt = [t + prompt for t in ["Sandra Oh", "Kim Kardashian",
"rihanna", "taylor swift"]]
generator = [torch.Generator(device="cpu").manual_seed(2) for i
in range(len(prompt))]
output = pipe(
prompt,
canny_image,
negative_prompt=["monochrome, lowres, bad anatomy, worst
quality, low quality"] * len (prompt),
generator=generator,
num_inference_steps=20,
)
image_grid(output.images, 2, 2)
生成的人物肖像,如下图所示:
还可以从一张图片中提取身体姿态,然后用它生成具有完全相同的身体姿态的另一张图片,代码如下:
urls = "yoga1.jpeg", "yoga2.jpeg", "yoga3.jpeg", "yoga4.jpeg"
imgs = [
load_image("https://hf.co/datasets/YiYiXu/controlnet-testing/
resolve/main/" + url)
for url in urls
]
image_grid(imgs, 2, 2)
原始图片如下图所示:
提取瑜伽的身体姿态,代码如下:
from controlnet_aux import OpenposeDetector
model = OpenposeDetector.from_pretrained("lllyasviel/ControlNet")
poses = [model(img) for img in imgs]
image_grid(poses, 2, 2)
提取效果,如下图所示:
使用Open Pose ControlNet生成一些正在做瑜伽的超级英雄图片,代码如下:
controlnet = ControlNetModel.from_pretrained(
"fusing/stable-diffusion-v1-5-controlnet-openpose",
torch_dtype=torch.float16
)
model_id = "runwayml/stable-diffusion-v1-5"
pipe = StableDiffusionControlNetPipeline.from_pretrained(
model_id,
controlnet=controlnet,
torch_dtype=torch.float16,
)
pipe.scheduler = UniPCMultistepScheduler.from_config(pipe.scheduler.
config)
pipe.enable_model_cpu_of f load()
pipe.enable_xformers_memory_efficient_attention()
generator = [torch.Generator(device="cpu").manual_seed(2) for i
in range(4)]
prompt = "super-hero character, best quality, extremely detailed"
output = pipe(
[prompt] * 4,
poses,
negative_prompt=["monochrome, lowres, bad anatomy, worst
quality, low quality"] * 4,
generator=generator,
num_inference_steps=20,
)
image_grid(output.images, 2, 2)
生成的效果,如下图所示:
关于ControlNet更多的使用方法,可以参考如下:
lllyasviel/sd-controlnet-depth
lllyasviel/sd-controlnet-hed
lllyasviel/sd-controlnet-normal
lllyasviel/sd-controlnet-scribbl
lllyasviel/sd-controlnet-seg
lllyasviel/sd-controlnet-openpose
lllyasviel/sd-controlnet-mlsd