Files
TashaSkyUp-EternalKernelPyt…/hf_diffusers_nodes.py
T
Tasha Upchurch d9170f804b New Features: Added nodes for advanced video and audio processing
Key updates:

Added AddFramesToVideoFolder node to add frames to a video folder
Introduced VideoFolderProvider and VideoDefinitionProvider nodes to manage video folders and video definitions
Added FadeToInVideoFolder node for fading between frames in a video folder
Implemented ListedSVDConditioning node for applying SVD conditioning to a list of parameters
Added VideoFolderFFMPEGVideoInterpolate node to interpolate frames in a video folder using FFmpeg
Introduced AudioFolderProvider and AudioDefinitionProvider nodes for managing audio folders and definitions
Added AddAudioToAudioFolder and AddSilenceToAudioFolder nodes for adding audio files and silence to audio folders
Implemented GetAudioFilesLengthsFromAudioFolder node to retrieve lengths of audio files in a folder
Added AudioFolderRenderAudio node to join audio files in an audio folder
Introduced GenerateMusic node for generating music from descriptions using the AudioCraft library
Added XttsNode for text-to-speech conversion using the XTTS library
Implemented AdjustAudioVolumeForFiles node to adjust volume for a list of audio files
The changes also include the addition of LiteLLM nodes for text generation:

Added FuncAnysToText node that runs Python code with multiple input types and outputs a string
Introduced FuncAnysToLatent node for running code that outputs a latent representation
Added FuncAnysToString node for executing code with various inputs and producing a string output
Implemented FuncAnysToList node to run code that takes different input types and returns a list
Added FuncAnysToCond node for running code with multiple inputs and generating a conditioning output

These new nodes expand the capabilities for video and audio processing, allowing more advanced manipulations and generation of video and audio content within the graph.
2024-04-15 13:29:31 -04:00

278 lines
8.3 KiB
Python

