Spaces:
Running
on
Zero
A newer version of the Gradio SDK is available:
5.23.3
DiffEdit
[[open-in-colab]]
์ด๋ฏธ์ง ํธ์ง์ ํ๋ ค๋ฉด ์ผ๋ฐ์ ์ผ๋ก ํธ์งํ ์์ญ์ ๋ง์คํฌ๋ฅผ ์ ๊ณตํด์ผ ํฉ๋๋ค. DiffEdit๋ ํ ์คํธ ์ฟผ๋ฆฌ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ง์คํฌ๋ฅผ ์๋์ผ๋ก ์์ฑํ๋ฏ๋ก ์ด๋ฏธ์ง ํธ์ง ์ํํธ์จ์ด ์์ด๋ ๋ง์คํฌ๋ฅผ ๋ง๋ค๊ธฐ๊ฐ ์ ๋ฐ์ ์ผ๋ก ๋ ์ฌ์์ง๋๋ค. DiffEdit ์๊ณ ๋ฆฌ์ฆ์ ์ธ ๋จ๊ณ๋ก ์๋ํฉ๋๋ค:
- Diffusion ๋ชจ๋ธ์ด ์ผ๋ถ ์ฟผ๋ฆฌ ํ ์คํธ์ ์ฐธ์กฐ ํ ์คํธ๋ฅผ ์กฐ๊ฑด๋ถ๋ก ์ด๋ฏธ์ง์ ๋ ธ์ด์ฆ๋ฅผ ์ ๊ฑฐํ์ฌ ์ด๋ฏธ์ง์ ์ฌ๋ฌ ์์ญ์ ๋ํด ์๋ก ๋ค๋ฅธ ๋ ธ์ด์ฆ ์ถ์ ์น๋ฅผ ์์ฑํ๊ณ , ๊ทธ ์ฐจ์ด๋ฅผ ์ฌ์ฉํ์ฌ ์ฟผ๋ฆฌ ํ ์คํธ์ ์ผ์นํ๋๋ก ์ด๋ฏธ์ง์ ์ด๋ ์์ญ์ ๋ณ๊ฒฝํด์ผ ํ๋์ง ์๋ณํ๊ธฐ ์ํ ๋ง์คํฌ๋ฅผ ์ถ๋ก ํฉ๋๋ค.
- ์ ๋ ฅ ์ด๋ฏธ์ง๊ฐ DDIM์ ์ฌ์ฉํ์ฌ ์ ์ฌ ๊ณต๊ฐ์ผ๋ก ์ธ์ฝ๋ฉ๋ฉ๋๋ค.
- ๋ง์คํฌ ์ธ๋ถ์ ํฝ์ ์ด ์ ๋ ฅ ์ด๋ฏธ์ง์ ๋์ผํ๊ฒ ์ ์ง๋๋๋ก ๋ง์คํฌ๋ฅผ ๊ฐ์ด๋๋ก ์ฌ์ฉํ์ฌ ํ ์คํธ ์ฟผ๋ฆฌ์ ์กฐ๊ฑด์ด ์ง์ ๋ diffusion ๋ชจ๋ธ๋ก latents๋ฅผ ๋์ฝ๋ฉํฉ๋๋ค.
์ด ๊ฐ์ด๋์์๋ ๋ง์คํฌ๋ฅผ ์๋์ผ๋ก ๋ง๋ค์ง ์๊ณ DiffEdit๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ฏธ์ง๋ฅผ ํธ์งํ๋ ๋ฐฉ๋ฒ์ ์ค๋ช ํฉ๋๋ค.
์์ํ๊ธฐ ์ ์ ๋ค์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ค์น๋์ด ์๋์ง ํ์ธํ์ธ์:
# Colab์์ ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค์นํ๊ธฐ ์ํด ์ฃผ์์ ์ ์ธํ์ธ์
#!pip install -q diffusers transformers accelerate
[StableDiffusionDiffEditPipeline
]์๋ ์ด๋ฏธ์ง ๋ง์คํฌ์ ๋ถ๋ถ์ ์ผ๋ก ๋ฐ์ ๋ latents ์งํฉ์ด ํ์ํฉ๋๋ค. ์ด๋ฏธ์ง ๋ง์คํฌ๋ [~StableDiffusionDiffEditPipeline.generate_mask
] ํจ์์์ ์์ฑ๋๋ฉฐ, ๋ ๊ฐ์ ํ๋ผ๋ฏธํฐ์ธ source_prompt
์ target_prompt
๊ฐ ํฌํจ๋ฉ๋๋ค. ์ด ๋งค๊ฐ๋ณ์๋ ์ด๋ฏธ์ง์์ ๋ฌด์์ ํธ์งํ ์ง ๊ฒฐ์ ํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ๊ณผ์ผ ํ ๊ทธ๋ฆ์ ๋ฐฐ ํ ๊ทธ๋ฆ์ผ๋ก ๋ณ๊ฒฝํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ํ์ธ์:
source_prompt = "a bowl of fruits"
target_prompt = "a bowl of pears"
๋ถ๋ถ์ ์ผ๋ก ๋ฐ์ ๋ latents๋ [~StableDiffusionDiffEditPipeline.invert
] ํจ์์์ ์์ฑ๋๋ฉฐ, ์ผ๋ฐ์ ์ผ๋ก ์ด๋ฏธ์ง๋ฅผ ์ค๋ช
ํ๋ prompt
๋๋ ์บก์
์ ํฌํจํ๋ ๊ฒ์ด inverse latent sampling ํ๋ก์ธ์ค๋ฅผ ๊ฐ์ด๋ํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค. ์บก์
์ ์ข
์ข
source_prompt
๊ฐ ๋ ์ ์์ง๋ง, ๋ค๋ฅธ ํ
์คํธ ์ค๋ช
์ผ๋ก ์์ ๋กญ๊ฒ ์คํํด ๋ณด์ธ์!
ํ์ดํ๋ผ์ธ, ์ค์ผ์ค๋ฌ, ์ญ ์ค์ผ์ค๋ฌ๋ฅผ ๋ถ๋ฌ์ค๊ณ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ค์ด๊ธฐ ์ํด ๋ช ๊ฐ์ง ์ต์ ํ๋ฅผ ํ์ฑํํด ๋ณด๊ฒ ์ต๋๋ค:
import torch
from diffusers import DDIMScheduler, DDIMInverseScheduler, StableDiffusionDiffEditPipeline
pipeline = StableDiffusionDiffEditPipeline.from_pretrained(
"stabilityai/stable-diffusion-2-1",
torch_dtype=torch.float16,
safety_checker=None,
use_safetensors=True,
)
pipeline.scheduler = DDIMScheduler.from_config(pipeline.scheduler.config)
pipeline.inverse_scheduler = DDIMInverseScheduler.from_config(pipeline.scheduler.config)
pipeline.enable_model_cpu_offload()
pipeline.enable_vae_slicing()
์์ ํ๊ธฐ ์ํ ์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์ต๋๋ค:
from diffusers.utils import load_image, make_image_grid
img_url = "https://github.com/Xiang-cd/DiffEdit-stable-diffusion/raw/main/assets/origin.png"
raw_image = load_image(img_url).resize((768, 768))
raw_image
์ด๋ฏธ์ง ๋ง์คํฌ๋ฅผ ์์ฑํ๊ธฐ ์ํด [~StableDiffusionDiffEditPipeline.generate_mask
] ํจ์๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์ด๋ฏธ์ง์์ ํธ์งํ ๋ด์ฉ์ ์ง์ ํ๊ธฐ ์ํด source_prompt
์ target_prompt
๋ฅผ ์ ๋ฌํด์ผ ํฉ๋๋ค:
from PIL import Image
source_prompt = "a bowl of fruits"
target_prompt = "a basket of pears"
mask_image = pipeline.generate_mask(
image=raw_image,
source_prompt=source_prompt,
target_prompt=target_prompt,
)
Image.fromarray((mask_image.squeeze()*255).astype("uint8"), "L").resize((768, 768))
๋ค์์ผ๋ก, ๋ฐ์ ๋ latents๋ฅผ ์์ฑํ๊ณ ์ด๋ฏธ์ง๋ฅผ ๋ฌ์ฌํ๋ ์บก์ ์ ์ ๋ฌํฉ๋๋ค:
inv_latents = pipeline.invert(prompt=source_prompt, image=raw_image).latents
๋ง์ง๋ง์ผ๋ก, ์ด๋ฏธ์ง ๋ง์คํฌ์ ๋ฐ์ ๋ latents๋ฅผ ํ์ดํ๋ผ์ธ์ ์ ๋ฌํฉ๋๋ค. target_prompt
๋ ์ด์ prompt
๊ฐ ๋๋ฉฐ, source_prompt
๋ negative_prompt
๋ก ์ฌ์ฉ๋ฉ๋๋ค.
output_image = pipeline(
prompt=target_prompt,
mask_image=mask_image,
image_latents=inv_latents,
negative_prompt=source_prompt,
).images[0]
mask_image = Image.fromarray((mask_image.squeeze()*255).astype("uint8"), "L").resize((768, 768))
make_image_grid([raw_image, mask_image, output_image], rows=1, cols=3)


