인페인팅

인페인팅 (Inpainting)

인페인팅(Inpainting)은 이미지의 특정 영역을 교체하거나 편집하는 기법이에요. 결함이나 아티팩트를 제거하는 이미지 복원, 또는 이미지 영역을 완전히 새로운 것으로 바꾸는 작업에 유용하죠. 인페인팅은 어떤 영역을 채울지 정하기 위해 마스크(mask)를 사용해요. 인페인팅할 영역은 흰색 픽셀로, 유지할 영역은 검은색 픽셀로 표현하죠. 흰색 픽셀은 프롬프트에 따라 채워집니다.

🤗 Diffusers에서 인페인팅은 이렇게 할 수 있어요:

  1. [AutoPipelineForInpainting] 클래스로 인페인팅 체크포인트를 로드합니다. 이 클래스는 체크포인트에 따라 로드할 적절한 파이프라인 클래스를 자동으로 감지해요:
import torch
from diffusers import AutoPipelineForInpainting
from diffusers.utils import load_image, make_image_grid

pipeline = AutoPipelineForInpainting.from_pretrained(
    "kandinsky-community/kandinsky-2-2-decoder-inpaint", dtype=torch.float16
)
pipeline.enable_model_cpu_offload()
# remove following line if xFormers is not installed or you have PyTorch 2.0 or higher installed
pipeline.enable_xformers_memory_efficient_attention()

[!TIP] 이 가이드 전체에서 메모리를 아끼고 추론 속도를 높이기 위해 [~DiffusionPipeline.enable_model_cpu_offload]와 [~DiffusionPipeline.enable_xformers_memory_efficient_attention]을 쓰는 걸 볼 수 있어요. PyTorch 2.0을 쓰고 있다면 파이프라인에서 [~DiffusionPipeline.enable_xformers_memory_efficient_attention]을 호출할 필요가 없어요. 이미 PyTorch 2.0 고유의 scaled-dot product attention을 쓰고 있기 때문이죠.

  1. 베이스 이미지와 마스크 이미지를 로드합니다:
init_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint.png")
mask_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint_mask.png")
  1. 인페인팅할 프롬프트를 만들고, 베이스 이미지와 마스크 이미지와 함께 파이프라인에 넘깁니다:
prompt = "a black cat with glowing eyes, cute, adorable, disney, pixar, highly detailed, 8k"
negative_prompt = "bad anatomy, deformed, ugly, disfigured"
image = pipeline(prompt=prompt, negative_prompt=negative_prompt, image=init_image, mask_image=mask_image).images[0]
make_image_grid([init_image, mask_image, image], rows=1, cols=3)
base image
mask image
generated image

마스크 이미지 만들기

이 가이드에서는 편의를 위해 모든 코드 예시에 마스크 이미지를 제공해요. 나만의 이미지에 인페인팅을 하고 싶다면 마스크 이미지를 직접 만들어야 해요. 아래 Space를 쓰면 마스크 이미지를 쉽게 만들 수 있습니다.

인페인팅할 베이스 이미지를 업로드하고 스케치 도구로 마스크를 그려 보세요. 끝나면 Run을 클릭해 마스크 이미지를 생성·다운로드할 수 있어요.

마스크 블러

[~VaeImageProcessor.blur] 메서드는 원본 이미지와 인페인팅 영역을 어떻게 블렌딩할지 정하는 옵션을 제공해요. 블러 양은 blur_factor 파라미터로 결정되죠. blur_factor를 높이면 마스크 가장자리에 적용되는 블러 양이 늘어나, 원본 이미지와 인페인팅 영역 사이의 전환이 부드러워져요. blur_factor가 낮거나 0이면 마스크의 날카로운 가장자리가 보존됩니다.

이걸 쓰려면 이미지 프로세서로 블러가 적용된 마스크를 만들면 돼요.

import torch
from diffusers import AutoPipelineForInpainting
from diffusers.utils import load_image
from PIL import Image

pipeline = AutoPipelineForInpainting.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5", dtype=torch.float16).to('cuda')  # or "mps", "xpu", "cpu"

mask = load_image("https://huggingface.co/datasets/YiYiXu/testing-images/resolve/main/seashore_mask.png")
blurred_mask = pipeline.mask_processor.blur(mask, blur_factor=33)
blurred_mask
mask with no blur
mask with blur applied

인기 있는 모델

