Diffusers documentation

Outpainting

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v0.42.1).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Outpainting

Outpainting 将图像扩展至其原始边界之外,使您能够在保留原始图像的同时,向图像中添加、替换或修改视觉元素。与 inpainting 类似,您希望用新的视觉元素填充白色区域(在此情况下,即原始图像之外的区域),同时保留原始图像(由黑色像素组成的掩码表示)。有几种 outpainting 方法,例如使用 ControlNet 或 Differential Diffusion。

本指南将向你展示如何使用inpainting模型、ControlNet和ZoeDepth估计器进行outpainting。

在开始之前,请确保已安装 controlnet_aux 库,以便使用 ZoeDepth 估计器。

!pip install -q controlnet_aux

图像准备

首先选择一张用于外扩绘制的图像,并使用 BRIA-RMBG-1.4 等 Space 移除背景。

例如,从这张鞋子的图片中移除背景。

原始图像
背景已移除

Stable Diffusion XL (SDXL) 模型在 1024x1024 图像上表现最佳,但只要您的硬件拥有足够的内存,就可以将图像调整为任意尺寸。图像中的透明背景也应替换为白色背景。创建一个函数(如下所示),将图像缩放并粘贴到白色背景上。

import random

import requests
import torch
from controlnet_aux import ZoeDetector
from PIL import Image, ImageOps

from diffusers import (
    AutoencoderKL,
    ControlNetModel,
    StableDiffusionXLControlNetPipeline,
    StableDiffusionXLInpaintPipeline,
)

def scale_and_paste(original_image):
    aspect_ratio = original_image.width / original_image.height

    if original_image.width > original_image.height:
        new_width = 1024
        new_height = round(new_width / aspect_ratio)
    else:
        new_height = 1024
        new_width = round(new_height * aspect_ratio)

    resized_original = original_image.resize((new_width, new_height), Image.LANCZOS)
    white_background = Image.new("RGBA", (1024, 1024), "white")
    x = (1024 - new_width) // 2
    y = (1024 - new_height) // 2
    white_background.paste(resized_original, (x, y), resized_original)

    return resized_original, white_background

original_image = Image.open(
    requests.get(
        "https://huggingface.co/datasets/stevhliu/testing-images/resolve/main/no-background-jordan.png",
        stream=True,
    ).raw
).convert("RGBA")
resized_img, white_bg_image = scale_and_paste(original_image)

为避免添加不必要的额外细节,请使用 ZoeDepth 估计器在生成过程中提供额外指导,以确保鞋子与原始图像保持一致。

zoe = ZoeDetector.from_pretrained("lllyasviel/Annotators")
image_zoe = zoe(white_bg_image, detect_resolution=512, image_resolution=1024)
image_zoe

Outpaint

当您的图像准备就绪后,您可以使用 controlnet-inpaint-dreamer-sdxl(一个专为图像修复训练的 SDXL ControlNet)在鞋子周围的白色区域生成内容。

加载 inpainting ControlNet、ZoeDepth 模型和 VAE,并将它们传递给 StableDiffusionXLControlNetPipeline。然后,你可以创建一个可选的 generate_image 函数(为了方便起见),用于对初始图像进行 outpaint。

controlnets = [
    ControlNetModel.from_pretrained(
        "destitech/controlnet-inpaint-dreamer-sdxl", dtype=torch.float16, variant="fp16"
    ),
    ControlNetModel.from_pretrained(
        "diffusers/controlnet-zoe-depth-sdxl-1.0", dtype=torch.float16
    ),
]
vae = AutoencoderKL.from_pretrained("madebyollin/sdxl-vae-fp16-fix", dtype=torch.float16).to("cuda")  # or "mps", "xpu", "cpu"
pipeline = StableDiffusionXLControlNetPipeline.from_pretrained(
    "SG161222/RealVisXL_V4.0", dtype=torch.float16, variant="fp16", controlnet=controlnets, vae=vae
).to("cuda")

def generate_image(prompt, negative_prompt, inpaint_image, zoe_image, seed: int = None):
    if seed is None:
        seed = random.randint(0, 2**32 - 1)

    generator = torch.Generator(device="cpu").manual_seed(seed)

    image = pipeline(
        prompt,
        negative_prompt=negative_prompt,
        image=[inpaint_image, zoe_image],
        guidance_scale=6.5,
        num_inference_steps=25,
        generator=generator,
        controlnet_conditioning_scale=[0.5, 0.8],
        control_guidance_end=[0.9, 0.6],
    ).images[0]

    return image

prompt = "nike air jordans on a basketball court"
negative_prompt = ""

temp_image = generate_image(prompt, negative_prompt, white_bg_image, image_zoe, 908097)

将原始图像粘贴到初始外扩图像上。您将在后续步骤中改进外扩的背景。

x = (1024 - resized_img.width) // 2
y = (1024 - resized_img.height) // 2
temp_image.paste(resized_img, (x, y), resized_img)
temp_image

如果您内存不足,现在是释放一些内存的好时机!

pipeline=None
torch.cuda.empty_cache()

现在你已经获得了初步的外扩图像,请加载 StableDiffusionXLInpaintPipeline 并使用 RealVisXL 模型,以生成质量更高的最终外扩图像。

pipeline = StableDiffusionXLInpaintPipeline.from_pretrained(
    "OzzyGT/RealVisXL_V4.0_inpainting",
    dtype=torch.float16,
    variant="fp16",
    vae=vae,
).to("cuda")  # or "mps", "xpu", "cpu"

为最终扩图图像准备掩码。为了在原始图像和扩图背景之间实现更自然的过渡,请对掩码进行模糊处理,以帮助其更好地融合。

mask = Image.new("L", temp_image.size)
mask.paste(resized_img.split()[3], (x, y))
mask = ImageOps.invert(mask)
final_mask = mask.point(lambda p: p > 128 and 255)
mask_blurred = pipeline.mask_processor.blur(final_mask, blur_factor=20)
mask_blurred

创建一个更好的提示词,并将其传递给 generate_outpaint 函数以生成最终的扩图图像。同样,将原始图像粘贴到最终的扩图背景上。

def generate_outpaint(prompt, negative_prompt, image, mask, seed: int = None):
    if seed is None:
        seed = random.randint(0, 2**32 - 1)

    generator = torch.Generator(device="cpu").manual_seed(seed)

    image = pipeline(
        prompt,
        negative_prompt=negative_prompt,
        image=image,
        mask_image=mask,
        guidance_scale=10.0,
        strength=0.8,
        num_inference_steps=30,
        generator=generator,
    ).images[0]

    return image

prompt = "high quality photo of nike air jordans on a basketball court, highly detailed"
negative_prompt = ""

final_image = generate_outpaint(prompt, negative_prompt, temp_image, mask_blurred, 7688778)
x = (1024 - resized_img.width) // 2
y = (1024 - resized_img.height) // 2
final_image.paste(resized_img, (x, y), resized_img)
final_image
Update on GitHub