diffusers/community-pipelines-mirror
Community Pipeline Examples For more information about community pipelines, please have a look at this issue. Community pipeline examples consist pipelines that have been added by the community. Please have a look at the following tables to get an overview of all community examples. Click on the Code Example to get a copy-and-paste ready code example that you can try out. If a community pipeline doesn't work as expected, please open an issue and ping the author on it. Please… See the full description on the dataset page: https://huggingface.co/datasets/diffusers/community-pipelines-mirror.
922k
1import inspect2from typing import List, Optional, Union3 4import numpy as np5import PIL.Image6import torch7from torch import nn8from torch.nn import functional as F9from torchvision import transforms10from transformers import CLIPFeatureExtractor, CLIPModel, CLIPTextModel, CLIPTokenizer11 12from diffusers import (13 AutoencoderKL,14 DDIMScheduler,15 DPMSolverMultistepScheduler,16 LMSDiscreteScheduler,17 PNDMScheduler,18 UNet2DConditionModel,19)20from diffusers.pipelines.pipeline_utils import DiffusionPipeline, StableDiffusionMixin21from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion import StableDiffusionPipelineOutput22from diffusers.utils import PIL_INTERPOLATION, deprecate23from diffusers.utils.torch_utils import randn_tensor24 25 26EXAMPLE_DOC_STRING = """27 Examples:28 ```py29 from io import BytesIO30 31 import requests32 import torch33 from diffusers import DiffusionPipeline34 from PIL import Image35 from transformers import CLIPFeatureExtractor, CLIPModel36 37 feature_extractor = CLIPFeatureExtractor.from_pretrained(38 "laion/CLIP-ViT-B-32-laion2B-s34B-b79K"39 )40 clip_model = CLIPModel.from_pretrained(41 "laion/CLIP-ViT-B-32-laion2B-s34B-b79K", torch_dtype=torch.float1642 )43 44 45 guided_pipeline = DiffusionPipeline.from_pretrained(46 "CompVis/stable-diffusion-v1-4",47 # custom_pipeline="clip_guided_stable_diffusion",48 custom_pipeline="/home/njindal/diffusers/examples/community/clip_guided_stable_diffusion.py",49 clip_model=clip_model,50 feature_extractor=feature_extractor,51 torch_dtype=torch.float16,52 )53 guided_pipeline.enable_attention_slicing()54 guided_pipeline = guided_pipeline.to("cuda")55 56 prompt = "fantasy book cover, full moon, fantasy forest landscape, golden vector elements, fantasy magic, dark light night, intricate, elegant, sharp focus, illustration, highly detailed, digital painting, concept art, matte, art by WLOP and Artgerm and Albert Bierstadt, masterpiece"57 58 url = "https://raw.githubusercontent.com/CompVis/stable-diffusion/main/assets/stable-samples/img2img/sketch-mountains-input.jpg"59 60 response = requests.get(url)61 init_image = Image.open(BytesIO(response.content)).convert("RGB")62 63 image = guided_pipeline(64 prompt=prompt,65 num_inference_steps=30,66 image=init_image,67 strength=0.75,68 guidance_scale=7.5,69 clip_guidance_scale=100,70 num_cutouts=4,71 use_cutouts=False,72 ).images[0]73 display(image)74 ```75"""76 77 78def preprocess(image, w, h):79 if isinstance(image, torch.Tensor):80 return image81 elif isinstance(image, PIL.Image.Image):82 image = [image]83 84 if isinstance(image[0], PIL.Image.Image):85 image = [np.array(i.resize((w, h), resample=PIL_INTERPOLATION["lanczos"]))[None, :] for i in image]86 image = np.concatenate(image, axis=0)87 image = np.array(image).astype(np.float32) / 255.088 image = image.transpose(0, 3, 1, 2)89 image = 2.0 * image - 1.090 image = torch.from_numpy(image)91 elif isinstance(image[0], torch.Tensor):92 image = torch.cat(image, dim=0)93 return image94 95 96class MakeCutouts(nn.Module):97 def __init__(self, cut_size, cut_power=1.0):98 super().__init__()99 100 self.cut_size = cut_size101 self.cut_power = cut_power102 103 def forward(self, pixel_values, num_cutouts):104 sideY, sideX = pixel_values.shape[2:4]105 max_size = min(sideX, sideY)106 min_size = min(sideX, sideY, self.cut_size)107 cutouts = []108 for _ in range(num_cutouts):109 size = int(torch.rand([]) ** self.cut_power * (max_size - min_size) + min_size)110 offsetx = torch.randint(0, sideX - size + 1, ())111 offsety = torch.randint(0, sideY - size + 1, ())112 cutout = pixel_values[:, :, offsety : offsety + size, offsetx : offsetx + size]113 cutouts.append(F.adaptive_avg_pool2d(cutout, self.cut_size))114 return torch.cat(cutouts)115 116 117def spherical_dist_loss(x, y):118 x = F.normalize(x, dim=-1)119 y = F.normalize(y, dim=-1)120 return (x - y).norm(dim=-1).div(2).arcsin().pow(2).mul(2)121 122 123def set_requires_grad(model, value):124 for param in model.parameters():125 param.requires_grad = value126 127 128class CLIPGuidedStableDiffusion(DiffusionPipeline, StableDiffusionMixin):129 """CLIP guided stable diffusion based on the amazing repo by @crowsonkb and @Jack000130 - https://github.com/Jack000/glid-3-xl131 - https://github.dev/crowsonkb/k-diffusion132 """133 134 def __init__(135 self,136 vae: AutoencoderKL,137 text_encoder: CLIPTextModel,138 clip_model: CLIPModel,139 tokenizer: CLIPTokenizer,140 unet: UNet2DConditionModel,141 scheduler: Union[PNDMScheduler, LMSDiscreteScheduler, DDIMScheduler, DPMSolverMultistepScheduler],142 feature_extractor: CLIPFeatureExtractor,143 ):144 super().__init__()145 self.register_modules(146 vae=vae,147 text_encoder=text_encoder,148 clip_model=clip_model,149 tokenizer=tokenizer,150 unet=unet,151 scheduler=scheduler,152 feature_extractor=feature_extractor,153 )154 155 self.normalize = transforms.Normalize(mean=feature_extractor.image_mean, std=feature_extractor.image_std)156 self.cut_out_size = (157 feature_extractor.size158 if isinstance(feature_extractor.size, int)159 else feature_extractor.size["shortest_edge"]160 )161 self.make_cutouts = MakeCutouts(self.cut_out_size)162 163 set_requires_grad(self.text_encoder, False)164 set_requires_grad(self.clip_model, False)165 166 def freeze_vae(self):167 set_requires_grad(self.vae, False)168 169 def unfreeze_vae(self):170 set_requires_grad(self.vae, True)171 172 def freeze_unet(self):173 set_requires_grad(self.unet, False)174 175 def unfreeze_unet(self):176 set_requires_grad(self.unet, True)177 178 def get_timesteps(self, num_inference_steps, strength, device):179 # get the original timestep using init_timestep180 init_timestep = min(int(num_inference_steps * strength), num_inference_steps)181 182 t_start = max(num_inference_steps - init_timestep, 0)183 timesteps = self.scheduler.timesteps[t_start:]184 185 return timesteps, num_inference_steps - t_start186 187 def prepare_latents(self, image, timestep, batch_size, num_images_per_prompt, dtype, device, generator=None):188 if not isinstance(image, (torch.Tensor, PIL.Image.Image, list)):189 raise ValueError(190 f"`image` has to be of type `torch.Tensor`, `PIL.Image.Image` or list but is {type(image)}"191 )192 193 image = image.to(device=device, dtype=dtype)194 195 batch_size = batch_size * num_images_per_prompt196 if isinstance(generator, list) and len(generator) != batch_size:197 raise ValueError(198 f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"199 f" size of {batch_size}. Make sure the batch size matches the length of the generators."200 )201 202 if isinstance(generator, list):203 init_latents = [204 self.vae.encode(image[i : i + 1]).latent_dist.sample(generator[i]) for i in range(batch_size)205 ]206 init_latents = torch.cat(init_latents, dim=0)207 else:208 init_latents = self.vae.encode(image).latent_dist.sample(generator)209 210 init_latents = self.vae.config.scaling_factor * init_latents211 212 if batch_size > init_latents.shape[0] and batch_size % init_latents.shape[0] == 0:213 # expand init_latents for batch_size214 deprecation_message = (215 f"You have passed {batch_size} text prompts (`prompt`), but only {init_latents.shape[0]} initial"216 " images (`image`). Initial images are now duplicating to match the number of text prompts. Note"217 " that this behavior is deprecated and will be removed in a version 1.0.0. Please make sure to update"218 " your script to pass as many initial images as text prompts to suppress this warning."219 )220 deprecate("len(prompt) != len(image)", "1.0.0", deprecation_message, standard_warn=False)221 additional_image_per_prompt = batch_size // init_latents.shape[0]222 init_latents = torch.cat([init_latents] * additional_image_per_prompt, dim=0)223 elif batch_size > init_latents.shape[0] and batch_size % init_latents.shape[0] != 0:224 raise ValueError(225 f"Cannot duplicate `image` of batch size {init_latents.shape[0]} to {batch_size} text prompts."226 )227 else:228 init_latents = torch.cat([init_latents], dim=0)229 230 shape = init_latents.shape231 noise = randn_tensor(shape, generator=generator, device=device, dtype=dtype)232 233 # get latents234 init_latents = self.scheduler.add_noise(init_latents, noise, timestep)235 latents = init_latents236 237 return latents238 239 @torch.enable_grad()240 def cond_fn(241 self,242 latents,243 timestep,244 index,245 text_embeddings,246 noise_pred_original,247 text_embeddings_clip,248 clip_guidance_scale,249 num_cutouts,250 use_cutouts=True,251 ):252 latents = latents.detach().requires_grad_()253 254 latent_model_input = self.scheduler.scale_model_input(latents, timestep)255 256 # predict the noise residual257 noise_pred = self.unet(latent_model_input, timestep, encoder_hidden_states=text_embeddings).sample258 259 if isinstance(self.scheduler, (PNDMScheduler, DDIMScheduler, DPMSolverMultistepScheduler)):260 alpha_prod_t = self.scheduler.alphas_cumprod[timestep]261 beta_prod_t = 1 - alpha_prod_t262 # compute predicted original sample from predicted noise also called263 # "predicted x_0" of formula (12) from https://arxiv.org/pdf/2010.02502.pdf264 pred_original_sample = (latents - beta_prod_t ** (0.5) * noise_pred) / alpha_prod_t ** (0.5)265 266 fac = torch.sqrt(beta_prod_t)267 sample = pred_original_sample * (fac) + latents * (1 - fac)268 elif isinstance(self.scheduler, LMSDiscreteScheduler):269 sigma = self.scheduler.sigmas[index]270 sample = latents - sigma * noise_pred271 else:272 raise ValueError(f"scheduler type {type(self.scheduler)} not supported")273 274 sample = 1 / self.vae.config.scaling_factor * sample275 image = self.vae.decode(sample).sample276 image = (image / 2 + 0.5).clamp(0, 1)277 278 if use_cutouts:279 image = self.make_cutouts(image, num_cutouts)280 else:281 image = transforms.Resize(self.cut_out_size)(image)282 image = self.normalize(image).to(latents.dtype)283 284 image_embeddings_clip = self.clip_model.get_image_features(image)285 image_embeddings_clip = image_embeddings_clip / image_embeddings_clip.norm(p=2, dim=-1, keepdim=True)286 287 if use_cutouts:288 dists = spherical_dist_loss(image_embeddings_clip, text_embeddings_clip)289 dists = dists.view([num_cutouts, sample.shape[0], -1])290 loss = dists.sum(2).mean(0).sum() * clip_guidance_scale291 else:292 loss = spherical_dist_loss(image_embeddings_clip, text_embeddings_clip).mean() * clip_guidance_scale293 294 grads = -torch.autograd.grad(loss, latents)[0]295 296 if isinstance(self.scheduler, LMSDiscreteScheduler):297 latents = latents.detach() + grads * (sigma**2)298 noise_pred = noise_pred_original299 else:300 noise_pred = noise_pred_original - torch.sqrt(beta_prod_t) * grads301 return noise_pred, latents302 303 @torch.no_grad()304 def __call__(305 self,306 prompt: Union[str, List[str]],307 height: Optional[int] = 512,308 width: Optional[int] = 512,309 image: Union[torch.Tensor, PIL.Image.Image] = None,310 strength: float = 0.8,311 num_inference_steps: Optional[int] = 50,312 guidance_scale: Optional[float] = 7.5,313 num_images_per_prompt: Optional[int] = 1,314 eta: float = 0.0,315 clip_guidance_scale: Optional[float] = 100,316 clip_prompt: Optional[Union[str, List[str]]] = None,317 num_cutouts: Optional[int] = 4,318 use_cutouts: Optional[bool] = True,319 generator: Optional[torch.Generator] = None,320 latents: Optional[torch.Tensor] = None,321 output_type: Optional[str] = "pil",322 return_dict: bool = True,323 ):324 if isinstance(prompt, str):325 batch_size = 1326 elif isinstance(prompt, list):327 batch_size = len(prompt)328 else:329 raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")330 331 if height % 8 != 0 or width % 8 != 0:332 raise ValueError(f"`height` and `width` have to be divisible by 8 but are {height} and {width}.")333 334 # get prompt text embeddings335 text_input = self.tokenizer(336 prompt,337 padding="max_length",338 max_length=self.tokenizer.model_max_length,339 truncation=True,340 return_tensors="pt",341 )342 text_embeddings = self.text_encoder(text_input.input_ids.to(self.device))[0]343 # duplicate text embeddings for each generation per prompt344 text_embeddings = text_embeddings.repeat_interleave(num_images_per_prompt, dim=0)345 346 # set timesteps347 accepts_offset = "offset" in set(inspect.signature(self.scheduler.set_timesteps).parameters.keys())348 extra_set_kwargs = {}349 if accepts_offset:350 extra_set_kwargs["offset"] = 1351 352 self.scheduler.set_timesteps(num_inference_steps, **extra_set_kwargs)353 # Some schedulers like PNDM have timesteps as arrays354 # It's more optimized to move all timesteps to correct device beforehand355 self.scheduler.timesteps.to(self.device)356 357 timesteps, num_inference_steps = self.get_timesteps(num_inference_steps, strength, self.device)358 latent_timestep = timesteps[:1].repeat(batch_size * num_images_per_prompt)359 360 # Preprocess image361 image = preprocess(image, width, height)362 if latents is None:363 latents = self.prepare_latents(364 image,365 latent_timestep,366 batch_size,367 num_images_per_prompt,368 text_embeddings.dtype,369 self.device,370 generator,371 )372 373 if clip_guidance_scale > 0:374 if clip_prompt is not None:375 clip_text_input = self.tokenizer(376 clip_prompt,377 padding="max_length",378 max_length=self.tokenizer.model_max_length,379 truncation=True,380 return_tensors="pt",381 ).input_ids.to(self.device)382 else:383 clip_text_input = text_input.input_ids.to(self.device)384 text_embeddings_clip = self.clip_model.get_text_features(clip_text_input)385 text_embeddings_clip = text_embeddings_clip / text_embeddings_clip.norm(p=2, dim=-1, keepdim=True)386 # duplicate text embeddings clip for each generation per prompt387 text_embeddings_clip = text_embeddings_clip.repeat_interleave(num_images_per_prompt, dim=0)388 389 # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2)390 # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1`391 # corresponds to doing no classifier free guidance.392 do_classifier_free_guidance = guidance_scale > 1.0393 # get unconditional embeddings for classifier free guidance394 if do_classifier_free_guidance:395 max_length = text_input.input_ids.shape[-1]396 uncond_input = self.tokenizer([""], padding="max_length", max_length=max_length, return_tensors="pt")397 uncond_embeddings = self.text_encoder(uncond_input.input_ids.to(self.device))[0]398 # duplicate unconditional embeddings for each generation per prompt399 uncond_embeddings = uncond_embeddings.repeat_interleave(num_images_per_prompt, dim=0)400 401 # For classifier free guidance, we need to do two forward passes.402 # Here we concatenate the unconditional and text embeddings into a single batch403 # to avoid doing two forward passes404 text_embeddings = torch.cat([uncond_embeddings, text_embeddings])405 406 # get the initial random noise unless the user supplied it407 408 # Unlike in other pipelines, latents need to be generated in the target device409 # for 1-to-1 results reproducibility with the CompVis implementation.410 # However this currently doesn't work in `mps`.411 latents_shape = (batch_size * num_images_per_prompt, self.unet.config.in_channels, height // 8, width // 8)412 latents_dtype = text_embeddings.dtype413 if latents is None:414 if self.device.type == "mps":415 # randn does not work reproducibly on mps416 latents = torch.randn(latents_shape, generator=generator, device="cpu", dtype=latents_dtype).to(417 self.device418 )419 else:420 latents = torch.randn(latents_shape, generator=generator, device=self.device, dtype=latents_dtype)421 else:422 if latents.shape != latents_shape:423 raise ValueError(f"Unexpected latents shape, got {latents.shape}, expected {latents_shape}")424 latents = latents.to(self.device)425 426 # scale the initial noise by the standard deviation required by the scheduler427 latents = latents * self.scheduler.init_noise_sigma428 429 # prepare extra kwargs for the scheduler step, since not all schedulers have the same signature430 # eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers.431 # eta corresponds to η in DDIM paper: https://arxiv.org/abs/2010.02502432 # and should be between [0, 1]433 accepts_eta = "eta" in set(inspect.signature(self.scheduler.step).parameters.keys())434 extra_step_kwargs = {}435 if accepts_eta:436 extra_step_kwargs["eta"] = eta437 438 # check if the scheduler accepts generator439 accepts_generator = "generator" in set(inspect.signature(self.scheduler.step).parameters.keys())440 if accepts_generator:441 extra_step_kwargs["generator"] = generator442 443 with self.progress_bar(total=num_inference_steps):444 for i, t in enumerate(timesteps):445 # expand the latents if we are doing classifier free guidance446 latent_model_input = torch.cat([latents] * 2) if do_classifier_free_guidance else latents447 latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)448 449 # predict the noise residual450 noise_pred = self.unet(latent_model_input, t, encoder_hidden_states=text_embeddings).sample451 452 # perform classifier free guidance453 if do_classifier_free_guidance:454 noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)455 noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)456 457 # perform clip guidance458 if clip_guidance_scale > 0:459 text_embeddings_for_guidance = (460 text_embeddings.chunk(2)[1] if do_classifier_free_guidance else text_embeddings461 )462 noise_pred, latents = self.cond_fn(463 latents,464 t,465 i,466 text_embeddings_for_guidance,467 noise_pred,468 text_embeddings_clip,469 clip_guidance_scale,470 num_cutouts,471 use_cutouts,472 )473 474 # compute the previous noisy sample x_t -> x_t-1475 latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs).prev_sample476 477 # scale and decode the image latents with vae478 latents = 1 / self.vae.config.scaling_factor * latents479 image = self.vae.decode(latents).sample480 481 image = (image / 2 + 0.5).clamp(0, 1)482 image = image.cpu().permute(0, 2, 3, 1).numpy()483 484 if output_type == "pil":485 image = self.numpy_to_pil(image)486 487 if not return_dict:488 return (image, None)489 490 return StableDiffusionPipelineOutput(images=image, nsfw_content_detected=None)491 