Diffusers documentation
Outpainting
Outpainting
Outpainting mở rộng một hình ảnh vượt ra ngoài các ranh giới ban đầu của nó, cho phép bạn thêm, thay thế hoặc chỉnh sửa các yếu tố trực quan trong hình ảnh trong khi vẫn giữ nguyên hình ảnh gốc. Giống như inpainting, bạn muốn lấp đầy vùng màu trắng (trong trường hợp này là vùng bên ngoài hình ảnh gốc) bằng các yếu tố trực quan mới, đồng thời giữ nguyên hình ảnh gốc (được biểu diễn bằng một mask gồm các pixel màu đen). Có một số cách để outpaint, chẳng hạn như sử dụng ControlNet hoặc Differential Diffusion.
Hướng dẫn này sẽ cho bạn thấy cách outpaint bằng một mô hình inpainting, ControlNet và bộ ước lượng ZoeDepth.
Trước khi bắt đầu, hãy đảm bảo bạn đã cài đặt thư viện controlnet_aux để có thể sử dụng bộ ước lượng ZoeDepth.
!pip install -q controlnet_aux
Chuẩn bị hình ảnh
Bắt đầu bằng cách chọn một hình ảnh để outpaint và xóa nền bằng một Space như BRIA-RMBG-1.4.
Ví dụ, hãy xóa nền khỏi hình ảnh một đôi giày này.
Mô hình Stable Diffusion XL (SDXL) hoạt động tốt nhất với ảnh kích thước 1024x1024, nhưng bạn có thể thay đổi kích thước ảnh sang bất kỳ kích thước nào miễn là phần cứng của bạn có đủ bộ nhớ để hỗ trợ. Nền trong suốt trong ảnh cũng nên được thay thế bằng nền trắng. Hãy tạo một hàm (như hàm bên dưới) để thu phóng và dán ảnh lên nền trắng.
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)Để tránh thêm các chi tiết thừa không mong muốn, hãy sử dụng bộ ước lượng ZoeDepth để cung cấp thêm sự dẫn dắt trong quá trình tạo và đảm bảo đôi giày nhất quán với hình ảnh gốc.
zoe = ZoeDetector.from_pretrained("lllyasviel/Annotators")
image_zoe = zoe(white_bg_image, detect_resolution=512, image_resolution=1024)
image_zoe
Outpaint
Khi hình ảnh của bạn đã sẵn sàng, bạn có thể tạo nội dung trong vùng trắng xung quanh đôi giày bằng controlnet-inpaint-dreamer-sdxl, một SDXL ControlNet được huấn luyện cho inpainting.
Tải ControlNet inpainting, mô hình ZoeDepth và VAE rồi truyền chúng vào StableDiffusionXLControlNetPipeline. Sau đó, bạn có thể tạo một hàm tùy chọn generate_image (cho tiện lợi) để outpaint một hình ảnh ban đầu.
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)Dán hình ảnh gốc lên trên hình ảnh outpaint ban đầu. Bạn sẽ cải thiện nền outpaint ở bước sau.
x = (1024 - resized_img.width) // 2
y = (1024 - resized_img.height) // 2
temp_image.paste(resized_img, (x, y), resized_img)
temp_image
Đây là thời điểm tốt để giải phóng một chút bộ nhớ nếu bạn đang sắp hết!
pipeline=None torch.cuda.empty_cache()
Bây giờ bạn đã có một hình ảnh outpaint ban đầu, hãy nạp StableDiffusionXLInpaintPipeline với mô hình RealVisXL để tạo ra hình ảnh outpaint cuối cùng với chất lượng tốt hơn.
pipeline = StableDiffusionXLInpaintPipeline.from_pretrained(
"OzzyGT/RealVisXL_V4.0_inpainting",
dtype=torch.float16,
variant="fp16",
vae=vae,
).to("cuda") # or "mps", "xpu", "cpu"Chuẩn bị một mặt nạ (mask) cho hình ảnh outpaint cuối cùng. Để tạo ra sự chuyển tiếp tự nhiên hơn giữa hình ảnh gốc và nền outpaint, hãy làm mờ mặt nạ để giúp nó hòa trộn tốt hơn.
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
Tạo một prompt tốt hơn và truyền nó vào hàm generate_outpaint để tạo ra hình ảnh outpaint cuối cùng. Một lần nữa, dán hình ảnh gốc lên trên nền outpaint cuối cùng.
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