import torch
import folder_paths
import torchvision
import torch.nn as nn
import functools
import torch
import json
try:
from .config import config_settings
except ImportError as e:
from config import config_settings
NODE_CLASS_MAPPINGS = {}
NODE_DISPLAY_NAME_MAPPINGS = {}
def ETK_HF_Diffusers_base(cls):
cls.CATEGORY = "ETK/HF/Diffusers"
# Add spaces to the camel case class name
pretty_name = cls.__name__
for i in range(1, len(pretty_name)):
if pretty_name[i].isupper():
pretty_name = pretty_name[:i] + " " + pretty_name[i:]
cls.DISPLAY_NAME = pretty_name
NODE_CLASS_MAPPINGS[cls.__name__] = cls
NODE_DISPLAY_NAME_MAPPINGS[cls.DISPLAY_NAME] = pretty_name
# Wrap the function defined in the FUNCTION attribute
func_name = getattr(cls, "FUNCTION", None)
if func_name and hasattr(cls, func_name):
original_func = getattr(cls, func_name)
@functools.wraps(original_func)
def wrapped_func(*args, **kwargs):
with torch.inference_mode(False):
return original_func(*args, **kwargs)
setattr(cls, func_name, wrapped_func)
return cls
@ETK_HF_Diffusers_base
class DDPMPipline:
"""A node that loads the specified DDPMPipeline, supports only from_pretrained for now"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"model_name": ("STRING", {"default": "johnowhitaker/ddpm-butterflies-32px"}),
"device": (["cpu", "cuda"], {"default": "cuda"}),
},
}
RETURN_TYPES = ("DDPM_PL",)
FUNCTION = "load_from_pretrained"
def load_from_pretrained(self, model_name, device):
from diffusers import DDPMPipeline
ret = DDPMPipeline.from_pretrained(model_name, device=device)
return (ret,)
@ETK_HF_Diffusers_base
class SampleDDPMPipline:
"""A node that samples from the specified DDPMPipeline"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"model": ("DDPM_PL", {}),
"batch_size": ("INT", {"default": 1}),
"num_timesteps": ("INT", {"default": 1000}),
},
}
RETURN_TYPES = ("TORCH_TENSOR",)
FUNCTION = "sample"
def sample(
self,
model,
batch_size,
num_timesteps,
):
ret = model(
batch_size=batch_size,
generator=None,
num_inference_steps=num_timesteps,
output_type="tensor",
)
ret = torch.tensor(ret.images)
return (ret,)
@ETK_HF_Diffusers_base
class TensorToImage:
"""A node that converts a tensor to an image"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"tensor": ("TORCH_TENSOR", {}),
"incoming_format": (["BHWC", "BCHW", "HWC"], {"default": "BHWC"}
),
},
}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "tensor_to_image"
def tensor_to_image(self, tensor, incoming_format):
# outgoing format is BHWC torch tensor float 32 0-1
if incoming_format == "BCHW":
tensor = tensor.permute(0, 2, 3, 1)
elif incoming_format == "HWC":
tensor = tensor.unsqueeze(0)
tensor = tensor.clamp(0, 1)
return (tensor,)
@ETK_HF_Diffusers_base
class LoadHFDataset:
"""A node that loads the specified HFDataset"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"dataset_name": ("STRING", {"default": "huggan/smithsonian_butterflies_subset"}),
},
}
RETURN_TYPES = ("HF_DATASET",)
FUNCTION = "load_from_hf"
def load_from_hf(self, dataset_name):
from datasets import load_dataset
ret = load_dataset(dataset_name,
split="train",
)
return (ret,)
@ETK_HF_Diffusers_base
class CreateUnet2dModel:
"""A node that creates a unet2d model"""
@classmethod
def INPUT_TYPES(cls):
down_blocks = ("DownBlock2D", "AttnDownBlock2D", "AttnDownBlock2D", "AttnDownBlock2D")
mid_blocks = ('UNetMidBlock2D', 'UnCLIPUNetMidBlock2D')
up_blocks = ("AttnUpBlock2D", "AttnUpBlock2D", "AttnUpBlock2D", "UpBlock2D")
return {
"required": {
"name": ("STRING", {"default": "UNet2D"}),
},
"optional": {
"sample_size": ("TUPLE", {"default": (32, 32, 3,)}),
"in_channels": ("INT", {"default": 3}),
"out_channels": ("INT", {"default": 3}),
"norm_num_groups": ("INT", {"default": 32}),
"layers_per_block": ("INT", {"default": 2}),
"down_block_types": ("TUPLE", {"default": (down_blocks[0],)}),
"mid_block_scale_factor": ("FLOAT", {"default": 1.0}),
"up_block_types": ("TUPLE", {"default": (up_blocks[0],)}),
"block_out_channels": ("TUPLE", {"default": (64, 128, 256, 512,)}),
},
}
RETURN_TYPES = ("TORCH_MODEL",)
FUNCTION = "create_unet2d_model"
def create_unet2d_model(self, name, sample_size, in_channels, out_channels, norm_num_groups, layers_per_block,
down_block_types=("AttnDownBlock2D",),
mid_block_scale_factor=("UNetMidBlock2D",),
up_block_types=("AttnUpBlock2D",),
block_out_channels=(64, 128, 256, 512,)
):
from diffusers import UNet2DModel
with torch.inference_mode(False):
ret = UNet2DModel(
sample_size=sample_size,
in_channels=in_channels,
out_channels=out_channels,
norm_num_groups=norm_num_groups,
layers_per_block=layers_per_block,
down_block_types=down_block_types,
mid_block_scale_factor=mid_block_scale_factor,
up_block_types=up_block_types,
block_out_channels=block_out_channels,
)
for param in ret.parameters():
param.requires_grad = True
#param.retains_grad = True
return (ret,)
@ETK_HF_Diffusers_base
class TrainUnet2dModel:
"""A node that trains a unet2d model"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"model": ("TORCH_MODEL", {}),
"dataset": ("HF_DATASET", {}),
"batch_size": ("INT", {"default": 1}),
"num_epochs": ("INT", {"default": 1}),
"learning_rate": ("FLOAT", {"default": 1e-4}),
},
}
RETURN_TYPES = ("TORCH_MODEL",)
FUNCTION = "train_unet2d_model"
def train_unet2d_model(self, model, dataset, batch_size, num_epochs, learning_rate):
from torch.utils.data import DataLoader
# import mse loss
from torch.nn import MSELoss
from torch.optim import Adam
from diffusers import UNet2DModel
model = model.train()
model = model.cuda()
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)
optimizer = Adam(model.parameters(), lr=learning_rate)
loss_fn = MSELoss()
for epoch in range(num_epochs):
for batch in dataloader:
batch = batch["image"]
batch = batch.cuda()
optimizer.zero_grad()
loss = loss_fn(model, batch)
loss.backward()
optimizer.step()
print(loss.item())
return (model,)
# @ETK_HF_Diffusers_base
# class LoadModelFromHF:
# """A node that loads the specified model from huggingface"""
#
# @classmethod
# def INPUT_TYPES(cls):
# return {
# "required": {
# "model_name": ("STRING", {"default": "johnowhitaker/ddpm-butterflies-32px"}),
# },
# }
#
# RETURN_TYPES = ("TORCH_MODEL",)
# FUNCTION = "load_model_from_hf"
#
# def load_model_from_hf(self, model_name):
# from huggingface_hub import hf_hub_url
# from diffusers import DDPMPipeline
# ret = DDPMPipeline.from_pretrained(model_name, device="cuda")
# return (ret,)