Diffusers 文档 (Diffusers documentation)

外绘 (Outpainting)

Hugging Face's logo
加入 Hugging Face 社区 (Join the Hugging Face community)

并获得增强的文档体验 (and get access to the augmented documentation experience)

开始使用 (to get started)

外绘 (Outpainting)

外绘将图像扩展到其原始边界之外,允许您在保留原始图像的同时,在图像中添加、替换或修改视觉元素。与图像修复 (inpainting)类似,您希望用新的视觉元素填充白色区域(在本例中为原始图像外部的区域),同时保留原始图像(用黑色像素的蒙版表示)。有几种外绘方法,例如使用 ControlNet 或使用 Differential Diffusion

本指南将向您展示如何使用图像修复模型、ControlNet 和 ZoeDepth 估计器进行外绘。

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

!pip install -q controlnet_aux

图像准备 (Image preparation)

首先选择要进行外绘的图像,并使用类似 BRIA-RMBG-1.4 的 Space 删除背景。

例如,从此鞋子图像中删除背景。

原始图像 (original image)
背景已删除 (background removed)

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)在鞋子周围的白色区域生成内容。

加载图像修复 ControlNet、ZoeDepth 模型、VAE 并将它们传递给 StableDiffusionXLControlNetPipeline。然后您可以创建一个可选的 generate_image 函数(为了方便起见)来外绘初始图像。

controlnets = [
    ControlNetModel.from_pretrained(
        "destitech/controlnet-inpaint-dreamer-sdxl", torch_dtype=torch.float16, variant="fp16"
    ),
    ControlNetModel.from_pretrained(
        "diffusers/controlnet-zoe-depth-sdxl-1.0", torch_dtype=torch.float16
    ),
]
vae = AutoencoderKL.from_pretrained("madebyollin/sdxl-vae-fp16-fix", torch_dtype=torch.float16).to("cuda")
pipeline = StableDiffusionXLControlNetPipeline.from_pretrained(
    "SG161222/RealVisXL_V4.0", torch_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()

现在您有了初始外绘图像,加载带有 RealVisXL 模型的 StableDiffusionXLInpaintPipeline,以生成质量更好的最终外绘图像。

pipeline = StableDiffusionXLInpaintPipeline.from_pretrained(
    "OzzyGT/RealVisXL_V4.0_inpainting",
    torch_dtype=torch.float16,
    variant="fp16",
    vae=vae,
).to("cuda")

为最终外绘图像准备一个蒙版。为了在原始图像和外绘背景之间创建更自然的过渡,模糊蒙版以帮助其更好地融合。

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

创建一个更好的 prompt 并将其传递给 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
< > 在 GitHub 上更新 (Update on GitHub)