Diffusers documentation
Outpainting
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.
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