인페인팅에서 가장 인기 있는 모델로는 Stable Diffusion Inpainting, Stable Diffusion XL (SDXL) Inpainting, Kandinsky 2.2 Inpainting이 있어요. SDXL은 보통 Stable Diffusion v1.5보다 고해상도 이미지를 만들고, Kandinsky 2.2도 고품질 이미지를 생성할 수 있어요.

Stable Diffusion Inpainting

Stable Diffusion Inpainting은 512x512 이미지에서 인페인팅으로 파인튜닝된 잠재 확산 모델이에요. 비교적 빠르고 좋은 품질의 이미지를 만들어서 시작점으로 아주 좋죠. 이 모델을 인페인팅에 쓰려면 파이프라인에 프롬프트, 베이스 이미지, 마스크 이미지를 넘겨야 해요:

import torch
from diffusers import AutoPipelineForInpainting
from diffusers.utils import load_image, make_image_grid

pipeline = AutoPipelineForInpainting.from_pretrained(
    "stable-diffusion-v1-5/stable-diffusion-inpainting", dtype=torch.float16, variant="fp16"
)
pipeline.enable_model_cpu_offload()
# remove following line if xFormers is not installed or you have PyTorch 2.0 or higher installed
pipeline.enable_xformers_memory_efficient_attention()

# load base and mask image
init_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint.png")
mask_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint_mask.png")

generator = torch.Generator("cuda").manual_seed(92)
prompt = "concept art digital painting of an elven castle, inspired by lord of the rings, highly detailed, 8k"
image = pipeline(prompt=prompt, image=init_image, mask_image=mask_image, generator=generator).images[0]
make_image_grid([init_image, mask_image, image], rows=1, cols=3)

Stable Diffusion XL (SDXL) Inpainting

SDXL은 Stable Diffusion v1.5의 더 크고 강력한 버전이에요. 이 모델은 2단계 모델 과정을 따를 수 있는데(각 모델을 단독으로 써도 됩니다), 베이스 모델이 이미지를 생성하고 리파이너(refiner) 모델이 그 이미지를 받아 디테일과 품질을 더욱 향상시켜요. SDXL 사용법과 파라미터 설정을 더 포괄적으로 보려면 SDXL 가이드를 확인해 보세요.

import torch
from diffusers import AutoPipelineForInpainting
from diffusers.utils import load_image, make_image_grid

pipeline = AutoPipelineForInpainting.from_pretrained(
    "diffusers/stable-diffusion-xl-1.0-inpainting-0.1", dtype=torch.float16, variant="fp16"
)
pipeline.enable_model_cpu_offload()
# remove following line if xFormers is not installed or you have PyTorch 2.0 or higher installed
pipeline.enable_xformers_memory_efficient_attention()

# load base and mask image
init_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint.png")
mask_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint_mask.png")

generator = torch.Generator("cuda").manual_seed(92)
prompt = "concept art digital painting of an elven castle, inspired by lord of the rings, highly detailed, 8k"
image = pipeline(prompt=prompt, image=init_image, mask_image=mask_image, generator=generator).images[0]
make_image_grid([init_image, mask_image, image], rows=1, cols=3)

Kandinsky 2.2 Inpainting

Kandinsky 모델군은 두 모델을 쓴다는 점에서 SDXL과 비슷해요. 이미지 프라이어(image prior) 모델이 이미지 임베딩을 만들고, 확산 모델이 그 임베딩으로 이미지를 생성하죠. 이미지 프라이어와 확산 모델을 각각 로드할 수도 있지만, Kandinsky 2.2를 쓰는 가장 쉬운 방법은 내부적으로 [KandinskyV22InpaintCombinedPipeline]을 사용하는 [AutoPipelineForInpainting] 클래스에 로드하는 거예요.

import torch
from diffusers import AutoPipelineForInpainting
from diffusers.utils import load_image, make_image_grid

pipeline = AutoPipelineForInpainting.from_pretrained(
    "kandinsky-community/kandinsky-2-2-decoder-inpaint", dtype=torch.float16
)
pipeline.enable_model_cpu_offload()
# remove following line if xFormers is not installed or you have PyTorch 2.0 or higher installed
pipeline.enable_xformers_memory_efficient_attention()

# load base and mask image
init_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint.png")
mask_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint_mask.png")

