Spaces:
Sleeping
Sleeping
| import torch | |
| from typing import Any, Callable, Dict, List, Optional, Tuple, Union | |
| from diffusers import StableDiffusionXLImg2ImgPipeline | |
| from diffusers.utils.torch_utils import randn_tensor | |
| from diffusers.pipelines.stable_diffusion_xl.pipeline_stable_diffusion_xl import ( | |
| StableDiffusionXLPipelineOutput, | |
| retrieve_timesteps, | |
| PipelineImageInput, | |
| ) | |
| from src.exact_inversion import inversion_step, renoise_inversion_step | |
| class SDXLDDIMInversionPipeline(StableDiffusionXLImg2ImgPipeline): | |
| # @torch.no_grad() | |
| def __call__( | |
| self, | |
| prompt: Union[str, List[str]] = None, | |
| prompt_2: Optional[Union[str, List[str]]] = None, | |
| image: PipelineImageInput = None, | |
| strength: float = 0.3, | |
| num_inversion_steps: int = 50, | |
| timesteps: List[int] = None, | |
| denoising_start: Optional[float] = None, | |
| denoising_end: Optional[float] = None, | |
| guidance_scale: float = 1.0, | |
| negative_prompt: Optional[Union[str, List[str]]] = None, | |
| negative_prompt_2: Optional[Union[str, List[str]]] = None, | |
| num_images_per_prompt: Optional[int] = 1, | |
| eta: float = 0.0, | |
| generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None, | |
| latents: Optional[torch.FloatTensor] = None, | |
| prompt_embeds: Optional[torch.FloatTensor] = None, | |
| negative_prompt_embeds: Optional[torch.FloatTensor] = None, | |
| pooled_prompt_embeds: Optional[torch.FloatTensor] = None, | |
| negative_pooled_prompt_embeds: Optional[torch.FloatTensor] = None, | |
| ip_adapter_image: Optional[PipelineImageInput] = None, | |
| ip_adapter_image_embeds: Optional[torch.FloatTensor] = None, | |
| output_type: Optional[str] = "pil", | |
| return_dict: bool = True, | |
| cross_attention_kwargs: Optional[Dict[str, Any]] = None, | |
| guidance_rescale: float = 0.0, | |
| original_size: Tuple[int, int] = None, | |
| crops_coords_top_left: Tuple[int, int] = (0, 0), | |
| target_size: Tuple[int, int] = None, | |
| negative_original_size: Optional[Tuple[int, int]] = None, | |
| negative_crops_coords_top_left: Tuple[int, int] = (0, 0), | |
| negative_target_size: Optional[Tuple[int, int]] = None, | |
| aesthetic_score: float = 6.0, | |
| negative_aesthetic_score: float = 2.5, | |
| clip_skip: Optional[int] = None, | |
| callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None, | |
| callback_on_step_end_tensor_inputs: List[str] = ["latents"], | |
| num_gd_steps: int = 0, | |
| gd_step_size: float = 0.0, | |
| optimization_start: int = 0, | |
| normalize: bool = False, | |
| use_cfgpp: bool = False, | |
| **kwargs, | |
| ): | |
| if prompt is None: | |
| prompt_embeds = torch.zeros(1, 77, 2048) | |
| pooled_prompt_embeds = torch.zeros(1, 1280) | |
| else: | |
| prompt_embeds = None | |
| pooled_prompt_embeds = None | |
| callback = kwargs.pop("callback", None) | |
| callback_steps = kwargs.pop("callback_steps", None) | |
| if callback is not None: | |
| deprecate( | |
| "callback", | |
| "1.0.0", | |
| "Passing `callback` as an input argument to `__call__` is deprecated, consider use `callback_on_step_end`", | |
| ) | |
| if callback_steps is not None: | |
| deprecate( | |
| "callback_steps", | |
| "1.0.0", | |
| "Passing `callback_steps` as an input argument to `__call__` is deprecated, consider use `callback_on_step_end`", | |
| ) | |
| # 1. Check inputs. Raise error if not correct | |
| self.check_inputs( | |
| prompt, | |
| prompt_2, | |
| strength, | |
| num_inversion_steps, | |
| callback_steps, | |
| negative_prompt, | |
| negative_prompt_2, | |
| prompt_embeds, | |
| negative_prompt_embeds, | |
| callback_on_step_end_tensor_inputs, | |
| ) | |
| self._guidance_scale = guidance_scale | |
| self._guidance_rescale = guidance_rescale | |
| self._use_cfgpp = use_cfgpp | |
| self._clip_skip = clip_skip | |
| self._cross_attention_kwargs = cross_attention_kwargs | |
| self._denoising_end = denoising_end | |
| self._denoising_start = denoising_start | |
| # 2. Define call parameters | |
| if prompt is not None and isinstance(prompt, str): | |
| batch_size = 1 | |
| elif prompt is not None and isinstance(prompt, list): | |
| batch_size = len(prompt) | |
| else: | |
| batch_size = prompt_embeds.shape[0] | |
| device = self._execution_device | |
| # 3. Encode input prompt | |
| text_encoder_lora_scale = ( | |
| self.cross_attention_kwargs.get("scale", None) | |
| if self.cross_attention_kwargs is not None | |
| else None | |
| ) | |
| ( | |
| prompt_embeds, | |
| negative_prompt_embeds, | |
| pooled_prompt_embeds, | |
| negative_pooled_prompt_embeds, | |
| ) = self.encode_prompt( | |
| prompt=prompt, | |
| prompt_2=prompt_2, | |
| device=device, | |
| num_images_per_prompt=num_images_per_prompt, | |
| do_classifier_free_guidance=self.do_classifier_free_guidance, | |
| negative_prompt=negative_prompt, | |
| negative_prompt_2=negative_prompt_2, | |
| prompt_embeds=prompt_embeds, | |
| negative_prompt_embeds=negative_prompt_embeds, | |
| pooled_prompt_embeds=pooled_prompt_embeds, | |
| negative_pooled_prompt_embeds=negative_pooled_prompt_embeds, | |
| lora_scale=text_encoder_lora_scale, | |
| clip_skip=self.clip_skip, | |
| ) | |
| # 4. Preprocess image | |
| image = self.image_processor.preprocess(image) | |
| # 5. Prepare timesteps | |
| def denoising_value_valid(dnv): | |
| return isinstance(self.denoising_end, float) and 0 < dnv < 1 | |
| timesteps, num_inversion_steps = retrieve_timesteps( | |
| self.scheduler, num_inversion_steps, device, timesteps | |
| ) | |
| timesteps, num_inversion_steps = self.get_timesteps( | |
| num_inversion_steps, | |
| strength, | |
| device, | |
| denoising_start=self.denoising_start if denoising_value_valid else None, | |
| ) | |
| # 6. Prepare latent variables | |
| with torch.no_grad(): | |
| latents = self.prepare_latents( | |
| image, | |
| None, | |
| batch_size, | |
| num_images_per_prompt, | |
| prompt_embeds.dtype, | |
| device, | |
| generator, | |
| False, | |
| ) | |
| # 7. Prepare extra step kwargs. | |
| extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta) | |
| height, width = latents.shape[-2:] | |
| height = height * self.vae_scale_factor | |
| width = width * self.vae_scale_factor | |
| original_size = original_size or (height, width) | |
| target_size = target_size or (height, width) | |
| # 8. Prepare added time ids & embeddings | |
| if negative_original_size is None: | |
| negative_original_size = original_size | |
| if negative_target_size is None: | |
| negative_target_size = target_size | |
| add_text_embeds = pooled_prompt_embeds | |
| if self.text_encoder_2 is None: | |
| text_encoder_projection_dim = int(pooled_prompt_embeds.shape[-1]) | |
| else: | |
| text_encoder_projection_dim = self.text_encoder_2.config.projection_dim | |
| add_time_ids, add_neg_time_ids = self._get_add_time_ids( | |
| original_size, | |
| crops_coords_top_left, | |
| target_size, | |
| aesthetic_score, | |
| negative_aesthetic_score, | |
| negative_original_size, | |
| negative_crops_coords_top_left, | |
| negative_target_size, | |
| dtype=prompt_embeds.dtype, | |
| text_encoder_projection_dim=text_encoder_projection_dim, | |
| ) | |
| add_time_ids = add_time_ids.repeat(batch_size * num_images_per_prompt, 1) | |
| if self.do_classifier_free_guidance: | |
| prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds], dim=0) | |
| add_text_embeds = torch.cat( | |
| [negative_pooled_prompt_embeds, add_text_embeds], dim=0 | |
| ) | |
| add_neg_time_ids = add_neg_time_ids.repeat( | |
| batch_size * num_images_per_prompt, 1 | |
| ) | |
| add_time_ids = torch.cat([add_neg_time_ids, add_time_ids], dim=0) | |
| prompt_embeds = prompt_embeds.to(device) | |
| add_text_embeds = add_text_embeds.to(device) | |
| add_time_ids = add_time_ids.to(device) | |
| if ip_adapter_image is not None: | |
| ip_adapter_image_embeds, ip_adapter_negative_image_embeds = ( | |
| self.encode_image(ip_adapter_image, device, num_images_per_prompt) | |
| ) | |
| if self.do_classifier_free_guidance: | |
| ip_adapter_image_embeds = torch.cat( | |
| [ip_adapter_negative_image_embeds, ip_adapter_image_embeds] | |
| ) | |
| ip_adapter_image_embeds = ip_adapter_image_embeds.to(device) | |
| # 9. Denoising loop | |
| num_warmup_steps = max( | |
| len(timesteps) - num_inversion_steps * self.scheduler.order, 0 | |
| ) | |
| self._num_timesteps = len(timesteps) | |
| self.z_0 = torch.clone(latents) | |
| self.noise = randn_tensor( | |
| self.z_0.shape, | |
| generator=generator, | |
| device=self.z_0.device, | |
| dtype=self.z_0.dtype, | |
| ) | |
| all_latents = [latents.clone()] | |
| with self.progress_bar(total=num_inversion_steps) as progress_bar: | |
| for i, t in enumerate(reversed(timesteps)): | |
| added_cond_kwargs = { | |
| "text_embeds": add_text_embeds, | |
| "time_ids": add_time_ids, | |
| } | |
| if ip_adapter_image_embeds is not None: | |
| added_cond_kwargs["image_embeds"] = ip_adapter_image_embeds | |
| if self.cfg.renoise: | |
| recon_diff = None | |
| latents = renoise_inversion_step( | |
| self, | |
| latents, | |
| t, | |
| prompt_embeds, | |
| added_cond_kwargs, | |
| generator=generator, | |
| use_cfgpp=use_cfgpp, | |
| ) | |
| else: | |
| latents, recon_diff = inversion_step( | |
| self, | |
| latents, | |
| t, | |
| prompt_embeds, | |
| added_cond_kwargs, | |
| num_gd_steps=num_gd_steps, | |
| gd_step_size=gd_step_size, | |
| optimization_start=optimization_start, | |
| normalize=normalize, | |
| use_cfgpp=use_cfgpp, | |
| generator=generator, | |
| ) | |
| all_latents.append(latents.clone()) | |
| if callback_on_step_end is not None: | |
| callback_kwargs = {} | |
| for k in callback_on_step_end_tensor_inputs: | |
| callback_kwargs[k] = locals()[k] | |
| callback_outputs = callback_on_step_end(self, i, t, callback_kwargs) | |
| latents = callback_outputs.pop("latents", latents) | |
| prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds) | |
| negative_prompt_embeds = callback_outputs.pop( | |
| "negative_prompt_embeds", negative_prompt_embeds | |
| ) | |
| add_text_embeds = callback_outputs.pop( | |
| "add_text_embeds", add_text_embeds | |
| ) | |
| negative_pooled_prompt_embeds = callback_outputs.pop( | |
| "negative_pooled_prompt_embeds", negative_pooled_prompt_embeds | |
| ) | |
| add_time_ids = callback_outputs.pop("add_time_ids", add_time_ids) | |
| add_neg_time_ids = callback_outputs.pop( | |
| "add_neg_time_ids", add_neg_time_ids | |
| ) | |
| # call the callback, if provided | |
| if i == len(timesteps) - 1 or ( | |
| (i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0 | |
| ): | |
| progress_bar.update() | |
| if callback is not None and i % callback_steps == 0: | |
| step_idx = i // getattr(self.scheduler, "order", 1) | |
| callback(step_idx, t, latents) | |
| image = latents | |
| # Offload all models | |
| self.maybe_free_model_hooks() | |
| return StableDiffusionXLPipelineOutput(images=image), all_latents | |
| def do_classifier_free_guidance(self): | |
| return ( | |
| self._guidance_scale > 1 and self.unet.config.time_cond_proj_dim is None | |
| ) or self._use_cfgpp | |
| _callback_tensor_inputs = [ | |
| "latents", | |
| "recon_diff" "prompt_embeds", | |
| "negative_prompt_embeds", | |
| "add_text_embeds", | |
| "add_time_ids", | |
| "negative_pooled_prompt_embeds", | |
| "add_neg_time_ids", | |
| ] | |