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 estende uma imagem além de suas fronteiras originais, permitindo adicionar, substituir ou modificar elementos visuais em uma imagem enquanto preserva a imagem original. Assim como no inpainting, o objetivo é preencher a área branca (neste caso, a área fora da imagem original) com novos elementos visuais, mantendo a imagem original (representada por uma máscara de pixels pretos). Existem algumas maneiras de realizar outpainting, como com um ControlNet ou com Differential Diffusion.

Este guia mostrará como fazer outpainting com um modelo de inpainting, ControlNet e um estimador ZoeDepth.

Antes de começar, verifique se a biblioteca controlnet_aux está instalada para que você possa usar o estimador ZoeDepth.

!pip install -q controlnet_aux

Preparação de imagem

Comece escolhendo uma imagem para fazer outpainting e remova o fundo com um Space como BRIA-RMBG-1.4.

Por exemplo, remova o fundo desta imagem de um par de sapatos.

imagem original
fundo removido

Os modelos Stable Diffusion XL (SDXL) funcionam melhor com imagens de 1024x1024, mas você pode redimensionar a imagem para qualquer tamanho, desde que seu hardware tenha memória suficiente para suportá-lo. O fundo transparente da imagem também deve ser substituído por um fundo branco. Crie uma função (como a abaixo) que escala e cola a imagem sobre um fundo branco.

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)

Para evitar a adição de detalhes extras indesejados, use o estimador ZoeDepth para fornecer orientação adicional durante a geração e garantir que os sapatos permaneçam consistentes com a imagem original.

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

Outpaint

Quando sua imagem estiver pronta, você pode gerar conteúdo na área branca ao redor dos sapatos com controlnet-inpaint-dreamer-sdxl, um ControlNet SDXL treinado para inpainting.

Carregue o ControlNet de inpainting, o modelo ZoeDepth e o VAE e passe-os para o StableDiffusionXLControlNetPipeline. Em seguida, você pode criar uma função opcional generate_image (para conveniência) para fazer outpainting de uma imagem inicial.

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)

Cole a imagem original sobre a imagem inicial expandida. Você melhorará o fundo expandido em uma etapa posterior.

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

Agora é um bom momento para liberar memória, caso você esteja com pouco espaço disponível!

pipeline=None
torch.cuda.empty_cache()

Agora que você tem uma imagem inicial outpainted, carregue o StableDiffusionXLInpaintPipeline com o modelo RealVisXL para gerar a imagem outpainted final com melhor qualidade.

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

Prepare uma máscara para a imagem final outpainted. Para criar uma transição mais natural entre a imagem original e o fundo outpainted, aplique um desfoque na máscara para ajudar a mesclá-la melhor.

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

Crie um prompt melhor e passe-o para a função generate_outpaint para gerar a imagem final outpainted. Novamente, cole a imagem original sobre o fundo outpainted final.

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