generator = torch.Generator("cuda").manual_seed(92)
prompt = "concept art digital painting of an elven castle, inspired by lord of the rings, highly detailed, 8k"
image = pipeline(prompt=prompt, image=init_image, mask_image=mask_image, generator=generator).images[0]
make_image_grid([init_image, mask_image, image], rows=1, cols=3)
base image
Stable Diffusion Inpainting
Stable Diffusion XL Inpainting
Kandinsky 2.2 Inpainting

인페인팅 전용이 아닌 체크포인트

지금까지 이 가이드에서는 stable-diffusion-v1-5/stable-diffusion-inpainting 같은 인페인팅 전용 체크포인트를 썼어요. 하지만 stable-diffusion-v1-5/stable-diffusion-v1-5 같은 일반 체크포인트도 쓸 수 있어요. 두 체크포인트의 결과를 비교해 볼게요.

왼쪽 이미지는 일반 체크포인트로 만든 결과고, 오른쪽 이미지는 인페인팅 체크포인트로 만든 결과예요. 왼쪽 이미지가 그렇게 깔끔하지 않고, 모델이 인페인팅해야 할 영역의 윤곽이 여전히 보이는 게 바로 눈에 띄죠. 오른쪽 이미지는 훨씬 깔끔하고 인페인팅된 영역이 더 자연스러워 보여요.

import torch
from diffusers import AutoPipelineForInpainting
from diffusers.utils import load_image, make_image_grid

pipeline = AutoPipelineForInpainting.from_pretrained(
    "stable-diffusion-v1-5/stable-diffusion-v1-5", dtype=torch.float16, variant="fp16"
)
pipeline.enable_model_cpu_offload()
# remove following line if xFormers is not installed or you have PyTorch 2.0 or higher installed
pipeline.enable_xformers_memory_efficient_attention()

# load base and mask image
init_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint.png")
mask_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint_mask.png")

generator = torch.Generator("cuda").manual_seed(92)
prompt = "concept art digital painting of an elven castle, inspired by lord of the rings, highly detailed, 8k"
image = pipeline(prompt=prompt, image=init_image, mask_image=mask_image, generator=generator).images[0]
make_image_grid([init_image, image], rows=1, cols=2)
import torch
from diffusers import AutoPipelineForInpainting
from diffusers.utils import load_image, make_image_grid

pipeline = AutoPipelineForInpainting.from_pretrained(
    "stable-diffusion-v1-5/stable-diffusion-inpainting", dtype=torch.float16, variant="fp16"
)
pipeline.enable_model_cpu_offload()
# remove following line if xFormers is not installed or you have PyTorch 2.0 or higher installed
pipeline.enable_xformers_memory_efficient_attention()

# load base and mask image
init_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint.png")
mask_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint_mask.png")

generator = torch.Generator("cuda").manual_seed(92)
prompt = "concept art digital painting of an elven castle, inspired by lord of the rings, highly detailed, 8k"
image = pipeline(prompt=prompt, image=init_image, mask_image=mask_image, generator=generator).images[0]
make_image_grid([init_image, image], rows=1, cols=2)
stable-diffusion-v1-5/stable-diffusion-v1-5
stable-diffusion-v1-5/stable-diffusion-inpainting

하지만 이미지에서 물체를 지우는(예: 도로 위 바위) 같은 더 기본적인 작업에서는 일반 체크포인트도 꽤 좋은 결과를 냅니다. 일반 체크포인트와 인페인팅 체크포인트 사이에 눈에 띄는 차이가 없죠.

import torch
from diffusers import AutoPipelineForInpainting
from diffusers.utils import load_image, make_image_grid

pipeline = AutoPipelineForInpainting.from_pretrained(
    "stable-diffusion-v1-5/stable-diffusion-v1-5", dtype=torch.float16, variant="fp16"
)
pipeline.enable_model_cpu_offload()
# remove following line if xFormers is not installed or you have PyTorch 2.0 or higher installed
pipeline.enable_xformers_memory_efficient_attention()

# load base and mask image
init_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint.png")
mask_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/road-mask.png")

image = pipeline(prompt="road", image=init_image, mask_image=mask_image).images[0]
make_image_grid([init_image, image], rows=1, cols=2)
import torch
from diffusers import AutoPipelineForInpainting
from diffusers.utils import load_image, make_image_grid

pipeline = AutoPipelineForInpainting.from_pretrained(
    "stable-diffusion-v1-5/stable-diffusion-inpainting", dtype=torch.float16, variant="fp16"
)
pipeline.enable_model_cpu_offload()
# remove following line if xFormers is not installed or you have PyTorch 2.0 or higher installed
pipeline.enable_xformers_memory_efficient_attention()

