在Automatic1111的stable-diffusion-webui中,我们可以通过img2img中的inpaint功能,结合ControlNet插件,实现ControlNet控制的图像重绘。并且我们也可以将这一流程通过API参数实现调用,从而将这一套代码在云端进行可自动扩缩容的部署,参考:
最近研究了另一种实现ControlNet inpaint的方式,那就是使用diffusers库,这是huggingface官方推出的用于AI画图的一个库,其中也对stable diffusion和ControlNet进行了集成,我们可以以更简单的方式进行调用。
这篇文章就来介绍一下如何使用diffusers库进行controlnet inpaint,最后简单对比一下使用diffusers库和使用stable-diffusion-webui的不同。
安装依赖
只需要通过pip安装下面的依赖就行:
torch==2.0.1
diffusers
transformers
omegaconf
accelerate
opencv-python
xformers
加载ControlNet模型和stable diffusion模型
首先加载Controlnet模型,以加载canny模型为例:
from diffusers import ControlNetModel
from diffusers import UniPCMultistepScheduler
controlnet_canny = ControlNetModel.from_pretrained(
"lllyasviel/control_v11p_sd15_inpaint",
# torch_dtype=torch.float16,
cache_dir=“./cache”,
local_files_only=True,
# resume_download=True,
)
diffusers使用的模型虽然也是safetensors,但是加载时需要的文件和webui不太一样,所以我们需要到huggingface去获取现成的转换好的模型,可以去 huggingface.co/models 进行搜索,常用的模型基本上都有现成的。
上面的代码中,ControlNet使用的是canny的inpaint模型,使用ControlNetModel.from_pretrained
进行加载,加载的参数中,如果使用的是Mac,需要注释掉torch_dtype
参数,cache_dir
指定了下载的模型的本地保存路径,resume_download
可以指定是否继续未完成的下载,local_files_only
适用于之前已经在本地下载了模型了,之后无需重新下载。更多参数可以参考:huggingface.co/docs/diffus…
接下来,加载stable diffusion模型,并且将上面加载的controlnet模型放到一个pipeline中:
from diffusers import StableDiffusionControlNetInpaintPipeline
canny_pipe = StableDiffusionControlNetInpaintPipeline.from_pretrained(
"Uminosachi/realisticVisionV51_v51VAE-inpainting",
controlnet=controlnet_canny,
# torch_dtype=torch.float16,
cache_dir="./cache",
local_files_only=True,
safety_checker=None,
use_safetensors=True,
# resume_download=True,
).to("cpu")
canny_pipe.scheduler = UniPCMultistepScheduler.from_config(canny_pipe.scheduler.config)
因为我们准备进行controlnet重绘任务,所以使用的是diffusers中的StableDiffusionControlNetInpaintPipeline
。要加载的模型也是去huggingface上搜现成的就行,contronet
参数指定了要放到pipeline中的controlnet模型,最后to('cpu')
的部分,如果电脑上装了cuda的话,可以改成to('cuda')
。最后还指定了一个scheduler,这个应该是类似于webui中的sampler的概念,目前比较推荐的应该是UniPCMultistepScheduler
图像预处理
对于canny的img2img,我们首先要在代码中对输入图片作预处理得到图片的线条轮廓,直接使用opencv-python
库就行了:
import numpy as np
from PIL import Image, ImageOps
import cv2
def process_image(image, structure, low_threshold=100, high_threshold=200):
if structure == 'canny':
input_image = canny_preprocessor(image, low_threshold, high_threshold)
return input_image
def canny_preprocessor(image, low_threshold, high_threshold):
# Convert to numpy
image = np.array(image)
image = cv2.Canny(image, low_threshold, high_threshold)
image = image[:, :, None]
image = np.concatenate([image, image, image], axis=2)
canny_image = Image.fromarray(image)
return canny_image
input_image = Image.open("111.jpeg")
canny_image = process_image(input_image, "canny", low_threshold=100, high_threshold=200)
除了canny,更多的预处理方式可以参考:github.com/anotherjess…
运行diffuser
接下来我们就可以开始调用模型进行img2img了:
import torch
import os
@torch.inference_mode()
def predict(self):
pipe = canny_pipe
# 如果安装了xformers,可以使用下面这一行代码,否则就注释掉
# pipe.enable_xformers_memory_efficient_attention()
seed = int.from_bytes(os.urandom(2), "big")
print(f"Using seed: {seed}")
mask_image = Image.open("mask.png")
prompt = "RAW photo, 1 girl, 8k uhd, dslr, soft lighting, high quality, film grain, Fujifilm XT3, on the snow"
negative_prompt = "(deformed iris, deformed pupils, semi-realistic, cgi, 3d, render, sketch, cartoon, drawing, anime, mutated hands and fingers:1.4), (deformed, distorted, disfigured:1.3), poorly drawn, bad anatomy, wrong anatomy, extra limb, missing limb, floating limbs, disconnected limbs, mutation, mutated, ugly, disgusting, amputation"
outputs = pipe(
prompt,
image=input_image,
mask_image=mask_image,
control_image=canny_image,
height=512,
width=512,
num_inference_steps=20,
guidance_scale=8.0,
strength=0.75,
negative_prompt=negative_prompt,
num_images_per_prompt=1,
)
for i, sample in enumerate(outputs.images):
output_path = f"out-{i}.png"
sample.save(output_path)
return
首先,我们随机生成了一个seed,然后基本上就可以直接进行模型调用了。最后使用outputs.images
获取生成的所有图片,这是一个list[PIL.Image]
,所以我们直接调用.save()
方法就能保存了。
因为是进行的controlnet inpaint任务,所以我们一共使用了三张图片:input_image
是原始输入图片,mask_image
是蒙版图片,control_image
就是我们canny预处理之后的图片。
其他参数基本上都很好理解,所有可以选择的参数可以参考:huggingface.co/docs/diffus…__
总结
diffusers相比webui的好处:
- 安装简单,调用简单
- 占用空间小,在云端部署docker镜像的话冷启动相对会快一些
- 速度快一些
缺点:
- 支持的参数和webui有些差别,比如webui中的
DPM++ SDE Karras
的sampler,diffusers里就暂时还没有比较好的替代。所以效果可能也就一般