Source์ target ์๋ฒ ๋ฉ ์์ฑํ๊ธฐ
Source์ target ์๋ฒ ๋ฉ์ ์๋์ผ๋ก ์์ฑํ๋ ๋์ Flan-T5 ๋ชจ๋ธ์ ์ฌ์ฉํ์ฌ ์๋์ผ๋ก ์์ฑํ ์ ์์ต๋๋ค.
Flan-T5 ๋ชจ๋ธ๊ณผ ํ ํฌ๋์ด์ ๋ฅผ ๐ค Transformers ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ๋ถ๋ฌ์ต๋๋ค:
import torch
from transformers import AutoTokenizer, T5ForConditionalGeneration
tokenizer = AutoTokenizer.from_pretrained("google/flan-t5-large")
model = T5ForConditionalGeneration.from_pretrained("google/flan-t5-large", device_map="auto", torch_dtype=torch.float16)
๋ชจ๋ธ์ ํ๋กฌํํธํ source์ target ํ๋กฌํํธ๋ฅผ ์์ฑํ๊ธฐ ์ํด ์ด๊ธฐ ํ ์คํธ๋ค์ ์ ๊ณตํฉ๋๋ค.
source_concept = "bowl"
target_concept = "basket"
source_text = f"Provide a caption for images containing a {source_concept}. "
"The captions should be in English and should be no longer than 150 characters."
target_text = f"Provide a caption for images containing a {target_concept}. "
"The captions should be in English and should be no longer than 150 characters."
๋ค์์ผ๋ก, ํ๋กฌํํธ๋ค์ ์์ฑํ๊ธฐ ์ํด ์ ํธ๋ฆฌํฐ ํจ์๋ฅผ ์์ฑํฉ๋๋ค.
@torch.no_grad()
def generate_prompts(input_prompt):
input_ids = tokenizer(input_prompt, return_tensors="pt").input_ids.to("cuda")
outputs = model.generate(
input_ids, temperature=0.8, num_return_sequences=16, do_sample=True, max_new_tokens=128, top_k=10
)
return tokenizer.batch_decode(outputs, skip_special_tokens=True)
source_prompts = generate_prompts(source_text)
target_prompts = generate_prompts(target_text)
print(source_prompts)
print(target_prompts)
๋ค์ํ ํ์ง์ ํ ์คํธ๋ฅผ ์์ฑํ๋ ์ ๋ต์ ๋ํด ์์ธํ ์์๋ณด๋ ค๋ฉด ์์ฑ ์ ๋ต ๊ฐ์ด๋๋ฅผ ์ฐธ์กฐํ์ธ์.
ํ
์คํธ ์ธ์ฝ๋ฉ์ ์ํด [StableDiffusionDiffEditPipeline
]์์ ์ฌ์ฉํ๋ ํ
์คํธ ์ธ์ฝ๋ ๋ชจ๋ธ์ ๋ถ๋ฌ์ต๋๋ค. ํ
์คํธ ์ธ์ฝ๋๋ฅผ ์ฌ์ฉํ์ฌ ํ
์คํธ ์๋ฒ ๋ฉ์ ๊ณ์ฐํฉ๋๋ค:
import torch
from diffusers import StableDiffusionDiffEditPipeline
pipeline = StableDiffusionDiffEditPipeline.from_pretrained(
"stabilityai/stable-diffusion-2-1", torch_dtype=torch.float16, use_safetensors=True
)
pipeline.enable_model_cpu_offload()
pipeline.enable_vae_slicing()
@torch.no_grad()
def embed_prompts(sentences, tokenizer, text_encoder, device="cuda"):
embeddings = []
for sent in sentences:
text_inputs = tokenizer(
sent,
padding="max_length",
max_length=tokenizer.model_max_length,
truncation=True,
return_tensors="pt",
)
text_input_ids = text_inputs.input_ids
prompt_embeds = text_encoder(text_input_ids.to(device), attention_mask=None)[0]
embeddings.append(prompt_embeds)
return torch.concatenate(embeddings, dim=0).mean(dim=0).unsqueeze(0)
source_embeds = embed_prompts(source_prompts, pipeline.tokenizer, pipeline.text_encoder)
target_embeds = embed_prompts(target_prompts, pipeline.tokenizer, pipeline.text_encoder)
๋ง์ง๋ง์ผ๋ก, ์๋ฒ ๋ฉ์ [~StableDiffusionDiffEditPipeline.generate_mask
] ๋ฐ [~StableDiffusionDiffEditPipeline.invert
] ํจ์์ ํ์ดํ๋ผ์ธ์ ์ ๋ฌํ์ฌ ์ด๋ฏธ์ง๋ฅผ ์์ฑํฉ๋๋ค:
from diffusers import DDIMInverseScheduler, DDIMScheduler
from diffusers.utils import load_image, make_image_grid
from PIL import Image
pipeline.scheduler = DDIMScheduler.from_config(pipeline.scheduler.config)
pipeline.inverse_scheduler = DDIMInverseScheduler.from_config(pipeline.scheduler.config)
img_url = "https://github.com/Xiang-cd/DiffEdit-stable-diffusion/raw/main/assets/origin.png"
raw_image = load_image(img_url).resize((768, 768))
mask_image = pipeline.generate_mask(
image=raw_image,
- source_prompt=source_prompt,
- target_prompt=target_prompt,
+ source_prompt_embeds=source_embeds,
+ target_prompt_embeds=target_embeds,
)
inv_latents = pipeline.invert(
- prompt=source_prompt,
+ prompt_embeds=source_embeds,
image=raw_image,
).latents
output_image = pipeline(
mask_image=mask_image,
image_latents=inv_latents,
- prompt=target_prompt,
- negative_prompt=source_prompt,
+ prompt_embeds=target_embeds,
+ negative_prompt_embeds=source_embeds,
).images[0]
mask_image = Image.fromarray((mask_image.squeeze()*255).astype("uint8"), "L")
make_image_grid([raw_image, mask_image, output_image], rows=1, cols=3)
๋ฐ์ ์ ์ํ ์บก์ ์์ฑํ๊ธฐ
source_prompt
๋ฅผ ์บก์
์ผ๋ก ์ฌ์ฉํ์ฌ ๋ถ๋ถ์ ์ผ๋ก ๋ฐ์ ๋ latents๋ฅผ ์์ฑํ ์ ์์ง๋ง, BLIP ๋ชจ๋ธ์ ์ฌ์ฉํ์ฌ ์บก์
์ ์๋์ผ๋ก ์์ฑํ ์๋ ์์ต๋๋ค.
๐ค Transformers ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ BLIP ๋ชจ๋ธ๊ณผ ํ๋ก์ธ์๋ฅผ ๋ถ๋ฌ์ต๋๋ค:
import torch
from transformers import BlipForConditionalGeneration, BlipProcessor
processor = BlipProcessor.from_pretrained("Salesforce/blip-image-captioning-base")
model = BlipForConditionalGeneration.from_pretrained("Salesforce/blip-image-captioning-base", torch_dtype=torch.float16, low_cpu_mem_usage=True)
์ ๋ ฅ ์ด๋ฏธ์ง์์ ์บก์ ์ ์์ฑํ๋ ์ ํธ๋ฆฌํฐ ํจ์๋ฅผ ๋ง๋ญ๋๋ค:
@torch.no_grad()
def generate_caption(images, caption_generator, caption_processor):
text = "a photograph of"
inputs = caption_processor(images, text, return_tensors="pt").to(device="cuda", dtype=caption_generator.dtype)
caption_generator.to("cuda")
outputs = caption_generator.generate(**inputs, max_new_tokens=128)
# ์บก์
generator ์คํ๋ก๋
caption_generator.to("cpu")
caption = caption_processor.batch_decode(outputs, skip_special_tokens=True)[0]
return caption
์
๋ ฅ ์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์ค๊ณ generate_caption
ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ํด๋น ์ด๋ฏธ์ง์ ๋ํ ์บก์
์ ์์ฑํฉ๋๋ค:
from diffusers.utils import load_image
img_url = "https://github.com/Xiang-cd/DiffEdit-stable-diffusion/raw/main/assets/origin.png"
raw_image = load_image(img_url).resize((768, 768))
caption = generate_caption(raw_image, model, processor)

์ด์ ์บก์
์ [~StableDiffusionDiffEditPipeline.invert
] ํจ์์ ๋์ ๋ถ๋ถ์ ์ผ๋ก ๋ฐ์ ๋ latents๋ฅผ ์์ฑํ ์ ์์ต๋๋ค!