# load base and mask image
init_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint.png")
mask_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/road-mask.png")

image = pipeline(prompt="road", image=init_image, mask_image=mask_image).images[0]
make_image_grid([init_image, image], rows=1, cols=2)
stable-diffusion-v1-5/stable-diffusion-v1-5
stable-diffusion-v1-5/stable-diffusion-inpainting

인페인팅 전용이 아닌 체크포인트를 쓸 때의 트레이드오프는 전체 이미지 품질이 낮아질 수 있다는 점이지만, 보통 마스크 영역을 보존하는 경향이 있어요(그래서 마스크 윤곽이 보이는 거죠). 인페인팅 전용 체크포인트는 더 높은 품질의 인페인팅 이미지를 생성하도록 의도적으로 훈련됐는데, 여기에는 마스크 영역과 마스크 밖 영역 사이의 더 자연스러운 전이를 만드는 것도 포함돼요. 그 결과 이 체크포인트들은 마스크 밖 영역을 바꿀 가능성이 더 높습니다.

작업에서 마스크 밖 영역을 보존하는 게 중요하다면, [VaeImageProcessor.apply_overlay] 메서드로 이미지의 마스크 밖 영역이 그대로 유지되도록 강제할 수 있어요. 대신 마스크 영역과 마스크 밖 영역 사이의 전환이 조금 더 부자연스러워질 수 있지만요.

import PIL
import numpy as np
import torch

from diffusers import AutoPipelineForInpainting
from diffusers.utils import load_image, make_image_grid

device = "cuda"  # or "mps", "xpu", "cpu"
pipeline = AutoPipelineForInpainting.from_pretrained(
    "stable-diffusion-v1-5/stable-diffusion-inpainting",
    dtype=torch.float16,
    variant="fp16"
)
pipeline = pipeline.to(device)

img_url = "https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo.png"
mask_url = "https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo_mask.png"

init_image = load_image(img_url).resize((512, 512))
mask_image = load_image(mask_url).resize((512, 512))

prompt = "Face of a yellow cat, high resolution, sitting on a park bench"
repainted_image = pipeline(prompt=prompt, image=init_image, mask_image=mask_image).images[0]
repainted_image.save("repainted_image.png")

unmasked_unchanged_image = pipeline.image_processor.apply_overlay(mask_image, init_image, repainted_image)
unmasked_unchanged_image.save("force_unmasked_unchanged.png")
make_image_grid([init_image, mask_image, repainted_image, unmasked_unchanged_image], rows=2, cols=2)

파이프라인 파라미터 설정

이미지의 품질·'창의력' 같은 특징은 파이프라인 파라미터에 달려 있어요. 원하는 결과를 얻으려면 이 파라미터들이 무엇을 하는지 아는 게 중요하죠. 가장 중요한 파라미터들을 살펴보고, 값을 바꾸면 출력이 어떻게 달라지는지 볼게요.

Strength

strength는 베이스 이미지에 얼마나 많은 노이즈를 더할지 나타내는 척도로, 출력이 베이스 이미지와 얼마나 비슷한지에 영향을 줘요.

  • 📈 strength 값이 높으면 이미지에 더 많은 노이즈가 더해지고 디노이징 과정이 더 오래 걸리지만, 베이스 이미지와 더 다른 고품질 이미지를 얻을 수 있어요
  • 📉 strength 값이 낮으면 이미지에 더 적은 노이즈가 더해지고 디노이징 과정이 더 빠르지만, 이미지 품질이 그만큼 좋지는 않고 생성 이미지가 베이스 이미지와 더 비슷해져요
import torch
from diffusers import AutoPipelineForInpainting
from diffusers.utils import load_image, make_image_grid

pipeline = AutoPipelineForInpainting.from_pretrained(
    "stable-diffusion-v1-5/stable-diffusion-inpainting", dtype=torch.float16, variant="fp16"
)
pipeline.enable_model_cpu_offload()
# remove following line if xFormers is not installed or you have PyTorch 2.0 or higher installed
pipeline.enable_xformers_memory_efficient_attention()

# load base and mask image
init_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint.png")
mask_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint_mask.png")

prompt = "concept art digital painting of an elven castle, inspired by lord of the rings, highly detailed, 8k"
image = pipeline(prompt=prompt, image=init_image, mask_image=mask_image, strength=0.6).images[0]
make_image_grid([init_image, mask_image, image], rows=1, cols=3)
strength = 0.6
strength = 0.8
strength = 1.0

