mirror of
https://github.com/vladmandic/automatic
synced 2026-09-19 09:14:35 +02:00
add meissonic (unstable)
Signed-off-by: Vladimir Mandic <mandic00@live.com>
This commit is contained in:
@@ -0,0 +1,373 @@
|
||||
# Copyright 2024 The HuggingFace Team and The MeissonFlow Team. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import sys
|
||||
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
||||
|
||||
import torch
|
||||
from transformers import CLIPTextModelWithProjection, CLIPTokenizer
|
||||
|
||||
from diffusers.image_processor import VaeImageProcessor
|
||||
from diffusers.models import VQModel
|
||||
|
||||
from .scheduler import Scheduler
|
||||
from diffusers.utils import replace_example_docstring
|
||||
from diffusers.pipelines.pipeline_utils import DiffusionPipeline, ImagePipelineOutput
|
||||
|
||||
from .transformer import Transformer2DModel
|
||||
|
||||
|
||||
EXAMPLE_DOC_STRING = """
|
||||
Examples:
|
||||
```py
|
||||
>>> image = pipe(prompt).images[0]
|
||||
```
|
||||
"""
|
||||
|
||||
|
||||
def _prepare_latent_image_ids(batch_size, height, width, device, dtype):
|
||||
latent_image_ids = torch.zeros(height // 2, width // 2, 3)
|
||||
latent_image_ids[..., 1] = latent_image_ids[..., 1] + torch.arange(height // 2)[:, None]
|
||||
latent_image_ids[..., 2] = latent_image_ids[..., 2] + torch.arange(width // 2)[None, :]
|
||||
|
||||
latent_image_id_height, latent_image_id_width, latent_image_id_channels = latent_image_ids.shape
|
||||
|
||||
latent_image_ids = latent_image_ids.reshape(
|
||||
latent_image_id_height * latent_image_id_width, latent_image_id_channels
|
||||
)
|
||||
|
||||
return latent_image_ids.to(device=device, dtype=dtype)
|
||||
|
||||
|
||||
class Pipeline(DiffusionPipeline):
|
||||
image_processor: VaeImageProcessor
|
||||
vqvae: VQModel
|
||||
tokenizer: CLIPTokenizer
|
||||
text_encoder: CLIPTextModelWithProjection
|
||||
transformer: Transformer2DModel
|
||||
scheduler: Scheduler
|
||||
# tokenizer_t5: T5Tokenizer
|
||||
# text_encoder_t5: T5ForConditionalGeneration
|
||||
|
||||
model_cpu_offload_seq = "text_encoder->transformer->vqvae"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
vqvae: VQModel,
|
||||
tokenizer: CLIPTokenizer,
|
||||
text_encoder: CLIPTextModelWithProjection,
|
||||
transformer: Transformer2DModel,
|
||||
scheduler: Scheduler,
|
||||
# tokenizer_t5: T5Tokenizer,
|
||||
# text_encoder_t5: T5ForConditionalGeneration,
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
self.register_modules(
|
||||
vqvae=vqvae,
|
||||
tokenizer=tokenizer,
|
||||
text_encoder=text_encoder,
|
||||
transformer=transformer,
|
||||
scheduler=scheduler,
|
||||
# tokenizer_t5=tokenizer_t5,
|
||||
# text_encoder_t5=text_encoder_t5,
|
||||
)
|
||||
self.vae_scale_factor = 2 ** (len(self.vqvae.config.block_out_channels) - 1)
|
||||
self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor, do_normalize=False)
|
||||
|
||||
@torch.no_grad()
|
||||
@replace_example_docstring(EXAMPLE_DOC_STRING)
|
||||
def __call__(
|
||||
self,
|
||||
prompt: Optional[Union[List[str], str]] = None,
|
||||
height: Optional[int] = 1024,
|
||||
width: Optional[int] = 1024,
|
||||
num_inference_steps: int = 48,
|
||||
guidance_scale: float = 9.0,
|
||||
negative_prompt: Optional[Union[str, List[str]]] = None,
|
||||
num_images_per_prompt: Optional[int] = 1,
|
||||
generator: Optional[torch.Generator] = None,
|
||||
latents: Optional[torch.IntTensor] = None,
|
||||
prompt_embeds: Optional[torch.Tensor] = None,
|
||||
encoder_hidden_states: Optional[torch.Tensor] = None,
|
||||
negative_prompt_embeds: Optional[torch.Tensor] = None,
|
||||
negative_encoder_hidden_states: Optional[torch.Tensor] = None,
|
||||
output_type="pil",
|
||||
return_dict: bool = True,
|
||||
callback: Optional[Callable[[int, int, torch.Tensor], None]] = None,
|
||||
callback_steps: int = 1,
|
||||
cross_attention_kwargs: Optional[Dict[str, Any]] = None,
|
||||
micro_conditioning_aesthetic_score: int = 6,
|
||||
micro_conditioning_crop_coord: Tuple[int, int] = (0, 0),
|
||||
temperature: Union[int, Tuple[int, int], List[int]] = (2, 0),
|
||||
):
|
||||
"""
|
||||
The call function to the pipeline for generation.
|
||||
|
||||
Args:
|
||||
prompt (`str` or `List[str]`, *optional*):
|
||||
The prompt or prompts to guide image generation. If not defined, you need to pass `prompt_embeds`.
|
||||
height (`int`, *optional*, defaults to `self.transformer.config.sample_size * self.vae_scale_factor`):
|
||||
The height in pixels of the generated image.
|
||||
width (`int`, *optional*, defaults to `self.unet.config.sample_size * self.vae_scale_factor`):
|
||||
The width in pixels of the generated image.
|
||||
num_inference_steps (`int`, *optional*, defaults to 16):
|
||||
The number of denoising steps. More denoising steps usually lead to a higher quality image at the
|
||||
expense of slower inference.
|
||||
guidance_scale (`float`, *optional*, defaults to 10.0):
|
||||
A higher guidance scale value encourages the model to generate images closely linked to the text
|
||||
`prompt` at the expense of lower image quality. Guidance scale is enabled when `guidance_scale > 1`.
|
||||
negative_prompt (`str` or `List[str]`, *optional*):
|
||||
The prompt or prompts to guide what to not include in image generation. If not defined, you need to
|
||||
pass `negative_prompt_embeds` instead. Ignored when not using guidance (`guidance_scale < 1`).
|
||||
num_images_per_prompt (`int`, *optional*, defaults to 1):
|
||||
The number of images to generate per prompt.
|
||||
generator (`torch.Generator`, *optional*):
|
||||
A [`torch.Generator`](https://pytorch.org/docs/stable/generated/torch.Generator.html) to make
|
||||
generation deterministic.
|
||||
latents (`torch.IntTensor`, *optional*):
|
||||
Pre-generated tokens representing latent vectors in `self.vqvae`, to be used as inputs for image
|
||||
gneration. If not provided, the starting latents will be completely masked.
|
||||
prompt_embeds (`torch.Tensor`, *optional*):
|
||||
Pre-generated text embeddings. Can be used to easily tweak text inputs (prompt weighting). If not
|
||||
provided, text embeddings are generated from the `prompt` input argument. A single vector from the
|
||||
pooled and projected final hidden states.
|
||||
encoder_hidden_states (`torch.Tensor`, *optional*):
|
||||
Pre-generated penultimate hidden states from the text encoder providing additional text conditioning.
|
||||
negative_prompt_embeds (`torch.Tensor`, *optional*):
|
||||
Pre-generated negative text embeddings. Can be used to easily tweak text inputs (prompt weighting). If
|
||||
not provided, `negative_prompt_embeds` are generated from the `negative_prompt` input argument.
|
||||
negative_encoder_hidden_states (`torch.Tensor`, *optional*):
|
||||
Analogous to `encoder_hidden_states` for the positive prompt.
|
||||
output_type (`str`, *optional*, defaults to `"pil"`):
|
||||
The output format of the generated image. Choose between `PIL.Image` or `np.array`.
|
||||
return_dict (`bool`, *optional*, defaults to `True`):
|
||||
Whether or not to return a [`~pipelines.stable_diffusion.StableDiffusionPipelineOutput`] instead of a
|
||||
plain tuple.
|
||||
callback (`Callable`, *optional*):
|
||||
A function that calls every `callback_steps` steps during inference. The function is called with the
|
||||
following arguments: `callback(step: int, timestep: int, latents: torch.Tensor)`.
|
||||
callback_steps (`int`, *optional*, defaults to 1):
|
||||
The frequency at which the `callback` function is called. If not specified, the callback is called at
|
||||
every step.
|
||||
cross_attention_kwargs (`dict`, *optional*):
|
||||
A kwargs dictionary that if specified is passed along to the [`AttentionProcessor`] as defined in
|
||||
[`self.processor`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
|
||||
micro_conditioning_aesthetic_score (`int`, *optional*, defaults to 6):
|
||||
The targeted aesthetic score according to the laion aesthetic classifier. See
|
||||
https://laion.ai/blog/laion-aesthetics/ and the micro-conditioning section of
|
||||
https://arxiv.org/abs/2307.01952.
|
||||
micro_conditioning_crop_coord (`Tuple[int]`, *optional*, defaults to (0, 0)):
|
||||
The targeted height, width crop coordinates. See the micro-conditioning section of
|
||||
https://arxiv.org/abs/2307.01952.
|
||||
temperature (`Union[int, Tuple[int, int], List[int]]`, *optional*, defaults to (2, 0)):
|
||||
Configures the temperature scheduler on `self.scheduler` see `Scheduler#set_timesteps`.
|
||||
|
||||
Examples:
|
||||
|
||||
Returns:
|
||||
[`~pipelines.pipeline_utils.ImagePipelineOutput`] or `tuple`:
|
||||
If `return_dict` is `True`, [`~pipelines.pipeline_utils.ImagePipelineOutput`] is returned, otherwise a
|
||||
`tuple` is returned where the first element is a list with the generated images.
|
||||
"""
|
||||
if (prompt_embeds is not None and encoder_hidden_states is None) or (
|
||||
prompt_embeds is None and encoder_hidden_states is not None
|
||||
):
|
||||
raise ValueError("pass either both `prompt_embeds` and `encoder_hidden_states` or neither")
|
||||
|
||||
if (negative_prompt_embeds is not None and negative_encoder_hidden_states is None) or (
|
||||
negative_prompt_embeds is None and negative_encoder_hidden_states is not None
|
||||
):
|
||||
raise ValueError(
|
||||
"pass either both `negatve_prompt_embeds` and `negative_encoder_hidden_states` or neither"
|
||||
)
|
||||
|
||||
if (prompt is None and prompt_embeds is None) or (prompt is not None and prompt_embeds is not None):
|
||||
raise ValueError("pass only one of `prompt` or `prompt_embeds`")
|
||||
|
||||
if isinstance(prompt, str):
|
||||
prompt = [prompt]
|
||||
|
||||
if prompt is not None:
|
||||
batch_size = len(prompt)
|
||||
else:
|
||||
batch_size = prompt_embeds.shape[0]
|
||||
|
||||
batch_size = batch_size * num_images_per_prompt
|
||||
|
||||
if height is None:
|
||||
height = self.transformer.config.sample_size * self.vae_scale_factor
|
||||
|
||||
if width is None:
|
||||
width = self.transformer.config.sample_size * self.vae_scale_factor
|
||||
|
||||
if prompt_embeds is None:
|
||||
input_ids = self.tokenizer(
|
||||
prompt,
|
||||
return_tensors="pt",
|
||||
padding="max_length",
|
||||
truncation=True,
|
||||
max_length=77, #self.tokenizer.model_max_length,
|
||||
).input_ids.to(self._execution_device)
|
||||
# input_ids_t5 = self.tokenizer_t5(
|
||||
# prompt,
|
||||
# return_tensors="pt",
|
||||
# padding="max_length",
|
||||
# truncation=True,
|
||||
# max_length=512,
|
||||
# ).input_ids.to(self._execution_device)
|
||||
|
||||
|
||||
outputs = self.text_encoder(input_ids, return_dict=True, output_hidden_states=True)
|
||||
# outputs_t5 = self.text_encoder_t5(input_ids_t5, decoder_input_ids = input_ids_t5 ,return_dict=True, output_hidden_states=True)
|
||||
prompt_embeds = outputs.text_embeds
|
||||
encoder_hidden_states = outputs.hidden_states[-2]
|
||||
# encoder_hidden_states = outputs_t5.encoder_hidden_states[-2]
|
||||
|
||||
prompt_embeds = prompt_embeds.repeat(num_images_per_prompt, 1)
|
||||
encoder_hidden_states = encoder_hidden_states.repeat(num_images_per_prompt, 1, 1)
|
||||
|
||||
if guidance_scale > 1.0:
|
||||
if negative_prompt_embeds is None:
|
||||
if negative_prompt is None:
|
||||
negative_prompt = [""] * len(prompt)
|
||||
|
||||
if isinstance(negative_prompt, str):
|
||||
negative_prompt = [negative_prompt]
|
||||
|
||||
input_ids = self.tokenizer(
|
||||
negative_prompt,
|
||||
return_tensors="pt",
|
||||
padding="max_length",
|
||||
truncation=True,
|
||||
max_length=77, #self.tokenizer.model_max_length,
|
||||
).input_ids.to(self._execution_device)
|
||||
# input_ids_t5 = self.tokenizer_t5(
|
||||
# prompt,
|
||||
# return_tensors="pt",
|
||||
# padding="max_length",
|
||||
# truncation=True,
|
||||
# max_length=512,
|
||||
# ).input_ids.to(self._execution_device)
|
||||
|
||||
outputs = self.text_encoder(input_ids, return_dict=True, output_hidden_states=True)
|
||||
# outputs_t5 = self.text_encoder_t5(input_ids_t5, decoder_input_ids = input_ids_t5 ,return_dict=True, output_hidden_states=True)
|
||||
negative_prompt_embeds = outputs.text_embeds
|
||||
negative_encoder_hidden_states = outputs.hidden_states[-2]
|
||||
# negative_encoder_hidden_states = outputs_t5.encoder_hidden_states[-2]
|
||||
|
||||
|
||||
|
||||
negative_prompt_embeds = negative_prompt_embeds.repeat(num_images_per_prompt, 1)
|
||||
negative_encoder_hidden_states = negative_encoder_hidden_states.repeat(num_images_per_prompt, 1, 1)
|
||||
|
||||
prompt_embeds = torch.concat([negative_prompt_embeds, prompt_embeds])
|
||||
encoder_hidden_states = torch.concat([negative_encoder_hidden_states, encoder_hidden_states])
|
||||
|
||||
# Note that the micro conditionings _do_ flip the order of width, height for the original size
|
||||
# and the crop coordinates. This is how it was done in the original code base
|
||||
micro_conds = torch.tensor(
|
||||
[
|
||||
width,
|
||||
height,
|
||||
micro_conditioning_crop_coord[0],
|
||||
micro_conditioning_crop_coord[1],
|
||||
micro_conditioning_aesthetic_score,
|
||||
],
|
||||
device=self._execution_device,
|
||||
dtype=encoder_hidden_states.dtype,
|
||||
)
|
||||
micro_conds = micro_conds.unsqueeze(0)
|
||||
micro_conds = micro_conds.expand(2 * batch_size if guidance_scale > 1.0 else batch_size, -1)
|
||||
|
||||
shape = (batch_size, height // self.vae_scale_factor, width // self.vae_scale_factor)
|
||||
|
||||
if latents is None:
|
||||
latents = torch.full(
|
||||
shape, self.scheduler.config.mask_token_id, dtype=torch.long, device=self._execution_device
|
||||
)
|
||||
|
||||
self.scheduler.set_timesteps(num_inference_steps, temperature, self._execution_device)
|
||||
|
||||
num_warmup_steps = len(self.scheduler.timesteps) - num_inference_steps * self.scheduler.order
|
||||
with self.progress_bar(total=num_inference_steps) as progress_bar:
|
||||
for i, timestep in enumerate(self.scheduler.timesteps):
|
||||
if guidance_scale > 1.0:
|
||||
model_input = torch.cat([latents] * 2)
|
||||
else:
|
||||
model_input = latents
|
||||
if height == 1024: #args.resolution == 1024:
|
||||
img_ids = _prepare_latent_image_ids(model_input.shape[0], model_input.shape[-2],model_input.shape[-1],model_input.device,model_input.dtype)
|
||||
else:
|
||||
img_ids = _prepare_latent_image_ids(model_input.shape[0],2*model_input.shape[-2],2*model_input.shape[-1],model_input.device,model_input.dtype)
|
||||
txt_ids = torch.zeros(encoder_hidden_states.shape[1],3).to(device = encoder_hidden_states.device, dtype = encoder_hidden_states.dtype)
|
||||
model_output = self.transformer(
|
||||
hidden_states = model_input,
|
||||
micro_conds=micro_conds,
|
||||
pooled_projections=prompt_embeds,
|
||||
encoder_hidden_states=encoder_hidden_states,
|
||||
img_ids = img_ids,
|
||||
txt_ids = txt_ids,
|
||||
timestep = torch.tensor([timestep], device=model_input.device, dtype=torch.long),
|
||||
# guidance = 7,
|
||||
# cross_attention_kwargs=cross_attention_kwargs,
|
||||
)
|
||||
|
||||
if guidance_scale > 1.0:
|
||||
uncond_logits, cond_logits = model_output.chunk(2)
|
||||
model_output = uncond_logits + guidance_scale * (cond_logits - uncond_logits)
|
||||
|
||||
latents = self.scheduler.step(
|
||||
model_output=model_output,
|
||||
timestep=timestep,
|
||||
sample=latents,
|
||||
generator=generator,
|
||||
).prev_sample
|
||||
|
||||
if i == len(self.scheduler.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, timestep, latents)
|
||||
|
||||
if output_type == "latent":
|
||||
output = latents
|
||||
else:
|
||||
needs_upcasting = self.vqvae.dtype == torch.float16 and self.vqvae.config.force_upcast
|
||||
|
||||
if needs_upcasting:
|
||||
self.vqvae.float()
|
||||
|
||||
output = self.vqvae.decode(
|
||||
latents,
|
||||
force_not_quantize=True,
|
||||
shape=(
|
||||
batch_size,
|
||||
height // self.vae_scale_factor,
|
||||
width // self.vae_scale_factor,
|
||||
self.vqvae.config.latent_channels,
|
||||
),
|
||||
).sample.clip(0, 1)
|
||||
output = self.image_processor.postprocess(output, output_type)
|
||||
|
||||
if needs_upcasting:
|
||||
self.vqvae.half()
|
||||
|
||||
self.maybe_free_model_hooks()
|
||||
|
||||
if not return_dict:
|
||||
return (output,)
|
||||
|
||||
return ImagePipelineOutput(output)
|
||||
@@ -0,0 +1,353 @@
|
||||
# Copyright 2024 The HuggingFace Team and The MeissonFlow Team. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
||||
|
||||
import torch
|
||||
from transformers import CLIPTextModelWithProjection, CLIPTokenizer
|
||||
|
||||
from diffusers.image_processor import PipelineImageInput, VaeImageProcessor
|
||||
from diffusers.models import UVit2DModel, VQModel
|
||||
# from diffusers.schedulers import AmusedScheduler
|
||||
from .scheduler import Scheduler
|
||||
from diffusers.utils import replace_example_docstring
|
||||
from diffusers.pipelines.pipeline_utils import DiffusionPipeline, ImagePipelineOutput
|
||||
|
||||
from .transformer import Transformer2DModel
|
||||
|
||||
EXAMPLE_DOC_STRING = """
|
||||
Examples:
|
||||
```py
|
||||
>>> image = pipe(prompt, input_image).images[0]
|
||||
```
|
||||
"""
|
||||
def _prepare_latent_image_ids(batch_size, height, width, device, dtype):
|
||||
latent_image_ids = torch.zeros(height // 2, width // 2, 3)
|
||||
latent_image_ids[..., 1] = latent_image_ids[..., 1] + torch.arange(height // 2)[:, None]
|
||||
latent_image_ids[..., 2] = latent_image_ids[..., 2] + torch.arange(width // 2)[None, :]
|
||||
|
||||
latent_image_id_height, latent_image_id_width, latent_image_id_channels = latent_image_ids.shape
|
||||
|
||||
latent_image_ids = latent_image_ids.reshape(
|
||||
latent_image_id_height * latent_image_id_width, latent_image_id_channels
|
||||
)
|
||||
# latent_image_ids = latent_image_ids.unsqueeze(0).repeat(batch_size, 1, 1)
|
||||
|
||||
return latent_image_ids.to(device=device, dtype=dtype)
|
||||
|
||||
|
||||
class Img2ImgPipeline(DiffusionPipeline):
|
||||
image_processor: VaeImageProcessor
|
||||
vqvae: VQModel
|
||||
tokenizer: CLIPTokenizer
|
||||
text_encoder: CLIPTextModelWithProjection
|
||||
transformer: Transformer2DModel #UVit2DModel
|
||||
scheduler: Scheduler
|
||||
|
||||
model_cpu_offload_seq = "text_encoder->transformer->vqvae"
|
||||
|
||||
# TODO - when calling self.vqvae.quantize, it uses self.vqvae.quantize.embedding.weight before
|
||||
# the forward method of self.vqvae.quantize, so the hook doesn't get called to move the parameter
|
||||
# off the meta device. There should be a way to fix this instead of just not offloading it
|
||||
_exclude_from_cpu_offload = ["vqvae"]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
vqvae: VQModel,
|
||||
tokenizer: CLIPTokenizer,
|
||||
text_encoder: CLIPTextModelWithProjection,
|
||||
transformer: Transformer2DModel, #UVit2DModel,
|
||||
scheduler: Scheduler,
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
self.register_modules(
|
||||
vqvae=vqvae,
|
||||
tokenizer=tokenizer,
|
||||
text_encoder=text_encoder,
|
||||
transformer=transformer,
|
||||
scheduler=scheduler,
|
||||
)
|
||||
self.vae_scale_factor = 2 ** (len(self.vqvae.config.block_out_channels) - 1)
|
||||
self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor, do_normalize=False)
|
||||
|
||||
@torch.no_grad()
|
||||
@replace_example_docstring(EXAMPLE_DOC_STRING)
|
||||
def __call__(
|
||||
self,
|
||||
prompt: Optional[Union[List[str], str]] = None,
|
||||
image: PipelineImageInput = None,
|
||||
strength: float = 0.5,
|
||||
num_inference_steps: int = 12,
|
||||
guidance_scale: float = 10.0,
|
||||
negative_prompt: Optional[Union[str, List[str]]] = None,
|
||||
num_images_per_prompt: Optional[int] = 1,
|
||||
generator: Optional[torch.Generator] = None,
|
||||
prompt_embeds: Optional[torch.Tensor] = None,
|
||||
encoder_hidden_states: Optional[torch.Tensor] = None,
|
||||
negative_prompt_embeds: Optional[torch.Tensor] = None,
|
||||
negative_encoder_hidden_states: Optional[torch.Tensor] = None,
|
||||
output_type="pil",
|
||||
return_dict: bool = True,
|
||||
callback: Optional[Callable[[int, int, torch.Tensor], None]] = None,
|
||||
callback_steps: int = 1,
|
||||
cross_attention_kwargs: Optional[Dict[str, Any]] = None,
|
||||
micro_conditioning_aesthetic_score: int = 6,
|
||||
micro_conditioning_crop_coord: Tuple[int, int] = (0, 0),
|
||||
temperature: Union[int, Tuple[int, int], List[int]] = (2, 0),
|
||||
):
|
||||
"""
|
||||
The call function to the pipeline for generation.
|
||||
|
||||
Args:
|
||||
prompt (`str` or `List[str]`, *optional*):
|
||||
The prompt or prompts to guide image generation. If not defined, you need to pass `prompt_embeds`.
|
||||
image (`torch.Tensor`, `PIL.Image.Image`, `np.ndarray`, `List[torch.Tensor]`, `List[PIL.Image.Image]`, or `List[np.ndarray]`):
|
||||
`Image`, numpy array or tensor representing an image batch to be used as the starting point. For both
|
||||
numpy array and pytorch tensor, the expected value range is between `[0, 1]` If it's a tensor or a list
|
||||
or tensors, the expected shape should be `(B, C, H, W)` or `(C, H, W)`. If it is a numpy array or a
|
||||
list of arrays, the expected shape should be `(B, H, W, C)` or `(H, W, C)` It can also accept image
|
||||
latents as `image`, but if passing latents directly it is not encoded again.
|
||||
strength (`float`, *optional*, defaults to 0.5):
|
||||
Indicates extent to transform the reference `image`. Must be between 0 and 1. `image` is used as a
|
||||
starting point and more noise is added the higher the `strength`. The number of denoising steps depends
|
||||
on the amount of noise initially added. When `strength` is 1, added noise is maximum and the denoising
|
||||
process runs for the full number of iterations specified in `num_inference_steps`. A value of 1
|
||||
essentially ignores `image`.
|
||||
num_inference_steps (`int`, *optional*, defaults to 12):
|
||||
The number of denoising steps. More denoising steps usually lead to a higher quality image at the
|
||||
expense of slower inference.
|
||||
guidance_scale (`float`, *optional*, defaults to 10.0):
|
||||
A higher guidance scale value encourages the model to generate images closely linked to the text
|
||||
`prompt` at the expense of lower image quality. Guidance scale is enabled when `guidance_scale > 1`.
|
||||
negative_prompt (`str` or `List[str]`, *optional*):
|
||||
The prompt or prompts to guide what to not include in image generation. If not defined, you need to
|
||||
pass `negative_prompt_embeds` instead. Ignored when not using guidance (`guidance_scale < 1`).
|
||||
num_images_per_prompt (`int`, *optional*, defaults to 1):
|
||||
The number of images to generate per prompt.
|
||||
generator (`torch.Generator`, *optional*):
|
||||
A [`torch.Generator`](https://pytorch.org/docs/stable/generated/torch.Generator.html) to make
|
||||
generation deterministic.
|
||||
prompt_embeds (`torch.Tensor`, *optional*):
|
||||
Pre-generated text embeddings. Can be used to easily tweak text inputs (prompt weighting). If not
|
||||
provided, text embeddings are generated from the `prompt` input argument. A single vector from the
|
||||
pooled and projected final hidden states.
|
||||
encoder_hidden_states (`torch.Tensor`, *optional*):
|
||||
Pre-generated penultimate hidden states from the text encoder providing additional text conditioning.
|
||||
negative_prompt_embeds (`torch.Tensor`, *optional*):
|
||||
Pre-generated negative text embeddings. Can be used to easily tweak text inputs (prompt weighting). If
|
||||
not provided, `negative_prompt_embeds` are generated from the `negative_prompt` input argument.
|
||||
negative_encoder_hidden_states (`torch.Tensor`, *optional*):
|
||||
Analogous to `encoder_hidden_states` for the positive prompt.
|
||||
output_type (`str`, *optional*, defaults to `"pil"`):
|
||||
The output format of the generated image. Choose between `PIL.Image` or `np.array`.
|
||||
return_dict (`bool`, *optional*, defaults to `True`):
|
||||
Whether or not to return a [`~pipelines.stable_diffusion.StableDiffusionPipelineOutput`] instead of a
|
||||
plain tuple.
|
||||
callback (`Callable`, *optional*):
|
||||
A function that calls every `callback_steps` steps during inference. The function is called with the
|
||||
following arguments: `callback(step: int, timestep: int, latents: torch.Tensor)`.
|
||||
callback_steps (`int`, *optional*, defaults to 1):
|
||||
The frequency at which the `callback` function is called. If not specified, the callback is called at
|
||||
every step.
|
||||
cross_attention_kwargs (`dict`, *optional*):
|
||||
A kwargs dictionary that if specified is passed along to the [`AttentionProcessor`] as defined in
|
||||
[`self.processor`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
|
||||
micro_conditioning_aesthetic_score (`int`, *optional*, defaults to 6):
|
||||
The targeted aesthetic score according to the laion aesthetic classifier. See
|
||||
https://laion.ai/blog/laion-aesthetics/ and the micro-conditioning section of
|
||||
https://arxiv.org/abs/2307.01952.
|
||||
micro_conditioning_crop_coord (`Tuple[int]`, *optional*, defaults to (0, 0)):
|
||||
The targeted height, width crop coordinates. See the micro-conditioning section of
|
||||
https://arxiv.org/abs/2307.01952.
|
||||
temperature (`Union[int, Tuple[int, int], List[int]]`, *optional*, defaults to (2, 0)):
|
||||
Configures the temperature scheduler on `self.scheduler` see `AmusedScheduler#set_timesteps`.
|
||||
|
||||
Examples:
|
||||
|
||||
Returns:
|
||||
[`~pipelines.pipeline_utils.ImagePipelineOutput`] or `tuple`:
|
||||
If `return_dict` is `True`, [`~pipelines.pipeline_utils.ImagePipelineOutput`] is returned, otherwise a
|
||||
`tuple` is returned where the first element is a list with the generated images.
|
||||
"""
|
||||
|
||||
if (prompt_embeds is not None and encoder_hidden_states is None) or (
|
||||
prompt_embeds is None and encoder_hidden_states is not None
|
||||
):
|
||||
raise ValueError("pass either both `prompt_embeds` and `encoder_hidden_states` or neither")
|
||||
|
||||
if (negative_prompt_embeds is not None and negative_encoder_hidden_states is None) or (
|
||||
negative_prompt_embeds is None and negative_encoder_hidden_states is not None
|
||||
):
|
||||
raise ValueError(
|
||||
"pass either both `negative_prompt_embeds` and `negative_encoder_hidden_states` or neither"
|
||||
)
|
||||
|
||||
if (prompt is None and prompt_embeds is None) or (prompt is not None and prompt_embeds is not None):
|
||||
raise ValueError("pass only one of `prompt` or `prompt_embeds`")
|
||||
|
||||
if isinstance(prompt, str):
|
||||
prompt = [prompt]
|
||||
|
||||
if prompt is not None:
|
||||
batch_size = len(prompt)
|
||||
else:
|
||||
batch_size = prompt_embeds.shape[0]
|
||||
|
||||
batch_size = batch_size * num_images_per_prompt
|
||||
|
||||
if prompt_embeds is None:
|
||||
input_ids = self.tokenizer(
|
||||
prompt,
|
||||
return_tensors="pt",
|
||||
padding="max_length",
|
||||
truncation=True,
|
||||
max_length=77, #self.tokenizer.model_max_length,
|
||||
).input_ids.to(self._execution_device)
|
||||
|
||||
outputs = self.text_encoder(input_ids, return_dict=True, output_hidden_states=True)
|
||||
prompt_embeds = outputs.text_embeds
|
||||
encoder_hidden_states = outputs.hidden_states[-2]
|
||||
|
||||
prompt_embeds = prompt_embeds.repeat(num_images_per_prompt, 1)
|
||||
encoder_hidden_states = encoder_hidden_states.repeat(num_images_per_prompt, 1, 1)
|
||||
|
||||
if guidance_scale > 1.0:
|
||||
if negative_prompt_embeds is None:
|
||||
if negative_prompt is None:
|
||||
negative_prompt = [""] * len(prompt)
|
||||
|
||||
if isinstance(negative_prompt, str):
|
||||
negative_prompt = [negative_prompt]
|
||||
|
||||
input_ids = self.tokenizer(
|
||||
negative_prompt,
|
||||
return_tensors="pt",
|
||||
padding="max_length",
|
||||
truncation=True,
|
||||
max_length=77, #self.tokenizer.model_max_length,
|
||||
).input_ids.to(self._execution_device)
|
||||
|
||||
outputs = self.text_encoder(input_ids, return_dict=True, output_hidden_states=True)
|
||||
negative_prompt_embeds = outputs.text_embeds
|
||||
negative_encoder_hidden_states = outputs.hidden_states[-2]
|
||||
|
||||
negative_prompt_embeds = negative_prompt_embeds.repeat(num_images_per_prompt, 1)
|
||||
negative_encoder_hidden_states = negative_encoder_hidden_states.repeat(num_images_per_prompt, 1, 1)
|
||||
|
||||
prompt_embeds = torch.concat([negative_prompt_embeds, prompt_embeds])
|
||||
encoder_hidden_states = torch.concat([negative_encoder_hidden_states, encoder_hidden_states])
|
||||
|
||||
image = self.image_processor.preprocess(image)
|
||||
|
||||
height, width = image.shape[-2:]
|
||||
|
||||
# Note that the micro conditionings _do_ flip the order of width, height for the original size
|
||||
# and the crop coordinates. This is how it was done in the original code base
|
||||
micro_conds = torch.tensor(
|
||||
[
|
||||
width,
|
||||
height,
|
||||
micro_conditioning_crop_coord[0],
|
||||
micro_conditioning_crop_coord[1],
|
||||
micro_conditioning_aesthetic_score,
|
||||
],
|
||||
device=self._execution_device,
|
||||
dtype=encoder_hidden_states.dtype,
|
||||
)
|
||||
|
||||
micro_conds = micro_conds.unsqueeze(0)
|
||||
micro_conds = micro_conds.expand(2 * batch_size if guidance_scale > 1.0 else batch_size, -1)
|
||||
|
||||
self.scheduler.set_timesteps(num_inference_steps, temperature, self._execution_device)
|
||||
num_inference_steps = int(len(self.scheduler.timesteps) * strength)
|
||||
start_timestep_idx = len(self.scheduler.timesteps) - num_inference_steps
|
||||
|
||||
needs_upcasting = False # = self.vqvae.dtype == torch.float16 and self.vqvae.config.force_upcast
|
||||
|
||||
if needs_upcasting:
|
||||
self.vqvae.float()
|
||||
|
||||
latents = self.vqvae.encode(image.to(dtype=self.vqvae.dtype, device=self._execution_device)).latents
|
||||
latents_bsz, channels, latents_height, latents_width = latents.shape
|
||||
latents = self.vqvae.quantize(latents)[2][2].reshape(latents_bsz, latents_height, latents_width)
|
||||
latents = self.scheduler.add_noise(
|
||||
latents, self.scheduler.timesteps[start_timestep_idx - 1], generator=generator
|
||||
)
|
||||
latents = latents.repeat(num_images_per_prompt, 1, 1)
|
||||
|
||||
with self.progress_bar(total=num_inference_steps) as progress_bar:
|
||||
for i in range(start_timestep_idx, len(self.scheduler.timesteps)):
|
||||
timestep = self.scheduler.timesteps[i]
|
||||
|
||||
if guidance_scale > 1.0:
|
||||
model_input = torch.cat([latents] * 2)
|
||||
else:
|
||||
model_input = latents
|
||||
if height == 1024: #args.resolution == 1024:
|
||||
img_ids = _prepare_latent_image_ids(model_input.shape[0], model_input.shape[-2],model_input.shape[-1],model_input.device,model_input.dtype)
|
||||
else:
|
||||
img_ids = _prepare_latent_image_ids(model_input.shape[0],2*model_input.shape[-2],2*model_input.shape[-1],model_input.device,model_input.dtype)
|
||||
txt_ids = torch.zeros(encoder_hidden_states.shape[1],3).to(device = encoder_hidden_states.device, dtype = encoder_hidden_states.dtype)
|
||||
model_output = self.transformer(
|
||||
model_input,
|
||||
micro_conds=micro_conds,
|
||||
pooled_projections=prompt_embeds,
|
||||
encoder_hidden_states=encoder_hidden_states,
|
||||
# cross_attention_kwargs=cross_attention_kwargs,
|
||||
img_ids = img_ids,
|
||||
txt_ids = txt_ids,
|
||||
timestep = torch.tensor([timestep], device=model_input.device, dtype=torch.long),
|
||||
)
|
||||
|
||||
if guidance_scale > 1.0:
|
||||
uncond_logits, cond_logits = model_output.chunk(2)
|
||||
model_output = uncond_logits + guidance_scale * (cond_logits - uncond_logits)
|
||||
|
||||
latents = self.scheduler.step(
|
||||
model_output=model_output,
|
||||
timestep=timestep,
|
||||
sample=latents,
|
||||
generator=generator,
|
||||
).prev_sample
|
||||
|
||||
if i == len(self.scheduler.timesteps) - 1 or ((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, timestep, latents)
|
||||
|
||||
if output_type == "latent":
|
||||
output = latents
|
||||
else:
|
||||
output = self.vqvae.decode(
|
||||
latents,
|
||||
force_not_quantize=True,
|
||||
shape=(
|
||||
batch_size,
|
||||
height // self.vae_scale_factor,
|
||||
width // self.vae_scale_factor,
|
||||
self.vqvae.config.latent_channels,
|
||||
),
|
||||
).sample.clip(0, 1)
|
||||
output = self.image_processor.postprocess(output, output_type)
|
||||
|
||||
if needs_upcasting:
|
||||
self.vqvae.half()
|
||||
|
||||
self.maybe_free_model_hooks()
|
||||
|
||||
if not return_dict:
|
||||
return (output,)
|
||||
|
||||
return ImagePipelineOutput(output)
|
||||
@@ -0,0 +1,374 @@
|
||||
# Copyright 2024 The HuggingFace Team and The MeissonFlow Team. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
||||
import torch
|
||||
from transformers import CLIPTextModelWithProjection, CLIPTokenizer
|
||||
from diffusers.image_processor import PipelineImageInput, VaeImageProcessor
|
||||
from diffusers.models import VQModel
|
||||
from diffusers.utils import replace_example_docstring
|
||||
from diffusers.pipelines.pipeline_utils import DiffusionPipeline, ImagePipelineOutput
|
||||
from .scheduler import Scheduler
|
||||
from .transformer import Transformer2DModel
|
||||
|
||||
EXAMPLE_DOC_STRING = """
|
||||
Examples:
|
||||
```py
|
||||
>>> pipe(prompt, input_image, mask).images[0].save("out.png")
|
||||
```
|
||||
"""
|
||||
|
||||
def _prepare_latent_image_ids(batch_size, height, width, device, dtype):
|
||||
latent_image_ids = torch.zeros(height // 2, width // 2, 3)
|
||||
latent_image_ids[..., 1] = latent_image_ids[..., 1] + torch.arange(height // 2)[:, None]
|
||||
latent_image_ids[..., 2] = latent_image_ids[..., 2] + torch.arange(width // 2)[None, :]
|
||||
|
||||
latent_image_id_height, latent_image_id_width, latent_image_id_channels = latent_image_ids.shape
|
||||
|
||||
latent_image_ids = latent_image_ids.reshape(
|
||||
latent_image_id_height * latent_image_id_width, latent_image_id_channels
|
||||
)
|
||||
# latent_image_ids = latent_image_ids.unsqueeze(0).repeat(batch_size, 1, 1)
|
||||
|
||||
return latent_image_ids.to(device=device, dtype=dtype)
|
||||
|
||||
|
||||
class InpaintPipeline(DiffusionPipeline):
|
||||
image_processor: VaeImageProcessor
|
||||
vqvae: VQModel
|
||||
tokenizer: CLIPTokenizer
|
||||
text_encoder: CLIPTextModelWithProjection
|
||||
transformer: Transformer2DModel #UVit2DModel
|
||||
scheduler: Scheduler
|
||||
|
||||
model_cpu_offload_seq = "text_encoder->transformer->vqvae"
|
||||
|
||||
# TODO - when calling self.vqvae.quantize, it uses self.vqvae.quantize.embedding.weight before
|
||||
# the forward method of self.vqvae.quantize, so the hook doesn't get called to move the parameter
|
||||
# off the meta device. There should be a way to fix this instead of just not offloading it
|
||||
_exclude_from_cpu_offload = ["vqvae"]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
vqvae: VQModel,
|
||||
tokenizer: CLIPTokenizer,
|
||||
text_encoder: CLIPTextModelWithProjection,
|
||||
transformer: Transformer2DModel, #UVit2DModel,
|
||||
scheduler: Scheduler,
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
self.register_modules(
|
||||
vqvae=vqvae,
|
||||
tokenizer=tokenizer,
|
||||
text_encoder=text_encoder,
|
||||
transformer=transformer,
|
||||
scheduler=scheduler,
|
||||
)
|
||||
self.vae_scale_factor = 2 ** (len(self.vqvae.config.block_out_channels) - 1)
|
||||
self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor, do_normalize=False)
|
||||
self.mask_processor = VaeImageProcessor(
|
||||
vae_scale_factor=self.vae_scale_factor,
|
||||
do_normalize=False,
|
||||
do_binarize=True,
|
||||
do_convert_grayscale=True,
|
||||
do_resize=True,
|
||||
)
|
||||
self.scheduler.register_to_config(masking_schedule="linear")
|
||||
|
||||
@torch.no_grad()
|
||||
@replace_example_docstring(EXAMPLE_DOC_STRING)
|
||||
def __call__(
|
||||
self,
|
||||
prompt: Optional[Union[List[str], str]] = None,
|
||||
image: PipelineImageInput = None,
|
||||
mask_image: PipelineImageInput = None,
|
||||
strength: float = 1.0,
|
||||
num_inference_steps: int = 12,
|
||||
guidance_scale: float = 10.0,
|
||||
negative_prompt: Optional[Union[str, List[str]]] = None,
|
||||
num_images_per_prompt: Optional[int] = 1,
|
||||
generator: Optional[torch.Generator] = None,
|
||||
prompt_embeds: Optional[torch.Tensor] = None,
|
||||
encoder_hidden_states: Optional[torch.Tensor] = None,
|
||||
negative_prompt_embeds: Optional[torch.Tensor] = None,
|
||||
negative_encoder_hidden_states: Optional[torch.Tensor] = None,
|
||||
output_type="pil",
|
||||
return_dict: bool = True,
|
||||
callback: Optional[Callable[[int, int, torch.Tensor], None]] = None,
|
||||
callback_steps: int = 1,
|
||||
cross_attention_kwargs: Optional[Dict[str, Any]] = None,
|
||||
micro_conditioning_aesthetic_score: int = 6,
|
||||
micro_conditioning_crop_coord: Tuple[int, int] = (0, 0),
|
||||
temperature: Union[int, Tuple[int, int], List[int]] = (2, 0),
|
||||
):
|
||||
"""
|
||||
The call function to the pipeline for generation.
|
||||
|
||||
Args:
|
||||
prompt (`str` or `List[str]`, *optional*):
|
||||
The prompt or prompts to guide image generation. If not defined, you need to pass `prompt_embeds`.
|
||||
image (`torch.Tensor`, `PIL.Image.Image`, `np.ndarray`, `List[torch.Tensor]`, `List[PIL.Image.Image]`, or `List[np.ndarray]`):
|
||||
`Image`, numpy array or tensor representing an image batch to be used as the starting point. For both
|
||||
numpy array and pytorch tensor, the expected value range is between `[0, 1]` If it's a tensor or a list
|
||||
or tensors, the expected shape should be `(B, C, H, W)` or `(C, H, W)`. If it is a numpy array or a
|
||||
list of arrays, the expected shape should be `(B, H, W, C)` or `(H, W, C)` It can also accept image
|
||||
latents as `image`, but if passing latents directly it is not encoded again.
|
||||
mask_image (`torch.Tensor`, `PIL.Image.Image`, `np.ndarray`, `List[torch.Tensor]`, `List[PIL.Image.Image]`, or `List[np.ndarray]`):
|
||||
`Image`, numpy array or tensor representing an image batch to mask `image`. White pixels in the mask
|
||||
are repainted while black pixels are preserved. If `mask_image` is a PIL image, it is converted to a
|
||||
single channel (luminance) before use. If it's a numpy array or pytorch tensor, it should contain one
|
||||
color channel (L) instead of 3, so the expected shape for pytorch tensor would be `(B, 1, H, W)`, `(B,
|
||||
H, W)`, `(1, H, W)`, `(H, W)`. And for numpy array would be for `(B, H, W, 1)`, `(B, H, W)`, `(H, W,
|
||||
1)`, or `(H, W)`.
|
||||
strength (`float`, *optional*, defaults to 1.0):
|
||||
Indicates extent to transform the reference `image`. Must be between 0 and 1. `image` is used as a
|
||||
starting point and more noise is added the higher the `strength`. The number of denoising steps depends
|
||||
on the amount of noise initially added. When `strength` is 1, added noise is maximum and the denoising
|
||||
process runs for the full number of iterations specified in `num_inference_steps`. A value of 1
|
||||
essentially ignores `image`.
|
||||
num_inference_steps (`int`, *optional*, defaults to 16):
|
||||
The number of denoising steps. More denoising steps usually lead to a higher quality image at the
|
||||
expense of slower inference.
|
||||
guidance_scale (`float`, *optional*, defaults to 10.0):
|
||||
A higher guidance scale value encourages the model to generate images closely linked to the text
|
||||
`prompt` at the expense of lower image quality. Guidance scale is enabled when `guidance_scale > 1`.
|
||||
negative_prompt (`str` or `List[str]`, *optional*):
|
||||
The prompt or prompts to guide what to not include in image generation. If not defined, you need to
|
||||
pass `negative_prompt_embeds` instead. Ignored when not using guidance (`guidance_scale < 1`).
|
||||
num_images_per_prompt (`int`, *optional*, defaults to 1):
|
||||
The number of images to generate per prompt.
|
||||
generator (`torch.Generator`, *optional*):
|
||||
A [`torch.Generator`](https://pytorch.org/docs/stable/generated/torch.Generator.html) to make
|
||||
generation deterministic.
|
||||
prompt_embeds (`torch.Tensor`, *optional*):
|
||||
Pre-generated text embeddings. Can be used to easily tweak text inputs (prompt weighting). If not
|
||||
provided, text embeddings are generated from the `prompt` input argument. A single vector from the
|
||||
pooled and projected final hidden states.
|
||||
encoder_hidden_states (`torch.Tensor`, *optional*):
|
||||
Pre-generated penultimate hidden states from the text encoder providing additional text conditioning.
|
||||
negative_prompt_embeds (`torch.Tensor`, *optional*):
|
||||
Pre-generated negative text embeddings. Can be used to easily tweak text inputs (prompt weighting). If
|
||||
not provided, `negative_prompt_embeds` are generated from the `negative_prompt` input argument.
|
||||
negative_encoder_hidden_states (`torch.Tensor`, *optional*):
|
||||
Analogous to `encoder_hidden_states` for the positive prompt.
|
||||
output_type (`str`, *optional*, defaults to `"pil"`):
|
||||
The output format of the generated image. Choose between `PIL.Image` or `np.array`.
|
||||
return_dict (`bool`, *optional*, defaults to `True`):
|
||||
Whether or not to return a [`~pipelines.stable_diffusion.StableDiffusionPipelineOutput`] instead of a
|
||||
plain tuple.
|
||||
callback (`Callable`, *optional*):
|
||||
A function that calls every `callback_steps` steps during inference. The function is called with the
|
||||
following arguments: `callback(step: int, timestep: int, latents: torch.Tensor)`.
|
||||
callback_steps (`int`, *optional*, defaults to 1):
|
||||
The frequency at which the `callback` function is called. If not specified, the callback is called at
|
||||
every step.
|
||||
cross_attention_kwargs (`dict`, *optional*):
|
||||
A kwargs dictionary that if specified is passed along to the [`AttentionProcessor`] as defined in
|
||||
[`self.processor`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
|
||||
micro_conditioning_aesthetic_score (`int`, *optional*, defaults to 6):
|
||||
The targeted aesthetic score according to the laion aesthetic classifier. See
|
||||
https://laion.ai/blog/laion-aesthetics/ and the micro-conditioning section of
|
||||
https://arxiv.org/abs/2307.01952.
|
||||
micro_conditioning_crop_coord (`Tuple[int]`, *optional*, defaults to (0, 0)):
|
||||
The targeted height, width crop coordinates. See the micro-conditioning section of
|
||||
https://arxiv.org/abs/2307.01952.
|
||||
temperature (`Union[int, Tuple[int, int], List[int]]`, *optional*, defaults to (2, 0)):
|
||||
Configures the temperature scheduler on `self.scheduler` see `AmusedScheduler#set_timesteps`.
|
||||
|
||||
Examples:
|
||||
|
||||
Returns:
|
||||
[`~pipelines.pipeline_utils.ImagePipelineOutput`] or `tuple`:
|
||||
If `return_dict` is `True`, [`~pipelines.pipeline_utils.ImagePipelineOutput`] is returned, otherwise a
|
||||
`tuple` is returned where the first element is a list with the generated images.
|
||||
"""
|
||||
|
||||
if (prompt_embeds is not None and encoder_hidden_states is None) or (
|
||||
prompt_embeds is None and encoder_hidden_states is not None
|
||||
):
|
||||
raise ValueError("pass either both `prompt_embeds` and `encoder_hidden_states` or neither")
|
||||
|
||||
if (negative_prompt_embeds is not None and negative_encoder_hidden_states is None) or (
|
||||
negative_prompt_embeds is None and negative_encoder_hidden_states is not None
|
||||
):
|
||||
raise ValueError(
|
||||
"pass either both `negatve_prompt_embeds` and `negative_encoder_hidden_states` or neither"
|
||||
)
|
||||
|
||||
if (prompt is None and prompt_embeds is None) or (prompt is not None and prompt_embeds is not None):
|
||||
raise ValueError("pass only one of `prompt` or `prompt_embeds`")
|
||||
|
||||
if isinstance(prompt, str):
|
||||
prompt = [prompt]
|
||||
|
||||
if prompt is not None:
|
||||
batch_size = len(prompt)
|
||||
else:
|
||||
batch_size = prompt_embeds.shape[0]
|
||||
|
||||
batch_size = batch_size * num_images_per_prompt
|
||||
|
||||
if prompt_embeds is None:
|
||||
input_ids = self.tokenizer(
|
||||
prompt,
|
||||
return_tensors="pt",
|
||||
padding="max_length",
|
||||
truncation=True,
|
||||
max_length=77, #self.tokenizer.model_max_length,
|
||||
).input_ids.to(self._execution_device)
|
||||
|
||||
outputs = self.text_encoder(input_ids, return_dict=True, output_hidden_states=True)
|
||||
prompt_embeds = outputs.text_embeds
|
||||
encoder_hidden_states = outputs.hidden_states[-2]
|
||||
|
||||
prompt_embeds = prompt_embeds.repeat(num_images_per_prompt, 1)
|
||||
encoder_hidden_states = encoder_hidden_states.repeat(num_images_per_prompt, 1, 1)
|
||||
|
||||
if guidance_scale > 1.0:
|
||||
if negative_prompt_embeds is None:
|
||||
if negative_prompt is None:
|
||||
negative_prompt = [""] * len(prompt)
|
||||
|
||||
if isinstance(negative_prompt, str):
|
||||
negative_prompt = [negative_prompt]
|
||||
|
||||
input_ids = self.tokenizer(
|
||||
negative_prompt,
|
||||
return_tensors="pt",
|
||||
padding="max_length",
|
||||
truncation=True,
|
||||
max_length=77, #self.tokenizer.model_max_length,
|
||||
).input_ids.to(self._execution_device)
|
||||
|
||||
outputs = self.text_encoder(input_ids, return_dict=True, output_hidden_states=True)
|
||||
negative_prompt_embeds = outputs.text_embeds
|
||||
negative_encoder_hidden_states = outputs.hidden_states[-2]
|
||||
|
||||
negative_prompt_embeds = negative_prompt_embeds.repeat(num_images_per_prompt, 1)
|
||||
negative_encoder_hidden_states = negative_encoder_hidden_states.repeat(num_images_per_prompt, 1, 1)
|
||||
|
||||
prompt_embeds = torch.concat([negative_prompt_embeds, prompt_embeds])
|
||||
encoder_hidden_states = torch.concat([negative_encoder_hidden_states, encoder_hidden_states])
|
||||
|
||||
image = self.image_processor.preprocess(image)
|
||||
|
||||
height, width = image.shape[-2:]
|
||||
|
||||
# Note that the micro conditionings _do_ flip the order of width, height for the original size
|
||||
# and the crop coordinates. This is how it was done in the original code base
|
||||
micro_conds = torch.tensor(
|
||||
[
|
||||
width,
|
||||
height,
|
||||
micro_conditioning_crop_coord[0],
|
||||
micro_conditioning_crop_coord[1],
|
||||
micro_conditioning_aesthetic_score,
|
||||
],
|
||||
device=self._execution_device,
|
||||
dtype=encoder_hidden_states.dtype,
|
||||
)
|
||||
|
||||
micro_conds = micro_conds.unsqueeze(0)
|
||||
micro_conds = micro_conds.expand(2 * batch_size if guidance_scale > 1.0 else batch_size, -1)
|
||||
|
||||
self.scheduler.set_timesteps(num_inference_steps, temperature, self._execution_device)
|
||||
num_inference_steps = int(len(self.scheduler.timesteps) * strength)
|
||||
start_timestep_idx = len(self.scheduler.timesteps) - num_inference_steps
|
||||
|
||||
needs_upcasting = False #self.vqvae.dtype == torch.float16 and self.vqvae.config.force_upcast
|
||||
|
||||
if needs_upcasting:
|
||||
self.vqvae.float()
|
||||
|
||||
latents = self.vqvae.encode(image.to(dtype=self.vqvae.dtype, device=self._execution_device)).latents
|
||||
latents_bsz, channels, latents_height, latents_width = latents.shape
|
||||
latents = self.vqvae.quantize(latents)[2][2].reshape(latents_bsz, latents_height, latents_width)
|
||||
|
||||
mask = self.mask_processor.preprocess(
|
||||
mask_image, height // self.vae_scale_factor, width // self.vae_scale_factor
|
||||
)
|
||||
mask = mask.reshape(mask.shape[0], latents_height, latents_width).bool().to(latents.device)
|
||||
latents[mask] = self.scheduler.config.mask_token_id
|
||||
|
||||
starting_mask_ratio = mask.sum() / latents.numel()
|
||||
|
||||
latents = latents.repeat(num_images_per_prompt, 1, 1)
|
||||
|
||||
with self.progress_bar(total=num_inference_steps) as progress_bar:
|
||||
for i in range(start_timestep_idx, len(self.scheduler.timesteps)):
|
||||
timestep = self.scheduler.timesteps[i]
|
||||
|
||||
if guidance_scale > 1.0:
|
||||
model_input = torch.cat([latents] * 2)
|
||||
else:
|
||||
model_input = latents
|
||||
|
||||
if height == 1024: #args.resolution == 1024:
|
||||
img_ids = _prepare_latent_image_ids(model_input.shape[0], model_input.shape[-2],model_input.shape[-1],model_input.device,model_input.dtype)
|
||||
else:
|
||||
img_ids = _prepare_latent_image_ids(model_input.shape[0],2*model_input.shape[-2],2*model_input.shape[-1],model_input.device,model_input.dtype)
|
||||
txt_ids = torch.zeros(encoder_hidden_states.shape[1],3).to(device = encoder_hidden_states.device, dtype = encoder_hidden_states.dtype)
|
||||
model_output = self.transformer(
|
||||
model_input,
|
||||
micro_conds=micro_conds,
|
||||
pooled_projections=prompt_embeds,
|
||||
encoder_hidden_states=encoder_hidden_states,
|
||||
# cross_attention_kwargs=cross_attention_kwargs,
|
||||
img_ids = img_ids,
|
||||
txt_ids = txt_ids,
|
||||
timestep = torch.tensor([timestep], device=model_input.device, dtype=torch.long),
|
||||
)
|
||||
|
||||
if guidance_scale > 1.0:
|
||||
uncond_logits, cond_logits = model_output.chunk(2)
|
||||
model_output = uncond_logits + guidance_scale * (cond_logits - uncond_logits)
|
||||
|
||||
latents = self.scheduler.step(
|
||||
model_output=model_output,
|
||||
timestep=timestep,
|
||||
sample=latents,
|
||||
generator=generator,
|
||||
starting_mask_ratio=starting_mask_ratio,
|
||||
).prev_sample
|
||||
|
||||
if i == len(self.scheduler.timesteps) - 1 or ((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, timestep, latents)
|
||||
|
||||
if output_type == "latent":
|
||||
output = latents
|
||||
else:
|
||||
output = self.vqvae.decode(
|
||||
latents,
|
||||
force_not_quantize=True,
|
||||
shape=(
|
||||
batch_size,
|
||||
height // self.vae_scale_factor,
|
||||
width // self.vae_scale_factor,
|
||||
self.vqvae.config.latent_channels,
|
||||
),
|
||||
).sample.clip(0, 1)
|
||||
output = self.image_processor.postprocess(output, output_type)
|
||||
|
||||
if needs_upcasting:
|
||||
self.vqvae.half()
|
||||
|
||||
self.maybe_free_model_hooks()
|
||||
|
||||
if not return_dict:
|
||||
return (output,)
|
||||
|
||||
return ImagePipelineOutput(output)
|
||||
@@ -0,0 +1,175 @@
|
||||
# Copyright 2024 The HuggingFace Team and The MeissonFlow Team. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Optional, Tuple, Union
|
||||
|
||||
import torch
|
||||
|
||||
from diffusers.configuration_utils import ConfigMixin, register_to_config
|
||||
from diffusers.utils import BaseOutput
|
||||
from diffusers.schedulers.scheduling_utils import SchedulerMixin
|
||||
|
||||
|
||||
def gumbel_noise(t, generator=None):
|
||||
device = generator.device if generator is not None else t.device
|
||||
noise = torch.zeros_like(t, device=device).uniform_(0, 1, generator=generator).to(t.device)
|
||||
return -torch.log((-torch.log(noise.clamp(1e-20))).clamp(1e-20))
|
||||
|
||||
|
||||
def mask_by_random_topk(mask_len, probs, temperature=1.0, generator=None):
|
||||
confidence = torch.log(probs.clamp(1e-20)) + temperature * gumbel_noise(probs, generator=generator)
|
||||
sorted_confidence = torch.sort(confidence, dim=-1).values
|
||||
cut_off = torch.gather(sorted_confidence, 1, mask_len.long())
|
||||
masking = confidence < cut_off
|
||||
return masking
|
||||
|
||||
|
||||
@dataclass
|
||||
class SchedulerOutput(BaseOutput):
|
||||
"""
|
||||
Output class for the scheduler's `step` function output.
|
||||
|
||||
Args:
|
||||
prev_sample (`torch.Tensor` of shape `(batch_size, num_channels, height, width)` for images):
|
||||
Computed sample `(x_{t-1})` of previous timestep. `prev_sample` should be used as next model input in the
|
||||
denoising loop.
|
||||
pred_original_sample (`torch.Tensor` of shape `(batch_size, num_channels, height, width)` for images):
|
||||
The predicted denoised sample `(x_{0})` based on the model output from the current timestep.
|
||||
`pred_original_sample` can be used to preview progress or for guidance.
|
||||
"""
|
||||
|
||||
prev_sample: torch.Tensor
|
||||
pred_original_sample: torch.Tensor = None
|
||||
|
||||
|
||||
class Scheduler(SchedulerMixin, ConfigMixin):
|
||||
order = 1
|
||||
|
||||
temperatures: torch.Tensor
|
||||
|
||||
@register_to_config
|
||||
def __init__(
|
||||
self,
|
||||
mask_token_id: int,
|
||||
masking_schedule: str = "cosine",
|
||||
):
|
||||
self.temperatures = None
|
||||
self.timesteps = None
|
||||
|
||||
def set_timesteps(
|
||||
self,
|
||||
num_inference_steps: int,
|
||||
temperature: Union[int, Tuple[int, int], List[int]] = (2, 0),
|
||||
device: Union[str, torch.device] = None,
|
||||
):
|
||||
self.timesteps = torch.arange(num_inference_steps, device=device).flip(0)
|
||||
|
||||
if isinstance(temperature, (tuple, list)):
|
||||
self.temperatures = torch.linspace(temperature[0], temperature[1], num_inference_steps, device=device)
|
||||
else:
|
||||
self.temperatures = torch.linspace(temperature, 0.01, num_inference_steps, device=device)
|
||||
|
||||
def step(
|
||||
self,
|
||||
model_output: torch.Tensor,
|
||||
timestep: torch.long,
|
||||
sample: torch.LongTensor,
|
||||
starting_mask_ratio: int = 1,
|
||||
generator: Optional[torch.Generator] = None,
|
||||
return_dict: bool = True,
|
||||
) -> Union[SchedulerOutput, Tuple]:
|
||||
two_dim_input = sample.ndim == 3 and model_output.ndim == 4
|
||||
|
||||
if two_dim_input:
|
||||
batch_size, codebook_size, height, width = model_output.shape
|
||||
sample = sample.reshape(batch_size, height * width)
|
||||
model_output = model_output.reshape(batch_size, codebook_size, height * width).permute(0, 2, 1)
|
||||
|
||||
unknown_map = sample == self.config.mask_token_id
|
||||
|
||||
probs = model_output.softmax(dim=-1)
|
||||
|
||||
device = probs.device
|
||||
probs_ = probs.to(generator.device) if generator is not None else probs # handles when generator is on CPU
|
||||
if probs_.device.type == "cpu" and probs_.dtype != torch.float32:
|
||||
probs_ = probs_.float() # multinomial is not implemented for cpu half precision
|
||||
probs_ = probs_.reshape(-1, probs.size(-1))
|
||||
pred_original_sample = torch.multinomial(probs_, 1, generator=generator).to(device=device)
|
||||
pred_original_sample = pred_original_sample[:, 0].view(*probs.shape[:-1])
|
||||
pred_original_sample = torch.where(unknown_map, pred_original_sample, sample)
|
||||
|
||||
if timestep == 0:
|
||||
prev_sample = pred_original_sample
|
||||
else:
|
||||
seq_len = sample.shape[1]
|
||||
step_idx = (self.timesteps == timestep).nonzero()
|
||||
ratio = (step_idx + 1) / len(self.timesteps)
|
||||
|
||||
if self.config.masking_schedule == "cosine":
|
||||
mask_ratio = torch.cos(ratio * math.pi / 2)
|
||||
elif self.config.masking_schedule == "linear":
|
||||
mask_ratio = 1 - ratio
|
||||
else:
|
||||
raise ValueError(f"unknown masking schedule {self.config.masking_schedule}")
|
||||
|
||||
mask_ratio = starting_mask_ratio * mask_ratio
|
||||
|
||||
mask_len = (seq_len * mask_ratio).floor()
|
||||
# do not mask more than amount previously masked
|
||||
mask_len = torch.min(unknown_map.sum(dim=-1, keepdim=True) - 1, mask_len)
|
||||
# mask at least one
|
||||
mask_len = torch.max(torch.tensor([1], device=model_output.device), mask_len)
|
||||
|
||||
selected_probs = torch.gather(probs, -1, pred_original_sample[:, :, None])[:, :, 0]
|
||||
# Ignores the tokens given in the input by overwriting their confidence.
|
||||
selected_probs = torch.where(unknown_map, selected_probs, torch.finfo(selected_probs.dtype).max)
|
||||
|
||||
masking = mask_by_random_topk(mask_len, selected_probs, self.temperatures[step_idx], generator)
|
||||
|
||||
# Masks tokens with lower confidence.
|
||||
prev_sample = torch.where(masking, self.config.mask_token_id, pred_original_sample)
|
||||
|
||||
if two_dim_input:
|
||||
prev_sample = prev_sample.reshape(batch_size, height, width)
|
||||
pred_original_sample = pred_original_sample.reshape(batch_size, height, width)
|
||||
|
||||
if not return_dict:
|
||||
return (prev_sample, pred_original_sample)
|
||||
|
||||
return SchedulerOutput(prev_sample, pred_original_sample)
|
||||
|
||||
def add_noise(self, sample, timesteps, generator=None):
|
||||
step_idx = (self.timesteps == timesteps).nonzero()
|
||||
ratio = (step_idx + 1) / len(self.timesteps)
|
||||
|
||||
if self.config.masking_schedule == "cosine":
|
||||
mask_ratio = torch.cos(ratio * math.pi / 2)
|
||||
elif self.config.masking_schedule == "linear":
|
||||
mask_ratio = 1 - ratio
|
||||
else:
|
||||
raise ValueError(f"unknown masking schedule {self.config.masking_schedule}")
|
||||
|
||||
mask_indices = (
|
||||
torch.rand(
|
||||
sample.shape, device=generator.device if generator is not None else sample.device, generator=generator
|
||||
).to(sample.device)
|
||||
< mask_ratio
|
||||
)
|
||||
|
||||
masked_sample = sample.clone()
|
||||
|
||||
masked_sample[mask_indices] = self.config.mask_token_id
|
||||
|
||||
return masked_sample
|
||||
@@ -0,0 +1,33 @@
|
||||
import sys
|
||||
sys.path.append("./")
|
||||
|
||||
# import torch
|
||||
# from torchvision import transforms
|
||||
from meissonic.transformer import Transformer2DModel as TransformerMeissonic
|
||||
from meissonic.pipeline import Pipeline as PipelineMeissonic
|
||||
from meissonic.scheduler import Scheduler as MeissonicScheduler
|
||||
from transformers import CLIPTextModelWithProjection, CLIPTokenizer
|
||||
from diffusers import VQModel
|
||||
|
||||
device = 'cuda'
|
||||
model_path = 'MeissonFlow/Meissonic'
|
||||
cache_dir = '/mnt/models/Diffusers'
|
||||
|
||||
# diffusers_load_config['variant'] = fp16
|
||||
|
||||
model = TransformerMeissonic.from_pretrained(model_path, subfolder="transformer", cache_dir=cache_dir)
|
||||
vq_model = VQModel.from_pretrained(model_path, subfolder="vqvae", cache_dir=cache_dir)
|
||||
# text_encoder = CLIPTextModelWithProjection.from_pretrained(model_path,subfolder="text_encoder",)
|
||||
text_encoder = CLIPTextModelWithProjection.from_pretrained("laion/CLIP-ViT-H-14-laion2B-s32B-b79K", cache_dir=cache_dir)
|
||||
tokenizer = CLIPTokenizer.from_pretrained(model_path, subfolder="tokenizer")
|
||||
scheduler = MeissonicScheduler.from_pretrained(model_path, subfolder="scheduler")
|
||||
pipe = PipelineMeissonic(vq_model, tokenizer=tokenizer, text_encoder=text_encoder, transformer=model, scheduler=scheduler)
|
||||
pipe = pipe.to(device)
|
||||
|
||||
steps = 64
|
||||
guidance_scale = 9
|
||||
resolution = 1024
|
||||
negative = "worst quality, low quality, low res, blurry, distortion, watermark, logo, signature, text, jpeg artifacts, signature, sketch, duplicate, ugly, identifying mark"
|
||||
prompt = "Beautiful young woman posing on a lake with snow covered mountains in the background"
|
||||
image = pipe(prompt=prompt, negative_prompt=negative, height=resolution, width=resolution, guidance_scale=guidance_scale, num_inference_steps=steps).images[0]
|
||||
image.save('/tmp/meissonic.png')
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,37 @@
|
||||
import transformers
|
||||
import diffusers
|
||||
|
||||
|
||||
def load_meissonic(checkpoint_info, diffusers_load_config={}):
|
||||
from modules import shared, devices, modelloader, sd_models
|
||||
from modules.meissonic.transformer import Transformer2DModel as TransformerMeissonic
|
||||
from modules.meissonic.scheduler import Scheduler as MeissonicScheduler
|
||||
from modules.meissonic.pipeline import Pipeline as PipelineMeissonic
|
||||
from modules.meissonic.pipeline_img2img import Img2ImgPipeline as PipelineMeissonicImg2Img
|
||||
from modules.meissonic.pipeline_inpaint import InpaintPipeline as PipelineMeissonicInpaint
|
||||
|
||||
modelloader.hf_login()
|
||||
fn = sd_models.path_to_repo(checkpoint_info.path)
|
||||
cache_dir = shared.opts.diffusers_dir
|
||||
|
||||
diffusers_load_config['variant'] = 'fp16'
|
||||
diffusers_load_config['trust_remote_code'] = True
|
||||
model = TransformerMeissonic.from_pretrained(fn, subfolder="transformer", cache_dir=cache_dir, **diffusers_load_config)
|
||||
vqvae = diffusers.VQModel.from_pretrained(fn, subfolder="vqvae", cache_dir=cache_dir, **diffusers_load_config)
|
||||
text_encoder = transformers.CLIPTextModelWithProjection.from_pretrained(fn, subfolder="text_encoder", cache_dir=cache_dir)
|
||||
# text_encoder = transformers.CLIPTextModelWithProjection.from_pretrained("laion/CLIP-ViT-H-14-laion2B-s32B-b79K", cache_dir=cache_dir)
|
||||
tokenizer = transformers.CLIPTokenizer.from_pretrained(fn, subfolder="tokenizer", cache_dir=cache_dir)
|
||||
scheduler = MeissonicScheduler.from_pretrained(fn, subfolder="scheduler", cache_dir=cache_dir)
|
||||
pipe = PipelineMeissonic(
|
||||
vqvae=vqvae.to(devices.dtype),
|
||||
text_encoder=text_encoder.to(devices.dtype),
|
||||
transformer=model.to(devices.dtype),
|
||||
tokenizer=tokenizer,
|
||||
scheduler=scheduler,
|
||||
)
|
||||
|
||||
diffusers.pipelines.auto_pipeline.AUTO_TEXT2IMAGE_PIPELINES_MAPPING["meissonic"] = PipelineMeissonic
|
||||
diffusers.pipelines.auto_pipeline.AUTO_IMAGE2IMAGE_PIPELINES_MAPPING["meissonic"] = PipelineMeissonicImg2Img
|
||||
diffusers.pipelines.auto_pipeline.AUTO_INPAINT_PIPELINES_MAPPING["meissonic"] = PipelineMeissonicInpaint
|
||||
devices.torch_gc()
|
||||
return pipe
|
||||
+108
-139
@@ -563,6 +563,7 @@ def detect_pipeline(f: str, op: str = 'model', warning=True, quiet=False):
|
||||
if guess == 'Autodetect':
|
||||
try:
|
||||
guess = 'Stable Diffusion XL' if 'XL' in f.upper() else 'Stable Diffusion'
|
||||
pipeline = None
|
||||
# guess by size
|
||||
if os.path.isfile(f) and f.endswith('.safetensors'):
|
||||
size = round(os.path.getsize(f) / 1024 / 1024)
|
||||
@@ -624,6 +625,9 @@ def detect_pipeline(f: str, op: str = 'model', warning=True, quiet=False):
|
||||
guess = 'AuraFlow'
|
||||
if 'cogview' in f.lower():
|
||||
guess = 'CogView'
|
||||
if 'meissonic' in f.lower():
|
||||
guess = 'Meissonic'
|
||||
pipeline = 'custom'
|
||||
if 'flux' in f.lower():
|
||||
guess = 'FLUX'
|
||||
if size > 11000 and size < 20000:
|
||||
@@ -638,18 +642,20 @@ def detect_pipeline(f: str, op: str = 'model', warning=True, quiet=False):
|
||||
elif guess == 'Stable Diffusion XL' and 'instruct' in f.lower():
|
||||
guess = 'Stable Diffusion XL Instruct'
|
||||
# get actual pipeline
|
||||
pipeline = shared_items.get_pipelines().get(guess, None)
|
||||
pipeline = shared_items.get_pipelines().get(guess, None) if pipeline is None else pipeline
|
||||
if not quiet:
|
||||
shared.log.info(f'Autodetect {op}: detect="{guess}" class={pipeline.__name__} file="{f}" size={size}MB')
|
||||
shared.log.info(f'Autodetect {op}: detect="{guess}" class={getattr(pipeline, "__name__", None)} file="{f}" size={size}MB')
|
||||
except Exception as e:
|
||||
shared.log.error(f'Autodetect {op}: file="{f}" {e}')
|
||||
if debug_load:
|
||||
errors.display(e, f'Load {op}: {f}')
|
||||
return None, None
|
||||
else:
|
||||
try:
|
||||
size = round(os.path.getsize(f) / 1024 / 1024)
|
||||
pipeline = shared_items.get_pipelines().get(guess, None)
|
||||
pipeline = shared_items.get_pipelines().get(guess, None) if pipeline is None else pipeline
|
||||
if not quiet:
|
||||
shared.log.info(f'Load {op}: detect="{guess}" class={pipeline.__name__} file="{f}" size={size}MB')
|
||||
shared.log.info(f'Load {op}: detect="{guess}" class={getattr(pipeline, "__name__", None)} file="{f}" size={size}MB')
|
||||
except Exception as e:
|
||||
shared.log.error(f'Load {op}: detect="{guess}" file="{f}" {e}')
|
||||
|
||||
@@ -1099,149 +1105,108 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No
|
||||
files = shared.walk_files(checkpoint_info.path, ['.safetensors', '.bin', '.ckpt'])
|
||||
if 'variant' not in diffusers_load_config and any('diffusion_pytorch_model.fp16' in f for f in files): # deal with diffusers lack of variant fallback when loading
|
||||
diffusers_load_config['variant'] = 'fp16'
|
||||
if model_type in ['Stable Cascade']: # forced pipeline
|
||||
if sd_model is None:
|
||||
try:
|
||||
from modules.model_stablecascade import load_cascade_combined
|
||||
sd_model = load_cascade_combined(checkpoint_info, diffusers_load_config)
|
||||
if model_type in ['Stable Cascade']: # forced pipeline
|
||||
from modules.model_stablecascade import load_cascade_combined
|
||||
sd_model = load_cascade_combined(checkpoint_info, diffusers_load_config)
|
||||
elif model_type in ['InstaFlow']: # forced pipeline
|
||||
pipeline = diffusers.utils.get_class_from_dynamic_module('instaflow_one_step', module_file='pipeline.py')
|
||||
sd_model = pipeline.from_pretrained(checkpoint_info.path, cache_dir=shared.opts.diffusers_dir, **diffusers_load_config)
|
||||
elif model_type in ['SegMoE']: # forced pipeline
|
||||
from modules.segmoe.segmoe_model import SegMoEPipeline
|
||||
sd_model = SegMoEPipeline(checkpoint_info.path, cache_dir=shared.opts.diffusers_dir, **diffusers_load_config)
|
||||
sd_model = sd_model.pipe # segmoe pipe does its stuff in __init__ and __call__ is the original pipeline
|
||||
elif model_type in ['PixArt-Sigma']: # forced pipeline
|
||||
from modules.model_pixart import load_pixart
|
||||
sd_model = load_pixart(checkpoint_info, diffusers_load_config)
|
||||
elif model_type in ['Lumina-Next']: # forced pipeline
|
||||
from modules.model_lumina import load_lumina
|
||||
sd_model = load_lumina(checkpoint_info, diffusers_load_config)
|
||||
elif model_type in ['Kolors']: # forced pipeline
|
||||
from modules.model_kolors import load_kolors
|
||||
sd_model = load_kolors(checkpoint_info, diffusers_load_config)
|
||||
elif model_type in ['AuraFlow']: # forced pipeline
|
||||
from modules.model_auraflow import load_auraflow
|
||||
sd_model = load_auraflow(checkpoint_info, diffusers_load_config)
|
||||
elif model_type in ['FLUX']:
|
||||
from modules.model_flux import load_flux
|
||||
sd_model = load_flux(checkpoint_info, diffusers_load_config)
|
||||
elif model_type in ['Stable Diffusion 3']:
|
||||
from modules.model_sd3 import load_sd3
|
||||
shared.log.debug(f'Load {op}: model="Stable Diffusion 3" variant=medium')
|
||||
shared.opts.scheduler = 'Default'
|
||||
sd_model = load_sd3(cache_dir=shared.opts.diffusers_dir, config=diffusers_load_config.get('config', None))
|
||||
elif model_type in ['Meissonic']: # forced pipeline
|
||||
from modules.model_meissonic import load_meissonic
|
||||
sd_model = load_meissonic(checkpoint_info, diffusers_load_config)
|
||||
except Exception as e:
|
||||
shared.log.error(f'Load {op}: path="{checkpoint_info.path}" {e}')
|
||||
if debug_load:
|
||||
errors.display(e, 'Load')
|
||||
return
|
||||
elif model_type in ['InstaFlow']: # forced pipeline
|
||||
try:
|
||||
pipeline = diffusers.utils.get_class_from_dynamic_module('instaflow_one_step', module_file='pipeline.py')
|
||||
sd_model = pipeline.from_pretrained(checkpoint_info.path, cache_dir=shared.opts.diffusers_dir, **diffusers_load_config)
|
||||
except Exception as e:
|
||||
shared.log.error(f'Load {op}: path="{checkpoint_info.path}" {e}')
|
||||
if debug_load:
|
||||
errors.display(e, 'Load')
|
||||
return
|
||||
elif model_type in ['SegMoE']: # forced pipeline
|
||||
try:
|
||||
from modules.segmoe.segmoe_model import SegMoEPipeline
|
||||
sd_model = SegMoEPipeline(checkpoint_info.path, cache_dir=shared.opts.diffusers_dir, **diffusers_load_config)
|
||||
sd_model = sd_model.pipe # segmoe pipe does its stuff in __init__ and __call__ is the original pipeline
|
||||
except Exception as e:
|
||||
shared.log.error(f'Load {op}: path="{checkpoint_info.path}" {e}')
|
||||
if debug_load:
|
||||
errors.display(e, 'Load')
|
||||
return
|
||||
elif model_type in ['PixArt-Sigma']: # forced pipeline
|
||||
try:
|
||||
from modules.model_pixart import load_pixart
|
||||
sd_model = load_pixart(checkpoint_info, diffusers_load_config)
|
||||
except Exception as e:
|
||||
shared.log.error(f'Load {op}: path="{checkpoint_info.path}" {e}')
|
||||
if debug_load:
|
||||
errors.display(e, 'Load')
|
||||
return
|
||||
elif model_type in ['Lumina-Next']: # forced pipeline
|
||||
try:
|
||||
from modules.model_lumina import load_lumina
|
||||
sd_model = load_lumina(checkpoint_info, diffusers_load_config)
|
||||
except Exception as e:
|
||||
shared.log.error(f'Load {op}: path="{checkpoint_info.path}" {e}')
|
||||
if debug_load:
|
||||
errors.display(e, 'Load')
|
||||
return
|
||||
elif model_type in ['Kolors']: # forced pipeline
|
||||
try:
|
||||
from modules.model_kolors import load_kolors
|
||||
sd_model = load_kolors(checkpoint_info, diffusers_load_config)
|
||||
except Exception as e:
|
||||
shared.log.error(f'Load {op}: path="{checkpoint_info.path}" {e}')
|
||||
if debug_load:
|
||||
errors.display(e, 'Load')
|
||||
return
|
||||
elif model_type in ['AuraFlow']: # forced pipeline
|
||||
try:
|
||||
from modules.model_auraflow import load_auraflow
|
||||
sd_model = load_auraflow(checkpoint_info, diffusers_load_config)
|
||||
except Exception as e:
|
||||
shared.log.error(f'Load {op}: path="{checkpoint_info.path}" {e}')
|
||||
if debug_load:
|
||||
errors.display(e, 'Load')
|
||||
return
|
||||
elif model_type in ['FLUX']:
|
||||
try:
|
||||
from modules.model_flux import load_flux
|
||||
sd_model = load_flux(checkpoint_info, diffusers_load_config)
|
||||
except Exception as e:
|
||||
shared.log.error(f'Load {op}: path="{checkpoint_info.path}" {e}')
|
||||
if debug_load:
|
||||
errors.display(e, 'Load')
|
||||
return
|
||||
elif model_type in ['Stable Diffusion 3']:
|
||||
try:
|
||||
from modules.model_sd3 import load_sd3
|
||||
shared.log.debug(f'Load {op}: model="Stable Diffusion 3" variant=medium')
|
||||
shared.opts.scheduler = 'Default'
|
||||
sd_model = load_sd3(cache_dir=shared.opts.diffusers_dir, config=diffusers_load_config.get('config', None))
|
||||
except Exception as e:
|
||||
shared.log.error(f'Load {op}: path="{checkpoint_info.path}" {e}')
|
||||
if debug_load:
|
||||
errors.display(e, 'Load')
|
||||
return
|
||||
elif model_type is not None and pipeline is not None and 'ONNX' in model_type: # forced pipeline
|
||||
try:
|
||||
sd_model = pipeline.from_pretrained(checkpoint_info.path)
|
||||
except Exception as e:
|
||||
shared.log.error(f'Load {op}: type=ONNX path="{checkpoint_info.path}" {e}')
|
||||
if debug_load:
|
||||
errors.display(e, 'Load')
|
||||
return
|
||||
else:
|
||||
err1, err2, err3 = None, None, None
|
||||
if os.path.exists(checkpoint_info.path) and os.path.isdir(checkpoint_info.path):
|
||||
if os.path.exists(os.path.join(checkpoint_info.path, 'unet', 'diffusion_pytorch_model.bin')):
|
||||
shared.log.debug(f'Load {op}: type=pickle')
|
||||
diffusers_load_config['use_safetensors'] = False
|
||||
if debug_load:
|
||||
shared.log.debug(f'Load {op}: args={diffusers_load_config}')
|
||||
try: # 1 - autopipeline, best choice but not all pipelines are available
|
||||
|
||||
if sd_model is None:
|
||||
if model_type is not None and pipeline is not None and 'ONNX' in model_type: # forced pipeline
|
||||
try:
|
||||
sd_model = diffusers.AutoPipelineForText2Image.from_pretrained(checkpoint_info.path, cache_dir=shared.opts.diffusers_dir, **diffusers_load_config)
|
||||
sd_model.model_type = sd_model.__class__.__name__
|
||||
except ValueError as e:
|
||||
if 'no variant default' in str(e):
|
||||
shared.log.warning(f'Load {op}: variant={diffusers_load_config["variant"]} model="{checkpoint_info.path}" using default variant')
|
||||
diffusers_load_config.pop('variant', None)
|
||||
sd_model = diffusers.AutoPipelineForText2Image.from_pretrained(checkpoint_info.path, cache_dir=shared.opts.diffusers_dir, **diffusers_load_config)
|
||||
sd_model.model_type = sd_model.__class__.__name__
|
||||
elif 'safetensors found in directory' in str(err1):
|
||||
shared.log.warning(f'Load {op}: type=pickle')
|
||||
sd_model = pipeline.from_pretrained(checkpoint_info.path)
|
||||
except Exception as e:
|
||||
shared.log.error(f'Load {op}: type=ONNX path="{checkpoint_info.path}" {e}')
|
||||
if debug_load:
|
||||
errors.display(e, 'Load')
|
||||
return
|
||||
else:
|
||||
err1, err2, err3 = None, None, None
|
||||
if os.path.exists(checkpoint_info.path) and os.path.isdir(checkpoint_info.path):
|
||||
if os.path.exists(os.path.join(checkpoint_info.path, 'unet', 'diffusion_pytorch_model.bin')):
|
||||
shared.log.debug(f'Load {op}: type=pickle')
|
||||
diffusers_load_config['use_safetensors'] = False
|
||||
if debug_load:
|
||||
shared.log.debug(f'Load {op}: args={diffusers_load_config}')
|
||||
try: # 1 - autopipeline, best choice but not all pipelines are available
|
||||
try:
|
||||
sd_model = diffusers.AutoPipelineForText2Image.from_pretrained(checkpoint_info.path, cache_dir=shared.opts.diffusers_dir, **diffusers_load_config)
|
||||
sd_model.model_type = sd_model.__class__.__name__
|
||||
else:
|
||||
raise ValueError from e # reraise
|
||||
except Exception as e:
|
||||
err1 = e
|
||||
if debug_load:
|
||||
errors.display(e, 'Load AutoPipeline')
|
||||
# shared.log.error(f'AutoPipeline: {e}')
|
||||
try: # 2 - diffusion pipeline, works for most non-linked pipelines
|
||||
if err1 is not None:
|
||||
sd_model = diffusers.DiffusionPipeline.from_pretrained(checkpoint_info.path, cache_dir=shared.opts.diffusers_dir, **diffusers_load_config)
|
||||
sd_model.model_type = sd_model.__class__.__name__
|
||||
except Exception as e:
|
||||
err2 = e
|
||||
if debug_load:
|
||||
errors.display(e, "Load DiffusionPipeline")
|
||||
# shared.log.error(f'DiffusionPipeline: {e}')
|
||||
try: # 3 - try basic pipeline just in case
|
||||
if err2 is not None:
|
||||
sd_model = diffusers.StableDiffusionPipeline.from_pretrained(checkpoint_info.path, cache_dir=shared.opts.diffusers_dir, **diffusers_load_config)
|
||||
sd_model.model_type = sd_model.__class__.__name__
|
||||
except Exception as e:
|
||||
err3 = e # ignore last error
|
||||
shared.log.error(f"StableDiffusionPipeline: {e}")
|
||||
if debug_load:
|
||||
errors.display(e, "Load StableDiffusionPipeline")
|
||||
if err3 is not None:
|
||||
shared.log.error(f'Load {op}: {checkpoint_info.path} auto={err1} diffusion={err2}')
|
||||
return
|
||||
except ValueError as e:
|
||||
if 'no variant default' in str(e):
|
||||
shared.log.warning(f'Load {op}: variant={diffusers_load_config["variant"]} model="{checkpoint_info.path}" using default variant')
|
||||
diffusers_load_config.pop('variant', None)
|
||||
sd_model = diffusers.AutoPipelineForText2Image.from_pretrained(checkpoint_info.path, cache_dir=shared.opts.diffusers_dir, **diffusers_load_config)
|
||||
sd_model.model_type = sd_model.__class__.__name__
|
||||
elif 'safetensors found in directory' in str(err1):
|
||||
shared.log.warning(f'Load {op}: type=pickle')
|
||||
diffusers_load_config['use_safetensors'] = False
|
||||
sd_model = diffusers.AutoPipelineForText2Image.from_pretrained(checkpoint_info.path, cache_dir=shared.opts.diffusers_dir, **diffusers_load_config)
|
||||
sd_model.model_type = sd_model.__class__.__name__
|
||||
else:
|
||||
raise ValueError from e # reraise
|
||||
except Exception as e:
|
||||
err1 = e
|
||||
if debug_load:
|
||||
errors.display(e, 'Load AutoPipeline')
|
||||
# shared.log.error(f'AutoPipeline: {e}')
|
||||
try: # 2 - diffusion pipeline, works for most non-linked pipelines
|
||||
if err1 is not None:
|
||||
sd_model = diffusers.DiffusionPipeline.from_pretrained(checkpoint_info.path, cache_dir=shared.opts.diffusers_dir, **diffusers_load_config)
|
||||
sd_model.model_type = sd_model.__class__.__name__
|
||||
except Exception as e:
|
||||
err2 = e
|
||||
if debug_load:
|
||||
errors.display(e, "Load DiffusionPipeline")
|
||||
# shared.log.error(f'DiffusionPipeline: {e}')
|
||||
try: # 3 - try basic pipeline just in case
|
||||
if err2 is not None:
|
||||
sd_model = diffusers.StableDiffusionPipeline.from_pretrained(checkpoint_info.path, cache_dir=shared.opts.diffusers_dir, **diffusers_load_config)
|
||||
sd_model.model_type = sd_model.__class__.__name__
|
||||
except Exception as e:
|
||||
err3 = e # ignore last error
|
||||
shared.log.error(f"StableDiffusionPipeline: {e}")
|
||||
if debug_load:
|
||||
errors.display(e, "Load StableDiffusionPipeline")
|
||||
if err3 is not None:
|
||||
shared.log.error(f'Load {op}: {checkpoint_info.path} auto={err1} diffusion={err2}')
|
||||
return
|
||||
|
||||
elif os.path.isfile(checkpoint_info.path) and checkpoint_info.path.lower().endswith('.safetensors'):
|
||||
diffusers_load_config["local_files_only"] = diffusers_version < 28 # must be true for old diffusers, otherwise false but we override config for sd15/sdxl
|
||||
diffusers_load_config["extract_ema"] = shared.opts.diffusers_extract_ema
|
||||
@@ -1297,7 +1262,7 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No
|
||||
errors.display(e, f'loading {op}={checkpoint_info.path} pipeline={shared.opts.diffusers_pipeline}/{sd_model.__class__.__name__}')
|
||||
return
|
||||
else:
|
||||
shared.log.error(f'Load {op}: path="{checkpoint_info.path}" failed')
|
||||
shared.log.error(f'Load {op}: path="{checkpoint_info.path}" not found')
|
||||
return
|
||||
|
||||
if "StableDiffusion" in sd_model.__class__.__name__:
|
||||
@@ -1981,7 +1946,11 @@ def remove_token_merging(sd_model):
|
||||
|
||||
|
||||
def path_to_repo(fn: str = ''):
|
||||
repo_id = fn.replace('\\', '/').split('/')
|
||||
repo_id = fn.replace('\\', '/')
|
||||
if 'models--' in repo_id:
|
||||
repo_id = repo_id.split('models--')[-1]
|
||||
repo_id = repo_id.split('/')[0]
|
||||
repo_id = repo_id.split('/')
|
||||
repo_id = '/'.join(repo_id[-2:] if len(repo_id) > 1 else repo_id)
|
||||
repo_id = repo_id.replace('models--', '').replace('--', '/')
|
||||
return repo_id
|
||||
|
||||
Reference in New Issue
Block a user