Files
MrForExample-ComfyUI-Animat…/src/models/main_diffuser.py
T
MrForExample fa383bd37e Update After RCFG experiment
- Update readme to log the RCFG experiment
- Refactor code
- Add delta parameter on Animate_Anyone_Sampler node
- Add ComfyUI Sampler progress bar
2024-01-21 17:56:58 +01:00

462 lines
15 KiB
Python

# Adapted from https://github.com/MooreThreads/Moore-AnimateAnyone/blob/master/src/pipelines/pipeline_pose2vid_long.py
import inspect
import math
from typing import Callable, List, Optional, Union
import comfy.utils
import torch
from diffusers import DiffusionPipeline, StableDiffusionPipeline
from diffusers.loaders import LoraLoaderMixin
from diffusers.schedulers import (
DDIMScheduler,
LCMScheduler,
DPMSolverMultistepScheduler,
EulerAncestralDiscreteScheduler,
EulerDiscreteScheduler,
LMSDiscreteScheduler,
PNDMScheduler,
)
from diffusers.utils.torch_utils import randn_tensor
from ..utils.util import get_tensor_interpolation_method, get_context_scheduler
class AADiffusion(DiffusionPipeline, LoraLoaderMixin):
def __init__(
self,
reference_unet,
unet,
scheduler: Union[
DDIMScheduler,
LCMScheduler,
PNDMScheduler,
LMSDiscreteScheduler,
EulerDiscreteScheduler,
EulerAncestralDiscreteScheduler,
DPMSolverMultistepScheduler,
],
):
super().__init__()
self.register_modules(
reference_unet=reference_unet,
unet=unet,
scheduler=scheduler,
)
self.reference_unet = reference_unet
self.unet = unet
self.scheduler = scheduler
def load_lora(self, pretrained_model_name_or_path_or_dict, adapter_name=None, **kwargs):
# First, ensure that the checkpoint is a compatible one and can be successfully loaded.
state_dict, network_alphas = self.lora_state_dict(pretrained_model_name_or_path_or_dict, **kwargs)
is_correct_format = all("lora" in key for key in state_dict.keys())
if not is_correct_format:
raise ValueError("Invalid LoRA checkpoint.")
low_cpu_mem_usage = kwargs.pop("low_cpu_mem_usage", True)
self.load_lora_into_unet(
state_dict,
network_alphas=network_alphas,
unet=getattr(self, self.unet_name) if not hasattr(self, "unet") else self.unet,
low_cpu_mem_usage=low_cpu_mem_usage,
adapter_name=adapter_name,
_pipeline=self,
)
def prepare_latents(
self,
batch_size,
num_channels_latents,
video_length,
height,
width,
dtype,
device,
generator,
latents=None,
):
shape = (
batch_size,
num_channels_latents,
video_length,
height,
width,
)
if isinstance(generator, list) and len(generator) != batch_size:
raise ValueError(
f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
f" size of {batch_size}. Make sure the batch size matches the length of the generators."
)
if latents is None:
latents = randn_tensor(
shape, generator=generator, device=device, dtype=dtype
)
else:
latents = latents.to(device)
# scale the initial noise by the standard deviation required by the scheduler
latents = latents * self.scheduler.init_noise_sigma
return latents
def prepare_extra_step_kwargs(self, generator, eta):
# prepare extra kwargs for the scheduler step, since not all schedulers have the same signature
# eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers.
# eta corresponds to η in DDIM paper: https://arxiv.org/abs/2010.02502
# and should be between [0, 1]
accepts_eta = "eta" in set(
inspect.signature(self.scheduler.step).parameters.keys()
)
extra_step_kwargs = {}
if accepts_eta:
extra_step_kwargs["eta"] = eta
# check if the scheduler accepts generator
accepts_generator = "generator" in set(
inspect.signature(self.scheduler.step).parameters.keys()
)
if accepts_generator:
extra_step_kwargs["generator"] = generator
return extra_step_kwargs
def interpolate_latents(self, latents: torch.Tensor, interpolation_factor: int, device):
if interpolation_factor < 2:
return latents
new_latents = torch.zeros(
(
latents.shape[0],
latents.shape[1],
((latents.shape[2] - 1) * interpolation_factor) + 1,
latents.shape[3],
latents.shape[4],
),
device=latents.device,
dtype=latents.dtype,
)
org_video_length = latents.shape[2]
rate = [i / interpolation_factor for i in range(interpolation_factor)][1:]
new_index = 0
v0 = None
v1 = None
for i0, i1 in zip(range(org_video_length), range(org_video_length)[1:]):
v0 = latents[:, :, i0, :, :]
v1 = latents[:, :, i1, :, :]
new_latents[:, :, new_index, :, :] = v0
new_index += 1
for f in rate:
v = get_tensor_interpolation_method()(
v0.to(device=device), v1.to(device=device), f
)
new_latents[:, :, new_index, :, :] = v.to(latents.device)
new_index += 1
new_latents[:, :, new_index, :, :] = v1
new_index += 1
return new_latents
def perform_guidance(
self,
noise_pred,
counter,
cfg,
delta,
do_double_pass,
):
# perform CFG
if do_double_pass:
noise_pred_uncond, noise_pred_cond = (noise_pred / counter).chunk(2)
else:
noise_pred_cond = noise_pred_uncond = noise_pred / counter
cfg = 0.0
noise_pred = delta * noise_pred_uncond + cfg * (noise_pred_cond - delta * noise_pred_uncond)
return noise_pred
def denoise_all_one_step(
self,
t,
latents,
encoder_hidden_states,
pose_latent,
global_context,
do_double_pass,
device,
):
noise_pred = torch.zeros(
(
latents.shape[0] * (2 if do_double_pass else 1),
*latents.shape[1:],
),
device=device,
dtype=latents.dtype,
)
counter = torch.zeros(
(1, 1, latents.shape[2], 1, 1),
device=device,
dtype=latents.dtype,
)
for context in global_context:
# 3.1 expand the latents if we are doing classifier free guidance
latent_model_input = (
torch.cat([latents[:, :, c] for c in context]) # Concatenates the context in the same batch
.to(device)
.repeat(2 if do_double_pass else 1, 1, 1, 1, 1)
)
latent_model_input = self.scheduler.scale_model_input(
latent_model_input, t
)
b, c, f, h, w = latent_model_input.shape
latent_pose_input = torch.cat(
[pose_latent[:, :, c] for c in context]
).repeat(2 if do_double_pass else 1, 1, 1, 1, 1)
pred = self.unet(
latent_model_input, # torch.Size([2, 4, 24, 96, 64])
t,
encoder_hidden_states=encoder_hidden_states[:b],
pose_cond_fea=latent_pose_input, # torch.Size([2, 320, 24, 96, 64])
return_dict=False,
)[0]
for j, c in enumerate(context):
noise_pred[:, :, c] = noise_pred[:, :, c] + pred
counter[:, :, c] = counter[:, :, c] + 1
return noise_pred, counter
def solve_one_step(
self,
t,
latents,
encoder_hidden_states,
pose_latent,
global_context,
cfg,
delta,
do_double_pass,
device,
extra_step_kwargs,
):
noise_pred, counter = self.denoise_all_one_step(
t=t,
latents=latents,
encoder_hidden_states=encoder_hidden_states,
pose_latent=pose_latent,
global_context=global_context,
do_double_pass=do_double_pass,
device=device,
)
noise_pred = self.perform_guidance(
noise_pred=noise_pred,
counter=counter,
cfg=cfg,
delta=delta,
do_double_pass=do_double_pass,
)
scheduler_output = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs)
return scheduler_output.prev_sample
def denoise_loop(
self,
ref_image_latent,
loop_steps,
latents,
encoder_hidden_states,
pose_latent,
global_context,
cfg,
delta,
do_double_pass,
interpolation_factor,
device,
extra_step_kwargs,
):
# Prepare confitional embeds
if do_double_pass:
uncond_encoder_hidden_states = torch.zeros_like(encoder_hidden_states)
encoder_hidden_states=torch.cat(
[uncond_encoder_hidden_states, encoder_hidden_states], dim=0
)
# Prepare reference attention control
reference_control_writer = ReferenceAttentionControl(
self.reference_unet,
do_classifier_free_guidance=do_double_pass,
mode="write",
fusion_blocks="full",
)
reference_control_reader = ReferenceAttentionControl(
self.unet,
do_classifier_free_guidance=do_double_pass,
mode="read",
fusion_blocks="full",
)
self.reference_unet(
ref_image_latent.repeat(2 if do_double_pass else 1, 1, 1, 1),
torch.zeros(size=(1,), dtype=int, device=device),
encoder_hidden_states=encoder_hidden_states,
return_dict=False,
)
reference_control_reader.update(reference_control_writer)
# Prepare timesteps
self.scheduler.set_timesteps(loop_steps, device=device)
timesteps = self.scheduler.timesteps
comfy_pbar = comfy.utils.ProgressBar(loop_steps)
with self.progress_bar(total=loop_steps) as progress_bar:
for i, t in enumerate(timesteps):
latents = self.solve_one_step(
t=t,
latents=latents,
encoder_hidden_states=encoder_hidden_states,
pose_latent=pose_latent,
global_context=global_context,
cfg=cfg,
delta=delta,
do_double_pass=do_double_pass,
device=device,
extra_step_kwargs=extra_step_kwargs,
)
progress_bar.update()
comfy_pbar.update_absolute(i + 1)
if interpolation_factor > 0:
latents = self.interpolate_latents(latents, interpolation_factor, device)
reference_control_reader.clear()
reference_control_writer.clear()
return latents
@torch.no_grad()
def __call__(
self,
steps,
cfg,
delta,
ref_image_latent,
pose_latent,
encoder_hidden_states,
seed = 999999999,
eta: float = 0.0,
context_schedule="uniform",
context_frames=24,
context_stride=1,
context_overlap=4,
context_batch_size=1,
interpolation_factor=1,
callback: Optional[Callable[[int, int, torch.FloatTensor], None]] = None,
callback_steps: Optional[int] = 1,
):
do_double_pass = cfg > 0
device = pose_latent.device
generator=torch.manual_seed(seed)
latents = self.prepare_latents(
batch_size=pose_fea.shape[0],
num_channels_latents=self.unet.config.in_channels,
video_length=pose_fea.shape[2],
height=pose_fea.shape[3],
width=pose_fea.shape[4],
dtype=encoder_hidden_states.dtype,
device=device,
generator=generator,
)
# Prepare extra step kwargs.
extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
# Prepare timesteps
self.scheduler.set_timesteps(steps, device=device)
timesteps = self.scheduler.timesteps
# Prepare Input Batches
context_scheduler = get_context_scheduler(context_schedule)
context_queue = list(
context_scheduler(
0,
steps,
latents.shape[2], # number of frames
context_frames,
context_stride,
context_overlap,
)
)
num_context_batches = math.ceil(len(context_queue) / context_batch_size)
global_context = []
for i in range(num_context_batches):
global_context.append(
context_queue[
i * context_batch_size : (i + 1) * context_batch_size
]
)
"""
Example: num_frames == 72, context_frames == 24, context_stride == 1, context_overlap == 4, context_batch_size == 1
context_queue: [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43],
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63],
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
]
global_context: [ # shape: (num_context_batches, context_batch_size, context_frames)
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]],
[[20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43]],
[[40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]],
[[60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]]
]
"""
# Prepare initial latents
latents = self.prepare_latents(
batch_size=pose_latent.shape[0],
num_channels_latents=self.unet.config.in_channels,
video_length=video_length,
height=pose_latent.shape[-2],
width=pose_latent.shape[-1],
dtype=encoder_hidden_states.dtype,
device=device,
generator=generator,
)
# sequential double pass
latents = self.denoise_loop(
ref_image_latent=ref_image_latent,
loop_steps=steps,
latents=latents,
encoder_hidden_states=encoder_hidden_states,
pose_latent=pose_latent,
global_context=global_context,
cfg=cfg,
delta=delta,
do_double_pass=do_double_pass,
interpolation_factor=interpolation_factor,
device=device,
extra_step_kwargs=extra_step_kwargs,
)
return latents