가이던스 스케일

guidance_scale은 텍스트 프롬프트와 생성 이미지가 얼마나 잘 정렬되는지에 영향을 줘요.

  • 📈 guidance_scale 값이 높으면 프롬프트와 생성 이미지가 밀접하게 정렬되어, 출력이 프롬프트를 더 엄격하게 해석한 결과가 돼요
  • 📉 guidance_scale 값이 낮으면 프롬프트와 생성 이미지가 느슨하게 정렬되어, 출력이 프롬프트에서 더 다양해질 수 있어요

strengthguidance_scale을 함께 쓰면 모델이 얼마나 표현력 있게 동작할지 더 제어할 수 있어요. 예를 들어 높은 strengthguidance_scale 값을 조합하면 모델에게 가장 큰 창의적 자유를 줍니다.

import torch
from diffusers import AutoPipelineForInpainting
from diffusers.utils import load_image, make_image_grid

pipeline = AutoPipelineForInpainting.from_pretrained(
    "stable-diffusion-v1-5/stable-diffusion-inpainting", dtype=torch.float16, variant="fp16"
)
pipeline.enable_model_cpu_offload()
# remove following line if xFormers is not installed or you have PyTorch 2.0 or higher installed
pipeline.enable_xformers_memory_efficient_attention()

# load base and mask image
init_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint.png")
mask_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint_mask.png")

prompt = "concept art digital painting of an elven castle, inspired by lord of the rings, highly detailed, 8k"
image = pipeline(prompt=prompt, image=init_image, mask_image=mask_image, guidance_scale=2.5).images[0]
make_image_grid([init_image, mask_image, image], rows=1, cols=3)
guidance_scale = 2.5
guidance_scale = 7.5
guidance_scale = 12.5

네거티브 프롬프트

네거티브 프롬프트는 프롬프트와 반대 역할을 해요. 모델이 이미지에서 특정 것을 생성하지 않도록 이끌죠. 이미지 품질을 빠르게 높이고, 원치 않는 걸 생성하지 않도록 막는 데 유용합니다.

import torch
from diffusers import AutoPipelineForInpainting
from diffusers.utils import load_image, make_image_grid

pipeline = AutoPipelineForInpainting.from_pretrained(
    "stable-diffusion-v1-5/stable-diffusion-inpainting", dtype=torch.float16, variant="fp16"
)
pipeline.enable_model_cpu_offload()
# remove following line if xFormers is not installed or you have PyTorch 2.0 or higher installed
pipeline.enable_xformers_memory_efficient_attention()

# load base and mask image
init_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint.png")
mask_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint_mask.png")

prompt = "concept art digital painting of an elven castle, inspired by lord of the rings, highly detailed, 8k"
negative_prompt = "bad architecture, unstable, poor details, blurry"
image = pipeline(prompt=prompt, negative_prompt=negative_prompt, image=init_image, mask_image=mask_image).images[0]
make_image_grid([init_image, mask_image, image], rows=1, cols=3)
negative_prompt = "bad architecture, unstable, poor details, blurry"

패딩 마스크 크롭

인페인팅 이미지 품질을 높이는 방법 중 하나로 padding_mask_crop 파라미터를 쓰는 걸 들 수 있어요. 이 옵션을 켜면 사용자가 지정한 패딩과 함께 마스크 영역을 크롭하고, 원본 이미지에서도 같은 영역을 크롭해요. 이미지와 마스크 모두 인페인팅을 위해 더 높은 해상도로 업스케일된 뒤 원본 이미지 위에 오버레이됩니다. [StableDiffusionUpscalePipeline] 같은 별도 파이프라인 없이 이미지 품질을 빠르고 쉽게 높이는 방법이죠.

파이프라인 호출에 padding_mask_crop 파라미터를 추가하고 원하는 패딩 값으로 설정하면 됩니다.

import torch
from diffusers import AutoPipelineForInpainting
from diffusers.utils import load_image
from PIL import Image

generator = torch.Generator(device='cuda').manual_seed(0)  # or "mps", "xpu", "cpu"
pipeline = AutoPipelineForInpainting.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5", dtype=torch.float16).to('cuda')

base = load_image("https://huggingface.co/datasets/YiYiXu/testing-images/resolve/main/seashore.png")
mask = load_image("https://huggingface.co/datasets/YiYiXu/testing-images/resolve/main/seashore_mask.png")

