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
1# Copyright 2025 The HuggingFace Team. All rights reserved.2#3# Licensed under the Apache License, Version 2.0 (the "License");4# you may not use this file except in compliance with the License.5# You may obtain a copy of the License at6#7# http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# distributed under the License is distributed on an "AS IS" BASIS,11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12# See the License for the specific language governing permissions and13# limitations under the License.14 15import inspect16from typing import Callable, List, Optional, Union17 18import numpy as np19import PIL.Image20import torch21from packaging import version22from transformers import CLIPImageProcessor, CLIPTextModel, CLIPTokenizer23 24from diffusers import AutoencoderKL, DiffusionPipeline, UNet2DConditionModel25from diffusers.configuration_utils import FrozenDict, deprecate26from diffusers.loaders import StableDiffusionLoraLoaderMixin, TextualInversionLoaderMixin27from diffusers.pipelines.pipeline_utils import StableDiffusionMixin28from diffusers.pipelines.stable_diffusion import StableDiffusionPipelineOutput29from diffusers.pipelines.stable_diffusion.safety_checker import (30 StableDiffusionSafetyChecker,31)32from diffusers.schedulers import KarrasDiffusionSchedulers33from diffusers.utils import (34 logging,35)36from diffusers.utils.torch_utils import randn_tensor37 38 39logger = logging.get_logger(__name__) # pylint: disable=invalid-name40 41 42def prepare_mask_and_masked_image(image, mask):43 """44 Prepares a pair (image, mask) to be consumed by the Stable Diffusion pipeline. This means that those inputs will be45 converted to ``torch.Tensor`` with shapes ``batch x channels x height x width`` where ``channels`` is ``3`` for the46 ``image`` and ``1`` for the ``mask``.47 The ``image`` will be converted to ``torch.float32`` and normalized to be in ``[-1, 1]``. The ``mask`` will be48 binarized (``mask > 0.5``) and cast to ``torch.float32`` too.49 Args:50 image (Union[np.array, PIL.Image, torch.Tensor]): The image to inpaint.51 It can be a ``PIL.Image``, or a ``height x width x 3`` ``np.array`` or a ``channels x height x width``52 ``torch.Tensor`` or a ``batch x channels x height x width`` ``torch.Tensor``.53 mask (_type_): The mask to apply to the image, i.e. regions to inpaint.54 It can be a ``PIL.Image``, or a ``height x width`` ``np.array`` or a ``1 x height x width``55 ``torch.Tensor`` or a ``batch x 1 x height x width`` ``torch.Tensor``.56 Raises:57 ValueError: ``torch.Tensor`` images should be in the ``[-1, 1]`` range. ValueError: ``torch.Tensor`` mask58 should be in the ``[0, 1]`` range. ValueError: ``mask`` and ``image`` should have the same spatial dimensions.59 TypeError: ``mask`` is a ``torch.Tensor`` but ``image`` is not60 (ot the other way around).61 Returns:62 tuple[torch.Tensor]: The pair (mask, masked_image) as ``torch.Tensor`` with 463 dimensions: ``batch x channels x height x width``.64 """65 if isinstance(image, torch.Tensor):66 if not isinstance(mask, torch.Tensor):67 raise TypeError(f"`image` is a torch.Tensor but `mask` (type: {type(mask)} is not")68 69 # Batch single image70 if image.ndim == 3:71 assert image.shape[0] == 3, "Image outside a batch should be of shape (3, H, W)"72 image = image.unsqueeze(0)73 74 # Batch and add channel dim for single mask75 if mask.ndim == 2:76 mask = mask.unsqueeze(0).unsqueeze(0)77 78 # Batch single mask or add channel dim79 if mask.ndim == 3:80 # Single batched mask, no channel dim or single mask not batched but channel dim81 if mask.shape[0] == 1:82 mask = mask.unsqueeze(0)83 84 # Batched masks no channel dim85 else:86 mask = mask.unsqueeze(1)87 88 assert image.ndim == 4 and mask.ndim == 4, "Image and Mask must have 4 dimensions"89 assert image.shape[-2:] == mask.shape[-2:], "Image and Mask must have the same spatial dimensions"90 assert image.shape[0] == mask.shape[0], "Image and Mask must have the same batch size"91 92 # Check image is in [-1, 1]93 if image.min() < -1 or image.max() > 1:94 raise ValueError("Image should be in [-1, 1] range")95 96 # Check mask is in [0, 1]97 if mask.min() < 0 or mask.max() > 1:98 raise ValueError("Mask should be in [0, 1] range")99 100 # Binarize mask101 mask[mask < 0.5] = 0102 mask[mask >= 0.5] = 1103 104 # Image as float32105 image = image.to(dtype=torch.float32)106 elif isinstance(mask, torch.Tensor):107 raise TypeError(f"`mask` is a torch.Tensor but `image` (type: {type(image)} is not")108 else:109 # preprocess image110 if isinstance(image, (PIL.Image.Image, np.ndarray)):111 image = [image]112 113 if isinstance(image, list) and isinstance(image[0], PIL.Image.Image):114 image = [np.array(i.convert("RGB"))[None, :] for i in image]115 image = np.concatenate(image, axis=0)116 elif isinstance(image, list) and isinstance(image[0], np.ndarray):117 image = np.concatenate([i[None, :] for i in image], axis=0)118 119 image = image.transpose(0, 3, 1, 2)120 image = torch.from_numpy(image).to(dtype=torch.float32) / 127.5 - 1.0121 122 # preprocess mask123 if isinstance(mask, (PIL.Image.Image, np.ndarray)):124 mask = [mask]125 126 if isinstance(mask, list) and isinstance(mask[0], PIL.Image.Image):127 mask = np.concatenate([np.array(m.convert("L"))[None, None, :] for m in mask], axis=0)128 mask = mask.astype(np.float32) / 255.0129 elif isinstance(mask, list) and isinstance(mask[0], np.ndarray):130 mask = np.concatenate([m[None, None, :] for m in mask], axis=0)131 132 mask[mask < 0.5] = 0133 mask[mask >= 0.5] = 1134 mask = torch.from_numpy(mask)135 136 # masked_image = image * (mask >= 0.5)137 masked_image = image138 139 return mask, masked_image140 141 142class StableDiffusionRepaintPipeline(143 DiffusionPipeline, StableDiffusionMixin, TextualInversionLoaderMixin, StableDiffusionLoraLoaderMixin144):145 r"""146 Pipeline for text-guided image inpainting using Stable Diffusion. *This is an experimental feature*.147 This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods the148 library implements for all the pipelines (such as downloading or saving, running on a particular device, etc.)149 In addition the pipeline inherits the following loading methods:150 - *Textual-Inversion*: [`loaders.TextualInversionLoaderMixin.load_textual_inversion`]151 - *LoRA*: [`loaders.StableDiffusionLoraLoaderMixin.load_lora_weights`]152 as well as the following saving methods:153 - *LoRA*: [`loaders.StableDiffusionLoraLoaderMixin.save_lora_weights`]154 Args:155 vae ([`AutoencoderKL`]):156 Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representations.157 text_encoder ([`CLIPTextModel`]):158 Frozen text-encoder. Stable Diffusion uses the text portion of159 [CLIP](https://huggingface.co/docs/transformers/model_doc/clip#transformers.CLIPTextModel), specifically160 the [clip-vit-large-patch14](https://huggingface.co/openai/clip-vit-large-patch14) variant.161 tokenizer (`CLIPTokenizer`):162 Tokenizer of class163 [CLIPTokenizer](https://huggingface.co/docs/transformers/v4.21.0/en/model_doc/clip#transformers.CLIPTokenizer).164 unet ([`UNet2DConditionModel`]): Conditional U-Net architecture to denoise the encoded image latents.165 scheduler ([`SchedulerMixin`]):166 A scheduler to be used in combination with `unet` to denoise the encoded image latents. Can be one of167 [`DDIMScheduler`], [`LMSDiscreteScheduler`], or [`PNDMScheduler`].168 safety_checker ([`StableDiffusionSafetyChecker`]):169 Classification module that estimates whether generated images could be considered offensive or harmful.170 Please, refer to the [model card](https://huggingface.co/runwayml/stable-diffusion-v1-5) for details.171 feature_extractor ([`CLIPImageProcessor`]):172 Model that extracts features from generated images to be used as inputs for the `safety_checker`.173 """174 175 _optional_components = ["safety_checker", "feature_extractor"]176 177 def __init__(178 self,179 vae: AutoencoderKL,180 text_encoder: CLIPTextModel,181 tokenizer: CLIPTokenizer,182 unet: UNet2DConditionModel,183 scheduler: KarrasDiffusionSchedulers,184 safety_checker: StableDiffusionSafetyChecker,185 feature_extractor: CLIPImageProcessor,186 requires_safety_checker: bool = True,187 ):188 super().__init__()189 190 if scheduler is not None and getattr(scheduler.config, "steps_offset", 1) != 1:191 deprecation_message = (192 f"The configuration file of this scheduler: {scheduler} is outdated. `steps_offset`"193 f" should be set to 1 instead of {scheduler.config.steps_offset}. Please make sure "194 "to update the config accordingly as leaving `steps_offset` might led to incorrect results"195 " in future versions. If you have downloaded this checkpoint from the Hugging Face Hub,"196 " it would be very nice if you could open a Pull request for the `scheduler/scheduler_config.json`"197 " file"198 )199 deprecate("steps_offset!=1", "1.0.0", deprecation_message, standard_warn=False)200 new_config = dict(scheduler.config)201 new_config["steps_offset"] = 1202 scheduler._internal_dict = FrozenDict(new_config)203 204 if scheduler is not None and getattr(scheduler.config, "skip_prk_steps", True) is False:205 deprecation_message = (206 f"The configuration file of this scheduler: {scheduler} has not set the configuration"207 " `skip_prk_steps`. `skip_prk_steps` should be set to True in the configuration file. Please make"208 " sure to update the config accordingly as not setting `skip_prk_steps` in the config might lead to"209 " incorrect results in future versions. If you have downloaded this checkpoint from the Hugging Face"210 " Hub, it would be very nice if you could open a Pull request for the"211 " `scheduler/scheduler_config.json` file"212 )213 deprecate(214 "skip_prk_steps not set",215 "1.0.0",216 deprecation_message,217 standard_warn=False,218 )219 new_config = dict(scheduler.config)220 new_config["skip_prk_steps"] = True221 scheduler._internal_dict = FrozenDict(new_config)222 223 if safety_checker is None and requires_safety_checker:224 logger.warning(225 f"You have disabled the safety checker for {self.__class__} by passing `safety_checker=None`. Ensure"226 " that you abide to the conditions of the Stable Diffusion license and do not expose unfiltered"227 " results in services or applications open to the public. Both the diffusers team and Hugging Face"228 " strongly recommend to keep the safety filter enabled in all public facing circumstances, disabling"229 " it only for use-cases that involve analyzing network behavior or auditing its results. For more"230 " information, please have a look at https://github.com/huggingface/diffusers/pull/254 ."231 )232 233 if safety_checker is not None and feature_extractor is None:234 raise ValueError(235 "Make sure to define a feature extractor when loading {self.__class__} if you want to use the safety"236 " checker. If you do not want to use the safety checker, you can pass `'safety_checker=None'` instead."237 )238 239 is_unet_version_less_0_9_0 = (240 unet is not None241 and hasattr(unet.config, "_diffusers_version")242 and version.parse(version.parse(unet.config._diffusers_version).base_version) < version.parse("0.9.0.dev0")243 )244 is_unet_sample_size_less_64 = (245 unet is not None and hasattr(unet.config, "sample_size") and unet.config.sample_size < 64246 )247 if is_unet_version_less_0_9_0 and is_unet_sample_size_less_64:248 deprecation_message = (249 "The configuration file of the unet has set the default `sample_size` to smaller than"250 " 64 which seems highly unlikely .If you're checkpoint is a fine-tuned version of any of the"251 " following: \n- CompVis/stable-diffusion-v1-4 \n- CompVis/stable-diffusion-v1-3 \n-"252 " CompVis/stable-diffusion-v1-2 \n- CompVis/stable-diffusion-v1-1 \n- runwayml/stable-diffusion-v1-5"253 " \n- runwayml/stable-diffusion-inpainting \n you should change 'sample_size' to 64 in the"254 " configuration file. Please make sure to update the config accordingly as leaving `sample_size=32`"255 " in the config might lead to incorrect results in future versions. If you have downloaded this"256 " checkpoint from the Hugging Face Hub, it would be very nice if you could open a Pull request for"257 " the `unet/config.json` file"258 )259 deprecate("sample_size<64", "1.0.0", deprecation_message, standard_warn=False)260 new_config = dict(unet.config)261 new_config["sample_size"] = 64262 unet._internal_dict = FrozenDict(new_config)263 # Check shapes, assume num_channels_latents == 4, num_channels_mask == 1, num_channels_masked == 4264 if unet is not None and unet.config.in_channels != 4:265 logger.warning(266 f"You have loaded a UNet with {unet.config.in_channels} input channels, whereas by default,"267 f" {self.__class__} assumes that `pipeline.unet` has 4 input channels: 4 for `num_channels_latents`,"268 ". If you did not intend to modify"269 " this behavior, please check whether you have loaded the right checkpoint."270 )271 272 self.register_modules(273 vae=vae,274 text_encoder=text_encoder,275 tokenizer=tokenizer,276 unet=unet,277 scheduler=scheduler,278 safety_checker=safety_checker,279 feature_extractor=feature_extractor,280 )281 self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1) if getattr(self, "vae", None) else 8282 self.register_to_config(requires_safety_checker=requires_safety_checker)283 284 # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline._encode_prompt285 def _encode_prompt(286 self,287 prompt,288 device,289 num_images_per_prompt,290 do_classifier_free_guidance,291 negative_prompt=None,292 prompt_embeds: Optional[torch.Tensor] = None,293 negative_prompt_embeds: Optional[torch.Tensor] = None,294 ):295 r"""296 Encodes the prompt into text encoder hidden states.297 Args:298 prompt (`str` or `List[str]`, *optional*):299 prompt to be encoded300 device: (`torch.device`):301 torch device302 num_images_per_prompt (`int`):303 number of images that should be generated per prompt304 do_classifier_free_guidance (`bool`):305 whether to use classifier free guidance or not306 negative_prompt (`str` or `List[str]`, *optional*):307 The prompt or prompts not to guide the image generation. If not defined, one has to pass308 `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is309 less than `1`).310 prompt_embeds (`torch.Tensor`, *optional*):311 Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not312 provided, text embeddings will be generated from `prompt` input argument.313 negative_prompt_embeds (`torch.Tensor`, *optional*):314 Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt315 weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input316 argument.317 """318 if prompt is not None and isinstance(prompt, str):319 batch_size = 1320 elif prompt is not None and isinstance(prompt, list):321 batch_size = len(prompt)322 else:323 batch_size = prompt_embeds.shape[0]324 325 if prompt_embeds is None:326 # textual inversion: process multi-vector tokens if necessary327 if isinstance(self, TextualInversionLoaderMixin):328 prompt = self.maybe_convert_prompt(prompt, self.tokenizer)329 330 text_inputs = self.tokenizer(331 prompt,332 padding="max_length",333 max_length=self.tokenizer.model_max_length,334 truncation=True,335 return_tensors="pt",336 )337 text_input_ids = text_inputs.input_ids338 untruncated_ids = self.tokenizer(prompt, padding="longest", return_tensors="pt").input_ids339 340 if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(341 text_input_ids, untruncated_ids342 ):343 removed_text = self.tokenizer.batch_decode(344 untruncated_ids[:, self.tokenizer.model_max_length - 1 : -1]345 )346 logger.warning(347 "The following part of your input was truncated because CLIP can only handle sequences up to"348 f" {self.tokenizer.model_max_length} tokens: {removed_text}"349 )350 351 if hasattr(self.text_encoder.config, "use_attention_mask") and self.text_encoder.config.use_attention_mask:352 attention_mask = text_inputs.attention_mask.to(device)353 else:354 attention_mask = None355 356 prompt_embeds = self.text_encoder(357 text_input_ids.to(device),358 attention_mask=attention_mask,359 )360 prompt_embeds = prompt_embeds[0]361 362 prompt_embeds = prompt_embeds.to(dtype=self.text_encoder.dtype, device=device)363 364 bs_embed, seq_len, _ = prompt_embeds.shape365 # duplicate text embeddings for each generation per prompt, using mps friendly method366 prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)367 prompt_embeds = prompt_embeds.view(bs_embed * num_images_per_prompt, seq_len, -1)368 369 # get unconditional embeddings for classifier free guidance370 if do_classifier_free_guidance and negative_prompt_embeds is None:371 uncond_tokens: List[str]372 if negative_prompt is None:373 uncond_tokens = [""] * batch_size374 elif type(prompt) is not type(negative_prompt):375 raise TypeError(376 f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="377 f" {type(prompt)}."378 )379 elif isinstance(negative_prompt, str):380 uncond_tokens = [negative_prompt]381 elif batch_size != len(negative_prompt):382 raise ValueError(383 f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"384 f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"385 " the batch size of `prompt`."386 )387 else:388 uncond_tokens = negative_prompt389 390 # textual inversion: process multi-vector tokens if necessary391 if isinstance(self, TextualInversionLoaderMixin):392 uncond_tokens = self.maybe_convert_prompt(uncond_tokens, self.tokenizer)393 394 max_length = prompt_embeds.shape[1]395 uncond_input = self.tokenizer(396 uncond_tokens,397 padding="max_length",398 max_length=max_length,399 truncation=True,400 return_tensors="pt",401 )402 403 if hasattr(self.text_encoder.config, "use_attention_mask") and self.text_encoder.config.use_attention_mask:404 attention_mask = uncond_input.attention_mask.to(device)405 else:406 attention_mask = None407 408 negative_prompt_embeds = self.text_encoder(409 uncond_input.input_ids.to(device),410 attention_mask=attention_mask,411 )412 negative_prompt_embeds = negative_prompt_embeds[0]413 414 if do_classifier_free_guidance:415 # duplicate unconditional embeddings for each generation per prompt, using mps friendly method416 seq_len = negative_prompt_embeds.shape[1]417 418 negative_prompt_embeds = negative_prompt_embeds.to(dtype=self.text_encoder.dtype, device=device)419 420 negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_images_per_prompt, 1)421 negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)422 423 # For classifier free guidance, we need to do two forward passes.424 # Here we concatenate the unconditional and text embeddings into a single batch425 # to avoid doing two forward passes426 prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds])427 428 return prompt_embeds429 430 # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.run_safety_checker431 def run_safety_checker(self, image, device, dtype):432 if self.safety_checker is not None:433 safety_checker_input = self.feature_extractor(self.numpy_to_pil(image), return_tensors="pt").to(device)434 image, has_nsfw_concept = self.safety_checker(435 images=image, clip_input=safety_checker_input.pixel_values.to(dtype)436 )437 else:438 has_nsfw_concept = None439 return image, has_nsfw_concept440 441 # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.prepare_extra_step_kwargs442 def prepare_extra_step_kwargs(self, generator, eta):443 # prepare extra kwargs for the scheduler step, since not all schedulers have the same signature444 # eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers.445 # eta corresponds to η in DDIM paper: https://huggingface.co/papers/2010.02502446 # and should be between [0, 1]447 448 accepts_eta = "eta" in set(inspect.signature(self.scheduler.step).parameters.keys())449 extra_step_kwargs = {}450 if accepts_eta:451 extra_step_kwargs["eta"] = eta452 453 # check if the scheduler accepts generator454 accepts_generator = "generator" in set(inspect.signature(self.scheduler.step).parameters.keys())455 if accepts_generator:456 extra_step_kwargs["generator"] = generator457 return extra_step_kwargs458 459 # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.decode_latents460 def decode_latents(self, latents):461 latents = 1 / self.vae.config.scaling_factor * latents462 image = self.vae.decode(latents).sample463 image = (image / 2 + 0.5).clamp(0, 1)464 # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16465 image = image.cpu().permute(0, 2, 3, 1).float().numpy()466 return image467 468 # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.check_inputs469 def check_inputs(470 self,471 prompt,472 height,473 width,474 callback_steps,475 negative_prompt=None,476 prompt_embeds=None,477 negative_prompt_embeds=None,478 ):479 if height % 8 != 0 or width % 8 != 0:480 raise ValueError(f"`height` and `width` have to be divisible by 8 but are {height} and {width}.")481 482 if (callback_steps is None) or (483 callback_steps is not None and (not isinstance(callback_steps, int) or callback_steps <= 0)484 ):485 raise ValueError(486 f"`callback_steps` has to be a positive integer but is {callback_steps} of type"487 f" {type(callback_steps)}."488 )489 490 if prompt is not None and prompt_embeds is not None:491 raise ValueError(492 f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"493 " only forward one of the two."494 )495 elif prompt is None and prompt_embeds is None:496 raise ValueError(497 "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."498 )499 elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):500 raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")501 502 if negative_prompt is not None and negative_prompt_embeds is not None:503 raise ValueError(504 f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:"505 f" {negative_prompt_embeds}. Please make sure to only forward one of the two."506 )507 508 if prompt_embeds is not None and negative_prompt_embeds is not None:509 if prompt_embeds.shape != negative_prompt_embeds.shape:510 raise ValueError(511 "`prompt_embeds` and `negative_prompt_embeds` must have the same shape when passed directly, but"512 f" got: `prompt_embeds` {prompt_embeds.shape} != `negative_prompt_embeds`"513 f" {negative_prompt_embeds.shape}."514 )515 516 # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.prepare_latents517 def prepare_latents(518 self,519 batch_size,520 num_channels_latents,521 height,522 width,523 dtype,524 device,525 generator,526 latents=None,527 ):528 shape = (529 batch_size,530 num_channels_latents,531 height // self.vae_scale_factor,532 width // self.vae_scale_factor,533 )534 if isinstance(generator, list) and len(generator) != batch_size:535 raise ValueError(536 f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"537 f" size of {batch_size}. Make sure the batch size matches the length of the generators."538 )539 540 if latents is None:541 latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)542 else:543 latents = latents.to(device)544 545 # scale the initial noise by the standard deviation required by the scheduler546 latents = latents * self.scheduler.init_noise_sigma547 return latents548 549 def prepare_mask_latents(550 self,551 mask,552 masked_image,553 batch_size,554 height,555 width,556 dtype,557 device,558 generator,559 do_classifier_free_guidance,560 ):561 # resize the mask to latents shape as we concatenate the mask to the latents562 # we do that before converting to dtype to avoid breaking in case we're using cpu_offload563 # and half precision564 mask = torch.nn.functional.interpolate(565 mask, size=(height // self.vae_scale_factor, width // self.vae_scale_factor)566 )567 mask = mask.to(device=device, dtype=dtype)568 569 masked_image = masked_image.to(device=device, dtype=dtype)570 571 # encode the mask image into latents space so we can concatenate it to the latents572 if isinstance(generator, list):573 masked_image_latents = [574 self.vae.encode(masked_image[i : i + 1]).latent_dist.sample(generator=generator[i])575 for i in range(batch_size)576 ]577 masked_image_latents = torch.cat(masked_image_latents, dim=0)578 else:579 masked_image_latents = self.vae.encode(masked_image).latent_dist.sample(generator=generator)580 masked_image_latents = self.vae.config.scaling_factor * masked_image_latents581 582 # duplicate mask and masked_image_latents for each generation per prompt, using mps friendly method583 if mask.shape[0] < batch_size:584 if not batch_size % mask.shape[0] == 0:585 raise ValueError(586 "The passed mask and the required batch size don't match. Masks are supposed to be duplicated to"587 f" a total batch size of {batch_size}, but {mask.shape[0]} masks were passed. Make sure the number"588 " of masks that you pass is divisible by the total requested batch size."589 )590 mask = mask.repeat(batch_size // mask.shape[0], 1, 1, 1)591 if masked_image_latents.shape[0] < batch_size:592 if not batch_size % masked_image_latents.shape[0] == 0:593 raise ValueError(594 "The passed images and the required batch size don't match. Images are supposed to be duplicated"595 f" to a total batch size of {batch_size}, but {masked_image_latents.shape[0]} images were passed."596 " Make sure the number of images that you pass is divisible by the total requested batch size."597 )598 masked_image_latents = masked_image_latents.repeat(batch_size // masked_image_latents.shape[0], 1, 1, 1)599 600 mask = torch.cat([mask] * 2) if do_classifier_free_guidance else mask601 masked_image_latents = (602 torch.cat([masked_image_latents] * 2) if do_classifier_free_guidance else masked_image_latents603 )604 605 # aligning device to prevent device errors when concating it with the latent model input606 masked_image_latents = masked_image_latents.to(device=device, dtype=dtype)607 return mask, masked_image_latents608 609 @torch.no_grad()610 def __call__(611 self,612 prompt: Union[str, List[str]] = None,613 image: Union[torch.Tensor, PIL.Image.Image] = None,614 mask_image: Union[torch.Tensor, PIL.Image.Image] = None,615 height: Optional[int] = None,616 width: Optional[int] = None,617 num_inference_steps: int = 50,618 jump_length: Optional[int] = 10,619 jump_n_sample: Optional[int] = 10,620 guidance_scale: float = 7.5,621 negative_prompt: Optional[Union[str, List[str]]] = None,622 num_images_per_prompt: Optional[int] = 1,623 eta: float = 0.0,624 generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,625 latents: Optional[torch.Tensor] = None,626 prompt_embeds: Optional[torch.Tensor] = None,627 negative_prompt_embeds: Optional[torch.Tensor] = None,628 output_type: Optional[str] = "pil",629 return_dict: bool = True,630 callback: Optional[Callable[[int, int, torch.Tensor], None]] = None,631 callback_steps: int = 1,632 ):633 r"""634 Function invoked when calling the pipeline for generation.635 Args:636 prompt (`str` or `List[str]`, *optional*):637 The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.638 instead.639 image (`PIL.Image.Image`):640 `Image`, or tensor representing an image batch which will be inpainted, *i.e.* parts of the image will641 be masked out with `mask_image` and repainted according to `prompt`.642 mask_image (`PIL.Image.Image`):643 `Image`, or tensor representing an image batch, to mask `image`. White pixels in the mask will be644 repainted, while black pixels will be preserved. If `mask_image` is a PIL image, it will be converted645 to a single channel (luminance) before use. If it's a tensor, it should contain one color channel (L)646 instead of 3, so the expected shape would be `(B, H, W, 1)`.647 height (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):648 The height in pixels of the generated image.649 width (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):650 The width in pixels of the generated image.651 num_inference_steps (`int`, *optional*, defaults to 50):652 The number of denoising steps. More denoising steps usually lead to a higher quality image at the653 expense of slower inference.654 jump_length (`int`, *optional*, defaults to 10):655 The number of steps taken forward in time before going backward in time for a single jump ("j" in656 RePaint paper). Take a look at Figure 9 and 10 in https://huggingface.co/papers/2201.09865.657 jump_n_sample (`int`, *optional*, defaults to 10):658 The number of times we will make forward time jump for a given chosen time sample. Take a look at659 Figure 9 and 10 in https://huggingface.co/papers/2201.09865.660 guidance_scale (`float`, *optional*, defaults to 7.5):661 Guidance scale as defined in [Classifier-Free Diffusion Guidance](https://huggingface.co/papers/2207.12598).662 `guidance_scale` is defined as `w` of equation 2. of [Imagen663 Paper](https://huggingface.co/papers/2205.11487). Guidance scale is enabled by setting `guidance_scale >664 1`. Higher guidance scale encourages to generate images that are closely linked to the text `prompt`,665 usually at the expense of lower image quality.666 negative_prompt (`str` or `List[str]`, *optional*):667 The prompt or prompts not to guide the image generation. If not defined, one has to pass668 `negative_prompt_embeds`. instead. Ignored when not using guidance (i.e., ignored if `guidance_scale`669 is less than `1`).670 num_images_per_prompt (`int`, *optional*, defaults to 1):671 The number of images to generate per prompt.672 eta (`float`, *optional*, defaults to 0.0):673 Corresponds to parameter eta (η) in the DDIM paper: https://huggingface.co/papers/2010.02502. Only applies to674 [`schedulers.DDIMScheduler`], will be ignored for others.675 generator (`torch.Generator`, *optional*):676 One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html)677 to make generation deterministic.678 latents (`torch.Tensor`, *optional*):679 Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image680 generation. Can be used to tweak the same generation with different prompts. If not provided, a latents681 tensor will ge generated by sampling using the supplied random `generator`.682 prompt_embeds (`torch.Tensor`, *optional*):683 Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not684 provided, text embeddings will be generated from `prompt` input argument.685 negative_prompt_embeds (`torch.Tensor`, *optional*):686 Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt687 weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input688 argument.689 output_type (`str`, *optional*, defaults to `"pil"`):690 The output format of the generate image. Choose between691 [PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`.692 return_dict (`bool`, *optional*, defaults to `True`):693 Whether or not to return a [`~pipelines.stable_diffusion.StableDiffusionPipelineOutput`] instead of a694 plain tuple.695 callback (`Callable`, *optional*):696 A function that will be called every `callback_steps` steps during inference. The function will be697 called with the following arguments: `callback(step: int, timestep: int, latents: torch.Tensor)`.698 callback_steps (`int`, *optional*, defaults to 1):699 The frequency at which the `callback` function will be called. If not specified, the callback will be700 called at every step.701 Examples:702 ```py703 >>> import PIL704 >>> import requests705 >>> import torch706 >>> from io import BytesIO707 >>> from diffusers import StableDiffusionPipeline, RePaintScheduler708 >>> def download_image(url):709 ... response = requests.get(url)710 ... return PIL.Image.open(BytesIO(response.content)).convert("RGB")711 >>> base_url = "https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/"712 >>> img_url = base_url + "overture-creations-5sI6fQgYIuo.png"713 >>> mask_url = base_url + "overture-creations-5sI6fQgYIuo_mask.png "714 >>> init_image = download_image(img_url).resize((512, 512))715 >>> mask_image = download_image(mask_url).resize((512, 512))716 >>> pipe = DiffusionPipeline.from_pretrained(717 ... "CompVis/stable-diffusion-v1-4", torch_dtype=torch.float16, custom_pipeline="stable_diffusion_repaint",718 ... )719 >>> pipe.scheduler = RePaintScheduler.from_config(pipe.scheduler.config)720 >>> pipe = pipe.to("cuda")721 >>> prompt = "Face of a yellow cat, high resolution, sitting on a park bench"722 >>> image = pipe(prompt=prompt, image=init_image, mask_image=mask_image).images[0]723 ```724 Returns:725 [`~pipelines.stable_diffusion.StableDiffusionPipelineOutput`] or `tuple`:726 [`~pipelines.stable_diffusion.StableDiffusionPipelineOutput`] if `return_dict` is True, otherwise a `tuple.727 When returning a tuple, the first element is a list with the generated images, and the second element is a728 list of `bool`s denoting whether the corresponding generated image likely represents "not-safe-for-work"729 (nsfw) content, according to the `safety_checker`.730 """731 # 0. Default height and width to unet732 height = height or self.unet.config.sample_size * self.vae_scale_factor733 width = width or self.unet.config.sample_size * self.vae_scale_factor734 735 # 1. Check inputs736 self.check_inputs(737 prompt,738 height,739 width,740 callback_steps,741 negative_prompt,742 prompt_embeds,743 negative_prompt_embeds,744 )745 746 if image is None:747 raise ValueError("`image` input cannot be undefined.")748 749 if mask_image is None:750 raise ValueError("`mask_image` input cannot be undefined.")751 752 # 2. Define call parameters753 if prompt is not None and isinstance(prompt, str):754 batch_size = 1755 elif prompt is not None and isinstance(prompt, list):756 batch_size = len(prompt)757 else:758 batch_size = prompt_embeds.shape[0]759 760 device = self._execution_device761 # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2)762 # of the Imagen paper: https://huggingface.co/papers/2205.11487 . `guidance_scale = 1`763 # corresponds to doing no classifier free guidance.764 do_classifier_free_guidance = guidance_scale > 1.0765 766 # 3. Encode input prompt767 prompt_embeds = self._encode_prompt(768 prompt,769 device,770 num_images_per_prompt,771 do_classifier_free_guidance,772 negative_prompt,773 prompt_embeds=prompt_embeds,774 negative_prompt_embeds=negative_prompt_embeds,775 )776 777 # 4. Preprocess mask and image778 mask, masked_image = prepare_mask_and_masked_image(image, mask_image)779 780 # 5. set timesteps781 self.scheduler.set_timesteps(num_inference_steps, jump_length, jump_n_sample, device)782 self.scheduler.eta = eta783 784 timesteps = self.scheduler.timesteps785 # latent_timestep = timesteps[:1].repeat(batch_size * num_images_per_prompt)786 787 # 6. Prepare latent variables788 num_channels_latents = self.vae.config.latent_channels789 latents = self.prepare_latents(790 batch_size * num_images_per_prompt,791 num_channels_latents,792 height,793 width,794 prompt_embeds.dtype,795 device,796 generator,797 latents,798 )799 800 # 7. Prepare mask latent variables801 mask, masked_image_latents = self.prepare_mask_latents(802 mask,803 masked_image,804 batch_size * num_images_per_prompt,805 height,806 width,807 prompt_embeds.dtype,808 device,809 generator,810 do_classifier_free_guidance=False, # We do not need duplicate mask and image811 )812 813 # 8. Check that sizes of mask, masked image and latents match814 # num_channels_mask = mask.shape[1]815 # num_channels_masked_image = masked_image_latents.shape[1]816 if num_channels_latents != self.unet.config.in_channels:817 raise ValueError(818 f"Incorrect configuration settings! The config of `pipeline.unet`: {self.unet.config} expects"819 f" {self.unet.config.in_channels} but received `num_channels_latents`: {num_channels_latents} "820 f" = Please verify the config of"821 " `pipeline.unet` or your `mask_image` or `image` input."822 )823 824 # 9. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline825 extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)826 827 t_last = timesteps[0] + 1828 829 # 10. Denoising loop830 with self.progress_bar(total=len(timesteps)) as progress_bar:831 for i, t in enumerate(timesteps):832 if t >= t_last:833 # compute the reverse: x_t-1 -> x_t834 latents = self.scheduler.undo_step(latents, t_last, generator)835 progress_bar.update()836 t_last = t837 continue838 839 # expand the latents if we are doing classifier free guidance840 latent_model_input = torch.cat([latents] * 2) if do_classifier_free_guidance else latents841 842 # concat latents, mask, masked_image_latents in the channel dimension843 latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)844 # latent_model_input = torch.cat([latent_model_input, mask, masked_image_latents], dim=1)845 846 # predict the noise residual847 noise_pred = self.unet(latent_model_input, t, encoder_hidden_states=prompt_embeds).sample848 849 # perform guidance850 if do_classifier_free_guidance:851 noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)852 noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)853 854 # compute the previous noisy sample x_t -> x_t-1855 latents = self.scheduler.step(856 noise_pred,857 t,858 latents,859 masked_image_latents,860 mask,861 **extra_step_kwargs,862 ).prev_sample863 864 # call the callback, if provided865 progress_bar.update()866 if callback is not None and i % callback_steps == 0:867 step_idx = i // getattr(self.scheduler, "order", 1)868 callback(step_idx, t, latents)869 870 t_last = t871 872 # 11. Post-processing873 image = self.decode_latents(latents)874 875 # 12. Run safety checker876 image, has_nsfw_concept = self.run_safety_checker(image, device, prompt_embeds.dtype)877 878 # 13. Convert to PIL879 if output_type == "pil":880 image = self.numpy_to_pil(image)881 882 # Offload last model to CPU883 if hasattr(self, "final_offload_hook") and self.final_offload_hook is not None:884 self.final_offload_hook.offload()885 886 if not return_dict:887 return (image, has_nsfw_concept)888 889 return StableDiffusionPipelineOutput(images=image, nsfw_content_detected=has_nsfw_concept)890 