diff --git a/comfy_extras/nodes/nodes_ace.py b/comfy_extras/nodes/nodes_ace.py index cbfec15a2..63ab0949d 100644 --- a/comfy_extras/nodes/nodes_ace.py +++ b/comfy_extras/nodes/nodes_ace.py @@ -1,16 +1,19 @@ import torch + import comfy.model_management -import node_helpers +from comfy import node_helpers + class TextEncodeAceStepAudio: @classmethod def INPUT_TYPES(s): return {"required": { - "clip": ("CLIP", ), + "clip": ("CLIP",), "tags": ("STRING", {"multiline": True, "dynamicPrompts": True}), "lyrics": ("STRING", {"multiline": True, "dynamicPrompts": True}), "lyrics_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), - }} + }} + RETURN_TYPES = ("CONDITIONING",) FUNCTION = "encode" @@ -20,7 +23,7 @@ class TextEncodeAceStepAudio: tokens = clip.tokenize(tags, lyrics=lyrics) conditioning = clip.encode_from_tokens_scheduled(tokens) conditioning = node_helpers.conditioning_set_values(conditioning, {"lyrics_strength": lyrics_strength}) - return (conditioning, ) + return (conditioning,) class EmptyAceStepLatentAudio: @@ -32,6 +35,7 @@ class EmptyAceStepLatentAudio: return {"required": {"seconds": ("FLOAT", {"default": 120.0, "min": 1.0, "max": 1000.0, "step": 0.1}), "batch_size": ("INT", {"default": 1, "min": 1, "max": 4096, "tooltip": "The number of latent images in the batch."}), }} + RETURN_TYPES = ("LATENT",) FUNCTION = "generate" @@ -40,7 +44,7 @@ class EmptyAceStepLatentAudio: def generate(self, seconds, batch_size): length = int(seconds * 44100 / 512 / 8) latent = torch.zeros([batch_size, 8, 16, length], device=self.device) - return ({"samples": latent, "type": "audio"}, ) + return ({"samples": latent, "type": "audio"},) NODE_CLASS_MAPPINGS = { diff --git a/comfy_extras/nodes/nodes_apg.py b/comfy_extras/nodes/nodes_apg.py index 25b21b1b8..261d85f07 100644 --- a/comfy_extras/nodes/nodes_apg.py +++ b/comfy_extras/nodes/nodes_apg.py @@ -1,11 +1,13 @@ import torch + def project(v0, v1): v1 = torch.nn.functional.normalize(v1, dim=[-1, -2, -3]) v0_parallel = (v0 * v1).sum(dim=[-1, -2, -3], keepdim=True) * v1 v0_orthogonal = v0 - v0_parallel return v0_parallel, v0_orthogonal + class APG: @classmethod def INPUT_TYPES(s): @@ -14,9 +16,10 @@ class APG: "model": ("MODEL",), "eta": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01, "tooltip": "Controls the scale of the parallel guidance vector. Default CFG behavior at a setting of 1."}), "norm_threshold": ("FLOAT", {"default": 5.0, "min": 0.0, "max": 50.0, "step": 0.1, "tooltip": "Normalize guidance vector to this value, normalization disable at a setting of 0."}), - "momentum": ("FLOAT", {"default": 0.0, "min": -5.0, "max": 1.0, "step": 0.01, "tooltip":"Controls a running average of guidance during diffusion, disabled at a setting of 0."}), + "momentum": ("FLOAT", {"default": 0.0, "min": -5.0, "max": 1.0, "step": 0.01, "tooltip": "Controls a running average of guidance during diffusion, disabled at a setting of 0."}), } } + RETURN_TYPES = ("MODEL",) FUNCTION = "patch" CATEGORY = "sampling/custom_sampling" @@ -67,6 +70,7 @@ class APG: m.set_model_sampler_pre_cfg_function(pre_cfg_function) return (m,) + NODE_CLASS_MAPPINGS = { "APG": APG, } diff --git a/comfy_extras/nodes/nodes_camera_trajectory.py b/comfy_extras/nodes/nodes_camera_trajectory.py index 5e0e39f91..43084c965 100644 --- a/comfy_extras/nodes/nodes_camera_trajectory.py +++ b/comfy_extras/nodes/nodes_camera_trajectory.py @@ -1,30 +1,26 @@ -import nodes -import torch import numpy as np +import torch from einops import rearrange + import comfy.model_management - - - -MAX_RESOLUTION = nodes.MAX_RESOLUTION +from comfy.nodes.common import MAX_RESOLUTION CAMERA_DICT = { "base_T_norm": 1.5, - "base_angle": np.pi/3, - "Static": { "angle":[0., 0., 0.], "T":[0., 0., 0.]}, - "Pan Up": { "angle":[0., 0., 0.], "T":[0., -1., 0.]}, - "Pan Down": { "angle":[0., 0., 0.], "T":[0.,1.,0.]}, - "Pan Left": { "angle":[0., 0., 0.], "T":[-1.,0.,0.]}, - "Pan Right": { "angle":[0., 0., 0.], "T": [1.,0.,0.]}, - "Zoom In": { "angle":[0., 0., 0.], "T": [0.,0.,2.]}, - "Zoom Out": { "angle":[0., 0., 0.], "T": [0.,0.,-2.]}, - "Anti Clockwise (ACW)": { "angle": [0., 0., -1.], "T":[0., 0., 0.]}, - "ClockWise (CW)": { "angle": [0., 0., 1.], "T":[0., 0., 0.]}, + "base_angle": np.pi / 3, + "Static": {"angle": [0., 0., 0.], "T": [0., 0., 0.]}, + "Pan Up": {"angle": [0., 0., 0.], "T": [0., -1., 0.]}, + "Pan Down": {"angle": [0., 0., 0.], "T": [0., 1., 0.]}, + "Pan Left": {"angle": [0., 0., 0.], "T": [-1., 0., 0.]}, + "Pan Right": {"angle": [0., 0., 0.], "T": [1., 0., 0.]}, + "Zoom In": {"angle": [0., 0., 0.], "T": [0., 0., 2.]}, + "Zoom Out": {"angle": [0., 0., 0.], "T": [0., 0., -2.]}, + "Anti Clockwise (ACW)": {"angle": [0., 0., -1.], "T": [0., 0., 0.]}, + "ClockWise (CW)": {"angle": [0., 0., 1.], "T": [0., 0., 0.]}, } def process_pose_params(cam_params, width=672, height=384, original_pose_width=1280, original_pose_height=720, device='cpu'): - def get_relative_pose(cam_params): """Copied from https://github.com/hehao13/CameraCtrl/blob/main/inference.py """ @@ -59,9 +55,9 @@ def process_pose_params(cam_params, width=672, height=384, original_pose_width=1 cam_param.fy = resized_ori_h * cam_param.fy / height intrinsic = np.asarray([[cam_param.fx * width, - cam_param.fy * height, - cam_param.cx * width, - cam_param.cy * height] + cam_param.fy * height, + cam_param.cx * width, + cam_param.cy * height] for cam_param in cam_params], dtype=np.float32) K = torch.as_tensor(intrinsic)[None] # [1, 1, 4] @@ -72,9 +68,11 @@ def process_pose_params(cam_params, width=672, height=384, original_pose_width=1 plucker_embedding = rearrange(plucker_embedding, "b f c h w -> b f h w c")[0] return plucker_embedding + class Camera(object): """Copied from https://github.com/hehao13/CameraCtrl/blob/main/inference.py """ + def __init__(self, entry): fx, fy, cx, cy = entry[1:5] self.fx = fx @@ -85,6 +83,7 @@ class Camera(object): self.c2w_mat = c2w_mat self.w2c_mat = np.linalg.inv(c2w_mat) + def ray_condition(K, c2w, H, W, device): """Copied from https://github.com/hehao13/CameraCtrl/blob/main/inference.py """ @@ -121,59 +120,62 @@ def ray_condition(K, c2w, H, W, device): # plucker = plucker.permute(0, 1, 4, 2, 3) return plucker + def get_camera_motion(angle, T, speed, n=81): def compute_R_form_rad_angle(angles): theta_x, theta_y, theta_z = angles Rx = np.array([[1, 0, 0], - [0, np.cos(theta_x), -np.sin(theta_x)], - [0, np.sin(theta_x), np.cos(theta_x)]]) + [0, np.cos(theta_x), -np.sin(theta_x)], + [0, np.sin(theta_x), np.cos(theta_x)]]) Ry = np.array([[np.cos(theta_y), 0, np.sin(theta_y)], - [0, 1, 0], - [-np.sin(theta_y), 0, np.cos(theta_y)]]) + [0, 1, 0], + [-np.sin(theta_y), 0, np.cos(theta_y)]]) Rz = np.array([[np.cos(theta_z), -np.sin(theta_z), 0], - [np.sin(theta_z), np.cos(theta_z), 0], - [0, 0, 1]]) + [np.sin(theta_z), np.cos(theta_z), 0], + [0, 0, 1]]) R = np.dot(Rz, np.dot(Ry, Rx)) return R + RT = [] for i in range(n): - _angle = (i/n)*speed*(CAMERA_DICT["base_angle"])*angle + _angle = (i / n) * speed * (CAMERA_DICT["base_angle"]) * angle R = compute_R_form_rad_angle(_angle) - _T=(i/n)*speed*(CAMERA_DICT["base_T_norm"])*(T.reshape(3,1)) - _RT = np.concatenate([R,_T], axis=1) + _T = (i / n) * speed * (CAMERA_DICT["base_T_norm"]) * (T.reshape(3, 1)) + _RT = np.concatenate([R, _T], axis=1) RT.append(_RT) RT = np.stack(RT) return RT + class WanCameraEmbedding: @classmethod def INPUT_TYPES(cls): return { "required": { - "camera_pose":(["Static","Pan Up","Pan Down","Pan Left","Pan Right","Zoom In","Zoom Out","Anti Clockwise (ACW)", "ClockWise (CW)"],{"default":"Static"}), + "camera_pose": (["Static", "Pan Up", "Pan Down", "Pan Left", "Pan Right", "Zoom In", "Zoom Out", "Anti Clockwise (ACW)", "ClockWise (CW)"], {"default": "Static"}), "width": ("INT", {"default": 832, "min": 16, "max": MAX_RESOLUTION, "step": 16}), "height": ("INT", {"default": 480, "min": 16, "max": MAX_RESOLUTION, "step": 16}), "length": ("INT", {"default": 81, "min": 1, "max": MAX_RESOLUTION, "step": 4}), }, - "optional":{ - "speed":("FLOAT",{"default":1.0, "min": 0, "max": 10.0, "step": 0.1}), - "fx":("FLOAT",{"default":0.5, "min": 0, "max": 1, "step": 0.000000001}), - "fy":("FLOAT",{"default":0.5, "min": 0, "max": 1, "step": 0.000000001}), - "cx":("FLOAT",{"default":0.5, "min": 0, "max": 1, "step": 0.01}), - "cy":("FLOAT",{"default":0.5, "min": 0, "max": 1, "step": 0.01}), + "optional": { + "speed": ("FLOAT", {"default": 1.0, "min": 0, "max": 10.0, "step": 0.1}), + "fx": ("FLOAT", {"default": 0.5, "min": 0, "max": 1, "step": 0.000000001}), + "fy": ("FLOAT", {"default": 0.5, "min": 0, "max": 1, "step": 0.000000001}), + "cx": ("FLOAT", {"default": 0.5, "min": 0, "max": 1, "step": 0.01}), + "cy": ("FLOAT", {"default": 0.5, "min": 0, "max": 1, "step": 0.01}), } } - RETURN_TYPES = ("WAN_CAMERA_EMBEDDING","INT","INT","INT") - RETURN_NAMES = ("camera_embedding","width","height","length") + RETURN_TYPES = ("WAN_CAMERA_EMBEDDING", "INT", "INT", "INT") + RETURN_NAMES = ("camera_embedding", "width", "height", "length") FUNCTION = "run" CATEGORY = "camera" - def run(self, camera_pose, width, height, length, speed=1.0, fx=0.5, fy=0.5, cx=0.5, cy=0.5): + def run(self, camera_pose, width, height, length, speed=1.0, fx=0.5, fy=0.5, cx=0.5, cy=0.5): """ Use Camera trajectory as extrinsic parameters to calculate Plücker embeddings (Sitzmannet al., 2021) Adapted from https://github.com/aigc-apps/VideoX-Fun/blob/main/comfyui/comfyui_nodes.py @@ -184,13 +186,13 @@ class WanCameraEmbedding: T = np.array(CAMERA_DICT[motion_list[0]]["T"]) RT = get_camera_motion(angle, T, speed, length) - trajs=[] + trajs = [] for cp in RT.tolist(): - traj=[fx,fy,cx,cy,0,0] + traj = [fx, fy, cx, cy, 0, 0] traj.extend(cp[0]) traj.extend(cp[1]) traj.extend(cp[2]) - traj.extend([0,0,0,1]) + traj.extend([0, 0, 0, 1]) trajs.append(traj) cam_params = np.array([[float(x) for x in pose] for pose in trajs]) diff --git a/comfy_extras/nodes/nodes_string.py b/comfy_extras/nodes/nodes_string.py index b1a8ceef0..502c991e8 100644 --- a/comfy_extras/nodes/nodes_string.py +++ b/comfy_extras/nodes/nodes_string.py @@ -2,6 +2,7 @@ import re from comfy.comfy_types.node_typing import IO + class StringConcatenate(): @classmethod def INPUT_TYPES(s): @@ -20,6 +21,7 @@ class StringConcatenate(): def execute(self, string_a, string_b, delimiter, **kwargs): return delimiter.join((string_a, string_b)), + class StringSubstring(): @classmethod def INPUT_TYPES(s): @@ -38,6 +40,7 @@ class StringSubstring(): def execute(self, string, start, end, **kwargs): return string[start:end], + class StringLength(): @classmethod def INPUT_TYPES(s): @@ -57,6 +60,7 @@ class StringLength(): return length, + class CaseConverter(): @classmethod def INPUT_TYPES(s): @@ -112,6 +116,7 @@ class StringTrim(): return result, + class StringReplace(): @classmethod def INPUT_TYPES(s): @@ -188,6 +193,7 @@ class StringCompare(): elif mode == "Ends With": return a.endswith(b), + class RegexMatch(): @classmethod def INPUT_TYPES(s): @@ -299,6 +305,7 @@ class RegexExtract(): class RegexReplace(): DESCRIPTION = "Find and replace text using regex patterns." + @classmethod def INPUT_TYPES(s): return { @@ -331,6 +338,7 @@ class RegexReplace(): result = re.sub(regex_pattern, replace, string, count=count, flags=flags) return result, + NODE_CLASS_MAPPINGS = { "StringConcatenate": StringConcatenate, "StringSubstring": StringSubstring,