image = pipeline("boat", image=base, mask_image=mask, strength=0.75, generator=generator, padding_mask_crop=32).images[0]
image
default inpaint image
inpaint image with `padding_mask_crop` enabled

인페인팅 파이프라인 연결하기

[AutoPipelineForInpainting]은 다른 🤗 Diffusers 파이프라인과 연결해서 그 출력을 편집할 수 있어요. 다른 확산 파이프라인의 출력 품질을 높이는 데 자주 유용하고, 여러 파이프라인을 쓴다면 출력을 잠재 공간에 유지하고 같은 파이프라인 구성 요소를 재사용할 수 있어 메모리 효율적이기도 하죠.

텍스트-이미지-인페인트

텍스트-이미지 파이프라인과 인페인팅 파이프라인을 연결하면 생성된 이미지에 인페인팅을 할 수 있고, 처음부터 베이스 이미지를 제공할 필요도 없어요. 완전히 새로운 이미지를 생성하지 않고도 좋아하는 텍스트-이미지 출력을 편집하기 편리하죠.

텍스트-이미지 파이프라인으로 성(castle)부터 만듭니다:

import torch
from diffusers import AutoPipelineForText2Image, AutoPipelineForInpainting
from diffusers.utils import load_image, make_image_grid

pipeline = AutoPipelineForText2Image.from_pretrained(
    "stable-diffusion-v1-5/stable-diffusion-v1-5", dtype=torch.float16, variant="fp16", use_safetensors=True
)
pipeline.enable_model_cpu_offload()
# remove following line if xFormers is not installed or you have PyTorch 2.0 or higher installed
pipeline.enable_xformers_memory_efficient_attention()

text2image = pipeline("concept art digital painting of an elven castle, inspired by lord of the rings, highly detailed, 8k").images[0]

위 출력의 마스크 이미지를 로드합니다:

mask_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint_text-chain-mask.png")

이제 마스크된 영역을 폭포로 인페인팅해 볼게요:

pipeline = AutoPipelineForInpainting.from_pretrained(
    "kandinsky-community/kandinsky-2-2-decoder-inpaint", dtype=torch.float16
)
pipeline.enable_model_cpu_offload()
# remove following line if xFormers is not installed or you have PyTorch 2.0 or higher installed
pipeline.enable_xformers_memory_efficient_attention()

prompt = "digital painting of a fantasy waterfall, cloudy"
image = pipeline(prompt=prompt, image=text2image, mask_image=mask_image).images[0]
make_image_grid([text2image, mask_image, image], rows=1, cols=3)
text-to-image
inpaint

인페인트-이미지-이미지

인페인팅 파이프라인을 이미지-이미지나 업스케일러 같은 다른 파이프라인 앞에 연결해서 품질을 높일 수도 있어요.

이미지 인페인팅부터 시작합니다:

import torch
from diffusers import AutoPipelineForInpainting, AutoPipelineForImage2Image
from diffusers.utils import load_image, make_image_grid

pipeline = AutoPipelineForInpainting.from_pretrained(
    "stable-diffusion-v1-5/stable-diffusion-inpainting", dtype=torch.float16, variant="fp16"
)
pipeline.enable_model_cpu_offload()
# remove following line if xFormers is not installed or you have PyTorch 2.0 or higher installed
pipeline.enable_xformers_memory_efficient_attention()

# load base and mask image
init_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint.png")
mask_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint_mask.png")

prompt = "concept art digital painting of an elven castle, inspired by lord of the rings, highly detailed, 8k"
image_inpainting = pipeline(prompt=prompt, image=init_image, mask_image=mask_image).images[0]

# resize image to 1024x1024 for SDXL
image_inpainting = image_inpainting.resize((1024, 1024))

이제 SDXL의 리파이너 모델이 있는 다른 인페인팅 파이프라인에 이미지를 넘겨 디테일과 품질을 높여 볼게요:

pipeline = AutoPipelineForInpainting.from_pretrained(
    "stabilityai/stable-diffusion-xl-refiner-1.0", dtype=torch.float16, variant="fp16"
)
pipeline.enable_model_cpu_offload()
# remove following line if xFormers is not installed or you have PyTorch 2.0 or higher installed
pipeline.enable_xformers_memory_efficient_attention()

image = pipeline(prompt=prompt, image=image_inpainting, mask_image=mask_image, output_type="latent").images[0]

