# Outpainting

Outpainting 将图像扩展至其原始边界之外，使您能够在保留原始图像的同时，向图像中添加、替换或修改视觉元素。与 [inpainting](../using-diffusers/inpaint) 类似，您希望用新的视觉元素填充白色区域（在此情况下，即原始图像之外的区域），同时保留原始图像（由黑色像素组成的掩码表示）。有几种 outpainting 方法，例如使用 [ControlNet](https://hf.co/blog/OzzyGT/outpainting-controlnet) 或 [Differential Diffusion](https://hf.co/blog/OzzyGT/outpainting-differential-diffusion)。

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

在开始之前，请确保已安装 [controlnet_aux](https://github.com/huggingface/controlnet_aux) 库，以便使用 ZoeDepth 估计器。

```py
!pip install -q controlnet_aux
```

## 图像准备

首先选择一张用于外扩绘制的图像，并使用 [BRIA-RMBG-1.4](https://hf.co/spaces/briaai/BRIA-RMBG-1.4) 等 Space 移除背景。

<iframe src="https://briaai-bria-rmbg-1-4.hf.space" frameborder="0" width="850" height="450"
>

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

  
    
    原始图像
  
  
    
    背景已移除
  

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

```py
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 估计器在生成过程中提供额外指导，以确保鞋子与原始图像保持一致。

```py
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](https://hf.co/destitech/controlnet-inpaint-dreamer-sdxl)（一个专为图像修复训练的 SDXL ControlNet）在鞋子周围的白色区域生成内容。

加载 inpainting ControlNet、ZoeDepth 模型和 VAE，并将它们传递给 [StableDiffusionXLControlNetPipeline](/docs/diffusers/v0.43.0/zh/api/pipelines/controlnet_sdxl#diffusers.StableDiffusionXLControlNetPipeline)。然后，你可以创建一个可选的 `generate_image` 函数（为了方便起见），用于对初始图像进行 outpaint。

```py
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)
```

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

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

    

> [!TIP]
> 如果您内存不足，现在是释放一些内存的好时机！
>
> ```py
> pipeline=None
> torch.cuda.empty_cache()
> ```

现在你已经获得了初步的外扩图像，请加载 [StableDiffusionXLInpaintPipeline](/docs/diffusers/v0.43.0/zh/api/pipelines/stable_diffusion/stable_diffusion_xl#diffusers.StableDiffusionXLInpaintPipeline) 并使用 [RealVisXL](https://hf.co/SG161222/RealVisXL_V4.0) 模型，以生成质量更高的最终外扩图像。

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

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

```py
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` 函数以生成最终的扩图图像。同样，将原始图像粘贴到最终的扩图背景上。

```py
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
```

    

