- Added CogVideoSchedulerLoader and CogVideoSampler to NODE_CLASS_MAPPINGS.

- Updated node class mappings to reflect new node structure.
- Refactored nodes to ensure compatibility with ComfyUI standards, including:
  - `CogVideoModelLoader`
  - `CogVideoPipeExtra`
  - `CogVideoEncodePrompt`
  - `CogVideoImageEncodeSampler`
  - `CogVideoSamplerDecodeImages`
  - `CogVideoProcessor`
This commit is contained in:
u 2024-08-29 20:46:58 +08:00
parent 036fa14cac
commit 40c8dc4899

View File

@ -1,5 +1,5 @@
import os import os
from typing import List, Optional, Union from typing import Callable, Dict, List, Optional, Tuple, Union
import torch import torch
import folder_paths import folder_paths
import comfy.model_management as mm import comfy.model_management as mm
@ -7,17 +7,290 @@ from comfy.utils import ProgressBar
from diffusers.schedulers import CogVideoXDDIMScheduler, CogVideoXDPMScheduler from diffusers.schedulers import CogVideoXDDIMScheduler, CogVideoXDPMScheduler
from diffusers.models import AutoencoderKLCogVideoX, CogVideoXTransformer3DModel from diffusers.models import AutoencoderKLCogVideoX, CogVideoXTransformer3DModel
from diffusers import CogVideoXPipeline from diffusers import CogVideoXPipeline
from datetime import datetime, timedelta from diffusers.pipelines.cogvideo.pipeline_cogvideox import retrieve_timesteps, get_resize_crop_region_for_grid, get_3d_rotary_pos_embed
from diffusers.utils.torch_utils import randn_tensor
from diffusers.callbacks import MultiPipelineCallbacks, PipelineCallback
from diffusers.image_processor import VaeImageProcessor from diffusers.image_processor import VaeImageProcessor
from transformers.models.t5.modeling_t5 import T5EncoderModel from transformers.models.t5.modeling_t5 import T5EncoderModel
from transformers.models.t5.tokenization_t5 import T5Tokenizer from transformers.models.t5.tokenization_t5 import T5Tokenizer
import math
from tqdm.auto import tqdm
import inspect
import logging import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get_timesteps(
cogvideo_pipeline: CogVideoXPipeline,
num_inference_steps, strength, device):
# get the original timestep using init_timestep
init_timestep = min(int(num_inference_steps * strength), num_inference_steps)
t_start = max(num_inference_steps - init_timestep, 0)
timesteps = cogvideo_pipeline.scheduler.timesteps[t_start * cogvideo_pipeline.scheduler.order :]
if hasattr(cogvideo_pipeline.scheduler, "set_begin_index"):
cogvideo_pipeline.scheduler.set_begin_index(t_start * cogvideo_pipeline.scheduler.order)
return timesteps.to(device), num_inference_steps - t_start
def _prepare_rotary_positional_embeddings(
cogvideo_pipeline: CogVideoXPipeline,
height: int,
width: int,
num_frames: int,
device: torch.device,
) -> Tuple[torch.Tensor, torch.Tensor]:
grid_height = height // (cogvideo_pipeline.vae_scale_factor_spatial * cogvideo_pipeline.transformer.config.patch_size)
grid_width = width // (cogvideo_pipeline.vae_scale_factor_spatial * cogvideo_pipeline.transformer.config.patch_size)
base_size_width = 720 // (cogvideo_pipeline.vae_scale_factor_spatial * cogvideo_pipeline.transformer.config.patch_size)
base_size_height = 480 // (cogvideo_pipeline.vae_scale_factor_spatial * cogvideo_pipeline.transformer.config.patch_size)
grid_crops_coords = get_resize_crop_region_for_grid(
(grid_height, grid_width), base_size_width, base_size_height
)
freqs_cos, freqs_sin = get_3d_rotary_pos_embed(
embed_dim=cogvideo_pipeline.transformer.config.attention_head_dim,
crops_coords=grid_crops_coords,
grid_size=(grid_height, grid_width),
temporal_size=num_frames,
use_real=True,
)
freqs_cos = freqs_cos.to(device=device)
freqs_sin = freqs_sin.to(device=device)
return freqs_cos, freqs_sin
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.prepare_extra_step_kwargs
def prepare_extra_step_kwargs(
cogvideo_pipeline: CogVideoXPipeline,
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(cogvideo_pipeline.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(cogvideo_pipeline.scheduler.step).parameters.keys())
if accepts_generator:
extra_step_kwargs["generator"] = generator
return extra_step_kwargs
def prepare_latents(
cogvideo_pipeline: CogVideoXPipeline,
batch_size, num_channels_latents, num_frames, height, width, dtype, device, generator, latents=None
):
shape = (
batch_size,
(num_frames - 1) // cogvideo_pipeline.vae_scale_factor_temporal + 1,
num_channels_latents,
height // cogvideo_pipeline.vae_scale_factor_spatial,
width // cogvideo_pipeline.vae_scale_factor_spatial,
)
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 * cogvideo_pipeline.scheduler.init_noise_sigma
return latents
@torch.no_grad()
def cogvideox_sampler(
cogvideo_pipeline: CogVideoXPipeline,
height: int = 480,
width: int = 720,
num_frames: int = 49,
num_inference_steps: int = 50,
timesteps: Optional[List[int]] = None,
guidance_scale: float = 6,
use_dynamic_cfg: bool = False,
num_videos_per_prompt: int = 1,
eta: float = 0.0,
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
latents: Optional[torch.FloatTensor] = None,
prompt_embeds: Optional[torch.FloatTensor] = None,
negative_prompt_embeds: Optional[torch.FloatTensor] = None,
device = torch.device("cuda"),
):
"""
Function invoked when calling the pipeline for generation.
Args:
height (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
The height in pixels of the generated image. This is set to 1024 by default for the best results.
width (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
The width in pixels of the generated image. This is set to 1024 by default for the best results.
num_frames (`int`, defaults to `49`):
Number of frames to generate. Must be divisible by self.vae_scale_factor_temporal. Generated video will
contain 1 extra frame because CogVideoX is conditioned with (num_seconds * fps + 1) frames where
num_seconds is 6 and fps is 4. However, since videos can be saved at any fps, the only condition that
needs to be satisfied is that of divisibility mentioned above.
num_inference_steps (`int`, *optional*, defaults to 50):
The number of denoising steps. More denoising steps usually lead to a higher quality image at the
expense of slower inference.
timesteps (`List[int]`, *optional*):
Custom timesteps to use for the denoising process with schedulers which support a `timesteps` argument
in their `set_timesteps` method. If not defined, the default behavior when `num_inference_steps` is
passed will be used. Must be in descending order.
guidance_scale (`float`, *optional*, defaults to 7.0):
Guidance scale as defined in [Classifier-Free Diffusion Guidance](https://arxiv.org/abs/2207.12598).
`guidance_scale` is defined as `w` of equation 2. of [Imagen
Paper](https://arxiv.org/pdf/2205.11487.pdf). Guidance scale is enabled by setting `guidance_scale >
1`. Higher guidance scale encourages to generate images that are closely linked to the text `prompt`,
usually at the expense of lower image quality.
num_videos_per_prompt (`int`, *optional*, defaults to 1):
The number of videos to generate per prompt.
generator (`torch.Generator` or `List[torch.Generator]`, *optional*):
One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html)
to make generation deterministic.
latents (`torch.FloatTensor`, *optional*):
Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image
generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
tensor will ge generated by sampling using the supplied random `generator`.
prompt_embeds (`torch.FloatTensor`, *optional*):
Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
provided, text embeddings will be generated from `prompt` input argument.
negative_prompt_embeds (`torch.FloatTensor`, *optional*):
Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
argument.
"""
if num_frames > 49:
raise ValueError(
"The number of frames must be less than 49 for now due to static positional embeddings. This will be updated in the future to remove this limitation."
)
height = height or cogvideo_pipeline.transformer.config.sample_size * cogvideo_pipeline.vae_scale_factor_spatial
width = width or cogvideo_pipeline.transformer.config.sample_size * cogvideo_pipeline.vae_scale_factor_spatial
num_videos_per_prompt = 1
cogvideo_pipeline._guidance_scale = guidance_scale
cogvideo_pipeline._interrupt = False
# 2. Default call parameters
batch_size = prompt_embeds.shape[0]
# here `guidance_scale` is defined analog to the guidance weight `w` of equation (2)
# of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1`
# corresponds to doing no classifier free guidance.
do_classifier_free_guidance = guidance_scale > 1.0
if do_classifier_free_guidance:
prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds], dim=0)
prompt_embeds = prompt_embeds.to(cogvideo_pipeline.transformer.dtype)
# 4. Prepare timesteps
timesteps, num_inference_steps = retrieve_timesteps(cogvideo_pipeline.scheduler, num_inference_steps, device, timesteps)
cogvideo_pipeline._num_timesteps = len(timesteps)
# 5. Prepare latents.
latent_channels = cogvideo_pipeline.transformer.config.in_channels
latents = prepare_latents(
cogvideo_pipeline,
batch_size * num_videos_per_prompt,
latent_channels,
num_frames,
height,
width,
prompt_embeds.dtype,
device,
generator,
latents,
)
latents = latents.to(cogvideo_pipeline.transformer.dtype)
# 6. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline
extra_step_kwargs = prepare_extra_step_kwargs(cogvideo_pipeline, generator, eta)
# 7. Create rotary embeds if required
image_rotary_emb = (
_prepare_rotary_positional_embeddings(cogvideo_pipeline, height, width, latents.size(1), device)
if cogvideo_pipeline.transformer.config.use_rotary_positional_embeddings
else None
)
# 8. Denoising loop
num_warmup_steps = max(len(timesteps) - num_inference_steps * cogvideo_pipeline.scheduler.order, 0)
comfy_pbar = ProgressBar(num_inference_steps)
with tqdm(total=num_inference_steps) as progress_bar:
# for DPM-solver++
old_pred_original_sample = None
for i, t in enumerate(timesteps):
if cogvideo_pipeline.interrupt:
continue
latent_model_input = torch.cat([latents] * 2) if do_classifier_free_guidance else latents
latent_model_input = cogvideo_pipeline.scheduler.scale_model_input(latent_model_input, t)
# broadcast to batch dimension in a way that's compatible with ONNX/Core ML
timestep = t.expand(latent_model_input.shape[0])
# predict noise model_output
noise_pred = cogvideo_pipeline.transformer(
hidden_states=latent_model_input,
encoder_hidden_states=prompt_embeds,
timestep=timestep,
image_rotary_emb=image_rotary_emb,
return_dict=False,
)[0]
noise_pred = noise_pred.float()
# perform guidance
if use_dynamic_cfg:
cogvideo_pipeline._guidance_scale = 1 + guidance_scale * (
(1 - math.cos(math.pi * ((num_inference_steps - t.item()) / num_inference_steps) ** 5.0)) / 2
)
if do_classifier_free_guidance:
noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
noise_pred = noise_pred_uncond + cogvideo_pipeline.guidance_scale * (noise_pred_text - noise_pred_uncond)
# compute the previous noisy sample x_t -> x_t-1
if not isinstance(cogvideo_pipeline.scheduler, CogVideoXDPMScheduler):
latents = cogvideo_pipeline.scheduler.step(noise_pred, t, latents, **extra_step_kwargs, return_dict=False)[0]
else:
latents, old_pred_original_sample = cogvideo_pipeline.scheduler.step(
noise_pred,
old_pred_original_sample,
t,
timesteps[i - 1] if i > 0 else None,
latents,
**extra_step_kwargs,
return_dict=False,
)
latents = latents.to(prompt_embeds.dtype)
if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % cogvideo_pipeline.scheduler.order == 0):
progress_bar.update()
comfy_pbar.update(1)
# Offload all models
cogvideo_pipeline.maybe_free_model_hooks()
return latents
class CogVideoModelLoader: class CogVideoModelLoader:
@classmethod @classmethod
def INPUT_TYPES(s): def INPUT_TYPES(s):
@ -39,6 +312,7 @@ class CogVideoModelLoader:
RETURN_NAMES = ("cogvideo_pipe",) RETURN_NAMES = ("cogvideo_pipe",)
FUNCTION = "cogvideo_loader" FUNCTION = "cogvideo_loader"
CATEGORY = "loaders" CATEGORY = "loaders"
DESCRIPTION = """CogVideoX model loader"""
def cogvideo_loader(self, base_path): def cogvideo_loader(self, base_path):
precision = "bf16" precision = "bf16"
@ -62,6 +336,42 @@ class CogVideoModelLoader:
return (pipeline,) return (pipeline,)
class CogVideoSchedulerLoader:
@classmethod
def INPUT_TYPES(s):
cog_videox_base = folder_paths.get_folder_paths("cog_videox")
model_paths = []
for path_ in cog_videox_base:
if path_ is None:
continue
for x in os.listdir(path_):
model_paths.append(os.path.join(path_, x))
return {
"required": {
"base_path": (model_paths,),
"scheduler": (["DDIM", "DPM"], {"tooltip": "5B likes DPM, but it doesn't support temporal tiling"}),
},
}
RETURN_TYPES = ("COGVIDEOSCHEDULER",)
RETURN_NAMES = ("scheduler",)
FUNCTION = "cogvideo_scheduler_loader"
CATEGORY = "loaders"
DESCRIPTION = """CogVideoX scheduler loader"""
def cogvideo_scheduler_loader(self, base_path, scheduler):
pipe_scheduler = None
if scheduler == "DDIM":
pipe_scheduler = CogVideoXDDIMScheduler.from_pretrained(base_path, subfolder="scheduler")
elif scheduler == "DPM":
pipe_scheduler = CogVideoXDPMScheduler.from_pretrained(base_path, subfolder="scheduler")
else:
raise ValueError(f"Invalid scheduler {scheduler}")
return (pipe_scheduler,)
class CogVideoPipeExtra: class CogVideoPipeExtra:
@classmethod @classmethod
@ -72,10 +382,11 @@ class CogVideoPipeExtra:
}, },
} }
RETURN_TYPES = ("T5_TOKENIZER", "TEXT_ENCODER", "VAE3D","3DTRANSFORMER", "SCHEDULER", "DTYPE") RETURN_TYPES = ("T5_TOKENIZER", "TEXT_ENCODER", "VAE3D","3DTRANSFORMER", "COGVIDEOSCHEDULER", "DTYPE",)
RETURN_NAMES = ("t5_tokenizer", "text_encoder", "vae3d", "transformer","scheduler" "dtype") RETURN_NAMES = ("t5_tokenizer", "text_encoder", "vae3d", "transformer","scheduler" "dtype",)
FUNCTION = "extra" FUNCTION = "extra"
CATEGORY = "loaders" CATEGORY = "loaders"
DESCRIPTION = """CogVideoX pipeline extra loader, return t5_tokenizer, text_encoder, vae3d, transformer, scheduler, dtype"""
def extra(self, pipeline): def extra(self, pipeline):
t5_tokenizer = pipeline["pipe"].tokenizer t5_tokenizer = pipeline["pipe"].tokenizer
@ -105,6 +416,7 @@ class CogVideoEncodePrompt:
RETURN_NAMES = ("positive", "negative") RETURN_NAMES = ("positive", "negative")
FUNCTION = "process" FUNCTION = "process"
CATEGORY = "latent" CATEGORY = "latent"
DESCRIPTION = """VideoEncode prompt, eg: Also use clip to convert CONDITIONING"""
def _get_t5_prompt_embeds( def _get_t5_prompt_embeds(
@ -268,6 +580,7 @@ class CogVideoImageEncodeSampler:
RETURN_NAMES = ("samples",) RETURN_NAMES = ("samples",)
FUNCTION = "encode" FUNCTION = "encode"
CATEGORY = "latent" CATEGORY = "latent"
DESCRIPTION = """3DVAE image Encode to latent"""
def encode(self, vae3d, image): def encode(self, vae3d, image):
device = mm.get_torch_device() device = mm.get_torch_device()
@ -305,7 +618,6 @@ class CogVideoImageEncodeSampler:
# Concatenate all the chunks along the temporal dimension # Concatenate all the chunks along the temporal dimension
final_latents = torch.cat(latents_list, dim=1) final_latents = torch.cat(latents_list, dim=1)
print("final latents: ", final_latents.shape)
vae3d.to(offload_device) vae3d.to(offload_device)
@ -322,57 +634,42 @@ class CogVideoSampler:
"negative": ("CONDITIONING", ), "negative": ("CONDITIONING", ),
"height": ("INT", {"default": 480, "min": 128, "max": 2048, "step": 8}), "height": ("INT", {"default": 480, "min": 128, "max": 2048, "step": 8}),
"width": ("INT", {"default": 720, "min": 128, "max": 2048, "step": 8}), "width": ("INT", {"default": 720, "min": 128, "max": 2048, "step": 8}),
"num_frames": ("INT", {"default": 49, "min": 16, "max": 1024, "step": 1}),
"steps": ("INT", {"default": 50, "min": 1}), "steps": ("INT", {"default": 50, "min": 1}),
"cfg": ("FLOAT", {"default": 6.0, "min": 0.0, "max": 30.0, "step": 0.01}), "cfg": ("FLOAT", {"default": 6.0, "min": 0.0, "max": 30.0, "step": 0.01}),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"scheduler": (["DDIM", "DPM"], {"tooltip": "5B likes DPM, but it doesn't support temporal tiling"}),
"t_tile_length": ("INT", {"default": 16, "min": 2, "max": 128, "step": 1, "tooltip": "Length of temporal tiling, use same alue as num_frames to disable, disabled automatically for DPM"}),
"t_tile_overlap": ("INT", {"default": 8, "min": 2, "max": 128, "step": 1, "tooltip": "Overlap of temporal tiling"}),
}, },
"optional": { "optional": {
"samples": ("LATENT", ), "samples": ("LATENT", ),
"denoise_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), "scheduler": ("COGVIDEOSCHEDULER", ),
} }
} }
RETURN_TYPES = ("COGVIDEOPIPE", "LATENT",) RETURN_TYPES = ("LATENT",)
RETURN_NAMES = ("cogvideo_pipe", "samples",) RETURN_NAMES = ("samples",)
FUNCTION = "process" FUNCTION = "process"
CATEGORY = "CogVideoWrapper" CATEGORY = "sampler"
DESCRIPTION = """CogVideoX sampler"""
def process(self, pipeline, positive, negative, steps, cfg, seed, height, width, num_frames, scheduler, t_tile_length, t_tile_overlap, samples=None, denoise_strength=1.0): def process(self, pipeline, positive, negative, steps, cfg, seed, height, width, samples=None, scheduler=None):
mm.soft_empty_cache() mm.soft_empty_cache()
assert t_tile_length > t_tile_overlap, "t_tile_length must be greater than t_tile_overlap"
assert t_tile_length <= num_frames, "t_tile_length must be equal or less than num_frames"
t_tile_length = t_tile_length // 4
t_tile_overlap = t_tile_overlap // 4
device = mm.get_torch_device() device = mm.get_torch_device()
offload_device = mm.unet_offload_device() offload_device = mm.unet_offload_device()
pipe = pipeline["pipe"] pipe = pipeline["pipe"]
dtype = pipeline["dtype"] dtype = pipeline["dtype"]
base_path = pipeline["base_path"]
pipe.transformer.to(device) pipe.transformer.to(device)
generator = torch.Generator(device=device).manual_seed(seed) generator = torch.Generator(device=device).manual_seed(seed)
if scheduler is not None:
pipe.scheduler = scheduler
if scheduler == "DDIM":
pipe.scheduler = CogVideoXDDIMScheduler.from_pretrained(base_path, subfolder="scheduler")
elif scheduler == "DPM":
pipe.scheduler = CogVideoXDPMScheduler.from_pretrained(base_path, subfolder="scheduler")
with torch.autocast(mm.get_autocast_device(device)): with torch.autocast(mm.get_autocast_device(device)):
latents = pipeline["pipe"]( latents = cogvideox_sampler(
pipe,
num_inference_steps=steps, num_inference_steps=steps,
height = height, height = height,
width = width, width = width,
num_frames = num_frames,
t_tile_length = t_tile_length,
t_tile_overlap = t_tile_overlap,
guidance_scale=cfg, guidance_scale=cfg,
latents=samples["samples"] if samples is not None else None, latents=samples["samples"] if samples is not None else None,
denoise_strength=denoise_strength,
prompt_embeds=positive.to(dtype).to(device), prompt_embeds=positive.to(dtype).to(device),
negative_prompt_embeds=negative.to(dtype).to(device), negative_prompt_embeds=negative.to(dtype).to(device),
generator=generator, generator=generator,
@ -380,9 +677,8 @@ class CogVideoSampler:
) )
pipe.transformer.to(offload_device) pipe.transformer.to(offload_device)
mm.soft_empty_cache() mm.soft_empty_cache()
print(latents.shape)
return (pipeline, {"samples": latents}) return ({"samples": latents}, )
class CogVideoSamplerDecodeImages: class CogVideoSamplerDecodeImages:
@classmethod @classmethod
@ -390,22 +686,16 @@ class CogVideoSamplerDecodeImages:
return {"required": { return {"required": {
"vae3d": ("VAE3D",), "vae3d": ("VAE3D",),
"samples": ("LATENT", ), "samples": ("LATENT", ),
"enable_vae_tiling": ("BOOLEAN", {"default": False}),
}, },
"optional": {
"tile_sample_min_height": ("INT", {"default": 96, "min": 16, "max": 2048, "step": 8}),
"tile_sample_min_width": ("INT", {"default": 96, "min": 16, "max": 2048, "step": 8}),
"tile_overlap_factor_height": ("FLOAT", {"default": 0.083, "min": 0.0, "max": 1.0, "step": 0.001}),
"tile_overlap_factor_width": ("FLOAT", {"default": 0.083, "min": 0.0, "max": 1.0, "step": 0.001}),
}
} }
RETURN_TYPES = ("BARCHIMAGE",) RETURN_TYPES = ("BARCHIMAGE",)
RETURN_NAMES = ("b_images",) RETURN_NAMES = ("b_images",)
FUNCTION = "decode" FUNCTION = "decode"
CATEGORY = "latent" CATEGORY = "latent"
DESCRIPTION = """CogVideoX sampler 3DVAE decode images"""
def decode(self, vae3d, samples, enable_vae_tiling, tile_sample_min_height, tile_sample_min_width, tile_overlap_factor_height, tile_overlap_factor_width): def decode(self, vae3d, samples):
""" """
vae3d: VAE3D vae3d: VAE3D
samples: dict of samples, torch.Tensor # B, T_chunk, C, H, W samples: dict of samples, torch.Tensor # B, T_chunk, C, H, W
@ -414,13 +704,7 @@ class CogVideoSamplerDecodeImages:
offload_device = mm.unet_offload_device() offload_device = mm.unet_offload_device()
latents = samples["samples"] latents = samples["samples"]
vae3d.to(device) vae3d.to(device)
if enable_vae_tiling:
vae3d.enable_tiling(
tile_sample_min_height=tile_sample_min_height,
tile_sample_min_width=tile_sample_min_width,
tile_overlap_factor_height=tile_overlap_factor_height,
tile_overlap_factor_width=tile_overlap_factor_width,
)
latents = latents.to(vae3d.dtype) latents = latents.to(vae3d.dtype)
latents = latents.permute(0, 2, 1, 3, 4) # [batch_size, num_channels, num_frames, height, width] latents = latents.permute(0, 2, 1, 3, 4) # [batch_size, num_channels, num_frames, height, width]
latents = 1 / vae3d.config.scaling_factor * latents latents = 1 / vae3d.config.scaling_factor * latents
@ -458,7 +742,7 @@ class CogVideoProcessor:
RETURN_TYPES = ("IMAGE","INT", "INT", "INT", "INT", "INT",) RETURN_TYPES = ("IMAGE","INT", "INT", "INT", "INT", "INT",)
RETURN_NAMES = ("image", "width", "height", "count", "batch_index", "batch_size",) RETURN_NAMES = ("image", "width", "height", "count", "batch_index", "batch_size",)
FUNCTION = "process" FUNCTION = "process"
CATEGORY = "video" CATEGORY = "latent"
DESCRIPTION = """Converts CogVideo tensor, pt image to np image or plt image, DESCRIPTION = """Converts CogVideo tensor, pt image to np image or plt image,
and returns the width, height, count, batch_index, and batch_size. and returns the width, height, count, batch_index, and batch_size.
@ -491,6 +775,7 @@ and returns the width, height, count, batch_index, and batch_size.
NODE_CLASS_MAPPINGS = { NODE_CLASS_MAPPINGS = {
"CogVideoModelLoader": CogVideoModelLoader, "CogVideoModelLoader": CogVideoModelLoader,
"CogVideoSchedulerLoader": CogVideoSchedulerLoader,
"CogVideoPipeExtra": CogVideoPipeExtra, "CogVideoPipeExtra": CogVideoPipeExtra,
"CogVideoEncodePrompt": CogVideoEncodePrompt, "CogVideoEncodePrompt": CogVideoEncodePrompt,
"CogVideoSampler": CogVideoSampler, "CogVideoSampler": CogVideoSampler,
@ -500,6 +785,7 @@ NODE_CLASS_MAPPINGS = {
} }
NODE_DISPLAY_NAME_MAPPINGS = { NODE_DISPLAY_NAME_MAPPINGS = {
"CogVideoModelLoader": "CogVideo ModelLoader", "CogVideoModelLoader": "CogVideo ModelLoader",
"CogVideoSchedulerLoader": "CogVideo SchedulerLoader",
"CogVideoPipeExtra": "CogVideo PipeExtra", "CogVideoPipeExtra": "CogVideo PipeExtra",
"CogVideoEncodePrompt": "CogVideo EncodePrompt", "CogVideoEncodePrompt": "CogVideo EncodePrompt",
"CogVideoSampler": "CogVideo Sampler", "CogVideoSampler": "CogVideo Sampler",