[!TIP] 파이프라인에서 output_type="latent"을 지정하는 게 중요해요. 출력 전부를 잠재 공간에 유지해서 불필요한 디코드-인코드 단계를 피할 수 있거든요. 단, 연결하는 파이프라인들이 같은 VAE를 쓸 때만 동작합니다. 예를 들어 Text-to-image-to-inpaint 섹션에서 Kandinsky 2.2는 Stable Diffusion 모델과 다른 VAE 클래스를 쓰기 때문에 동작하지 않아요. 하지만 두 파이프라인 모두 Stable Diffusion v1.5를 쓴다면 둘 다 [AutoencoderKL]을 쓰기 때문에 전부 잠재 공간에서 유지할 수 있어요.

마지막으로 이 이미지를 이미지-이미지 파이프라인에 넘겨 마무리를 할 수 있어요. [~AutoPipelineForImage2Image.from_pipe] 메서드로 기존 파이프라인 구성 요소를 재사용하면 모든 파이프라인 구성 요소를 다시 메모리에 불러오는 낭비를 피할 수 있어 더 효율적이죠.

pipeline = AutoPipelineForImage2Image.from_pipe(pipeline)
# remove following line if xFormers is not installed or you have PyTorch 2.0 or higher installed
pipeline.enable_xformers_memory_efficient_attention()

image = pipeline(prompt=prompt, image=image).images[0]
make_image_grid([init_image, mask_image, image_inpainting, image], rows=2, cols=2)
initial image
inpaint
image-to-image

이미지-이미지 생성과 인페인팅은 사실 굉장히 비슷한 작업이에요. 이미지-이미지 생성은 제공된 기존 이미지와 비슷한 새 이미지를 만들고, 인페인팅도 같은 일을 하지만 마스크가 정의한 영역만 변환하고 나머지 이미지는 그대로 두죠. 인페인팅을 특정 변경을 가하는 더 정밀한 도구로 볼 수 있고, 이미지-이미지 생성은 더 광범위한 변경을 가하는 넓은 범위의 도구로 볼 수 있어요.

이미지 생성 제어하기

디노이징 과정이 무작위라서 이미지를 내가 원하는 그대로 만드는 건 까다로워요. negative_prompt 같은 파라미터를 설정해 생성을 부분적으로 제어할 수 있지만, 이미지 생성을 제어하는 더 좋고 효율적인 방법도 있습니다.

프롬프트 가중치

프롬프트 가중치는 프롬프트 안 개념의 표현을 정량적으로 확대·축소하는 방법을 제공해요. 프롬프트 안 각 개념의 텍스트 임베딩 벡터 크기를 높이거나 낮추는 데 쓸 수 있고, 그 결과로 각 개념이 얼마나 생성될지가 결정되죠. Compel 라이브러리는 프롬프트 가중치를 스케일하고 임베딩을 생성하는 직관적인 문법을 제공해요. 임베딩을 만드는 방법은 Prompt weighting 가이드에서 배울 수 있습니다.

임베딩을 만들고 나면 [AutoPipelineForInpainting]의 prompt_embeds(네거티브 프롬프트를 쓴다면 negative_prompt_embeds) 파라미터로 넘겨 주세요. 임베딩이 prompt 파라미터를 대체합니다:

import torch
from diffusers import AutoPipelineForInpainting
from diffusers.utils import make_image_grid

pipeline = AutoPipelineForInpainting.from_pretrained(
    "stable-diffusion-v1-5/stable-diffusion-inpainting", dtype=torch.float16,
)
pipeline.enable_model_cpu_offload()
# remove following line if xFormers is not installed or you have PyTorch 2.0 or higher installed
pipeline.enable_xformers_memory_efficient_attention()

image = pipeline(prompt_embeds=prompt_embeds, # generated from Compel
    negative_prompt_embeds=negative_prompt_embeds, # generated from Compel
    image=init_image,
    mask_image=mask_image
).images[0]
make_image_grid([init_image, mask_image, image], rows=1, cols=3)

ControlNet

ControlNet 모델은 Stable Diffusion 같은 다른 확산 모델과 함께 쓰이며, 이미지가 생성되는 방식을 더 유연하고 정확하게 제어하게 해 줘요. ControlNet은 추가적인 컨디셔닝 이미지 입력을 받아, 확산 모델이 그 안의 특징을 보존하도록 안내합니다.

예를 들어 인페인팅 이미지로 사전 훈련된 ControlNet으로 이미지를 컨디셔닝해 볼게요:

import torch
import numpy as np
from diffusers import ControlNetModel, StableDiffusionControlNetInpaintPipeline
from diffusers.utils import load_image, make_image_grid

# load ControlNet
controlnet = ControlNetModel.from_pretrained("lllyasviel/control_v11p_sd15_inpaint", dtype=torch.float16, variant="fp16")

# pass ControlNet to the pipeline
pipeline = StableDiffusionControlNetInpaintPipeline.from_pretrained(
    "stable-diffusion-v1-5/stable-diffusion-inpainting", controlnet=controlnet, dtype=torch.float16, variant="fp16"
)
pipeline.enable_model_cpu_offload()
# remove following line if xFormers is not installed or you have PyTorch 2.0 or higher installed
pipeline.enable_xformers_memory_efficient_attention()

# load base and mask image
init_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint.png")
mask_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/inpaint_mask.png")

# prepare control image
def make_inpaint_condition(init_image, mask_image):
    init_image = np.array(init_image.convert("RGB")).astype(np.float32) / 255.0
    mask_image = np.array(mask_image.convert("L")).astype(np.float32) / 255.0

    assert init_image.shape[0:1] == mask_image.shape[0:1], "image and image_mask must have the same image size"
    init_image[mask_image > 0.5] = -1.0  # set as masked pixel
    init_image = np.expand_dims(init_image, 0).transpose(0, 3, 1, 2)
    init_image = torch.from_numpy(init_image)
    return init_image

control_image = make_inpaint_condition(init_image, mask_image)

이제 베이스, 마스크, 컨트롤 이미지로 이미지를 생성합니다. 베이스 이미지의 특징이 생성 이미지에 강하게 보존되는 걸 볼 수 있을 거예요.

prompt = "concept art digital painting of an elven castle, inspired by lord of the rings, highly detailed, 8k"
image = pipeline(prompt=prompt, image=init_image, mask_image=mask_image, control_image=control_image).images[0]
make_image_grid([init_image, mask_image, PIL.Image.fromarray(np.uint8(control_image[0][0])).convert('RGB'), image], rows=2, cols=2)

한 단계 더 나아가 이미지-이미지 파이프라인과 연결해 새 스타일을 적용할 수 있어요:

from diffusers import AutoPipelineForImage2Image

pipeline = AutoPipelineForImage2Image.from_pretrained(
    "nitrosocke/elden-ring-diffusion", dtype=torch.float16,
)
pipeline.enable_model_cpu_offload()
# remove following line if xFormers is not installed or you have PyTorch 2.0 or higher installed
pipeline.enable_xformers_memory_efficient_attention()

prompt = "elden ring style castle" # include the token "elden ring style" in the prompt
negative_prompt = "bad architecture, deformed, disfigured, poor details"

image_elden_ring = pipeline(prompt, negative_prompt=negative_prompt, image=image).images[0]
make_image_grid([init_image, mask_image, image, image_elden_ring], rows=2, cols=2)
initial image
ControlNet inpaint
image-to-image

최적화

리소스가 제한된 환경에서 확산 모델을 실행하는 건 어렵고 느릴 수 있지만, 몇 가지 최적화 트릭이면 꼭 그렇지만은 않아요. 켤 수 있는 가장 큰(그리고 가장 쉬운) 최적화 중 하나는 메모리 효율적인 어텐션으로 전환하는 거예요. PyTorch 2.0을 쓰면 scaled-dot product attention이 자동으로 활성화돼서 추가로 할 일이 없어요. PyTorch 2.0이 아닌 사용자는 xFormers의 메모리 효율적 어텐션 구현을 설치해 쓸 수 있어요. 두 옵션 모두 메모리 사용량을 줄이고 추론을 가속화합니다.

모델을 CPU로 오프로드하면 메모리를 더 아낄 수도 있어요:

+ pipeline.enable_xformers_memory_efficient_attention()
+ pipeline.enable_model_cpu_offload()

추론 코드를 더 빠르게 만들려면 torch_compile을 쓰세요. 파이프라인에서 가장 부하가 큰 구성 요소, 보통은 UNet을 torch.compile로 감싸면 됩니다:

pipeline.unet = torch.compile(pipeline.unet, mode="reduce-overhead", fullgraph=True)

자세한 내용은 Reduce memory usageAccelerate inference 가이드에서 확인할 수 있어요.

더 알아보기 (Learn more)