| |
| import torch |
| from torchvision.transforms import functional as TF |
| from PIL import Image, ImageDraw |
| import numpy as np |
| from ..utility.utility import pil2tensor |
| from nodes import MAX_RESOLUTION |
|
|
| class NormalizedAmplitudeToMask: |
| @classmethod |
| def INPUT_TYPES(s): |
| return {"required": { |
| "normalized_amp": ("NORMALIZED_AMPLITUDE",), |
| "width": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), |
| "height": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), |
| "frame_offset": ("INT", {"default": 0,"min": -255, "max": 255, "step": 1}), |
| "location_x": ("INT", {"default": 256,"min": 0, "max": 4096, "step": 1}), |
| "location_y": ("INT", {"default": 256,"min": 0, "max": 4096, "step": 1}), |
| "size": ("INT", {"default": 128,"min": 8, "max": 4096, "step": 1}), |
| "shape": ( |
| [ |
| 'none', |
| 'circle', |
| 'square', |
| 'triangle', |
| ], |
| { |
| "default": 'none' |
| }), |
| "color": ( |
| [ |
| 'white', |
| 'amplitude', |
| ], |
| { |
| "default": 'amplitude' |
| }), |
| },} |
|
|
| CATEGORY = "KJNodes/audio" |
| RETURN_TYPES = ("MASK",) |
| FUNCTION = "convert" |
| DESCRIPTION = """ |
| Works as a bridge to the AudioScheduler -nodes: |
| https://github.com/a1lazydog/ComfyUI-AudioScheduler |
| Creates masks based on the normalized amplitude. |
| """ |
|
|
| def convert(self, normalized_amp, width, height, frame_offset, shape, location_x, location_y, size, color): |
| |
| normalized_amp = np.clip(normalized_amp, 0.0, 1.0) |
|
|
| |
| normalized_amp = np.roll(normalized_amp, frame_offset) |
| |
| |
| out = [] |
| |
| for amp in normalized_amp: |
| |
| if color == 'amplitude': |
| grayscale_value = int(amp * 255) |
| elif color == 'white': |
| grayscale_value = 255 |
| |
| gray_color = (grayscale_value, grayscale_value, grayscale_value) |
| finalsize = size * amp |
| |
| if shape == 'none': |
| shapeimage = Image.new("RGB", (width, height), gray_color) |
| else: |
| shapeimage = Image.new("RGB", (width, height), "black") |
|
|
| draw = ImageDraw.Draw(shapeimage) |
| if shape == 'circle' or shape == 'square': |
| |
| left_up_point = (location_x - finalsize, location_y - finalsize) |
| right_down_point = (location_x + finalsize,location_y + finalsize) |
| two_points = [left_up_point, right_down_point] |
|
|
| if shape == 'circle': |
| draw.ellipse(two_points, fill=gray_color) |
| elif shape == 'square': |
| draw.rectangle(two_points, fill=gray_color) |
| |
| elif shape == 'triangle': |
| |
| left_up_point = (location_x - finalsize, location_y + finalsize) |
| right_down_point = (location_x + finalsize, location_y + finalsize) |
| top_point = (location_x, location_y) |
| draw.polygon([top_point, left_up_point, right_down_point], fill=gray_color) |
| |
| shapeimage = pil2tensor(shapeimage) |
| mask = shapeimage[:, :, :, 0] |
| out.append(mask) |
| |
| return (torch.cat(out, dim=0),) |
| |
| class NormalizedAmplitudeToFloatList: |
| @classmethod |
| def INPUT_TYPES(s): |
| return {"required": { |
| "normalized_amp": ("NORMALIZED_AMPLITUDE",), |
| },} |
|
|
| CATEGORY = "KJNodes/audio" |
| RETURN_TYPES = ("FLOAT",) |
| FUNCTION = "convert" |
| DESCRIPTION = """ |
| Works as a bridge to the AudioScheduler -nodes: |
| https://github.com/a1lazydog/ComfyUI-AudioScheduler |
| Creates a list of floats from the normalized amplitude. |
| """ |
|
|
| def convert(self, normalized_amp): |
| |
| normalized_amp = np.clip(normalized_amp, 0.0, 1.0) |
| return (normalized_amp.tolist(),) |
|
|
| class OffsetMaskByNormalizedAmplitude: |
| @classmethod |
| def INPUT_TYPES(s): |
| return { |
| "required": { |
| "normalized_amp": ("NORMALIZED_AMPLITUDE",), |
| "mask": ("MASK",), |
| "x": ("INT", { "default": 0, "min": -4096, "max": MAX_RESOLUTION, "step": 1, "display": "number" }), |
| "y": ("INT", { "default": 0, "min": -4096, "max": MAX_RESOLUTION, "step": 1, "display": "number" }), |
| "rotate": ("BOOLEAN", { "default": False }), |
| "angle_multiplier": ("FLOAT", { "default": 0.0, "min": -1.0, "max": 1.0, "step": 0.001, "display": "number" }), |
| } |
| } |
|
|
| RETURN_TYPES = ("MASK",) |
| RETURN_NAMES = ("mask",) |
| FUNCTION = "offset" |
| CATEGORY = "KJNodes/audio" |
| DESCRIPTION = """ |
| Works as a bridge to the AudioScheduler -nodes: |
| https://github.com/a1lazydog/ComfyUI-AudioScheduler |
| Offsets masks based on the normalized amplitude. |
| """ |
|
|
| def offset(self, mask, x, y, angle_multiplier, rotate, normalized_amp): |
|
|
| |
| offsetmask = mask.clone() |
| normalized_amp = np.clip(normalized_amp, 0.0, 1.0) |
| |
| batch_size, height, width = mask.shape |
|
|
| if rotate: |
| for i in range(batch_size): |
| rotation_amp = int(normalized_amp[i] * (360 * angle_multiplier)) |
| rotation_angle = rotation_amp |
| offsetmask[i] = TF.rotate(offsetmask[i].unsqueeze(0), rotation_angle).squeeze(0) |
| if x != 0 or y != 0: |
| for i in range(batch_size): |
| offset_amp = normalized_amp[i] * 10 |
| shift_x = min(x*offset_amp, width-1) |
| shift_y = min(y*offset_amp, height-1) |
| if shift_x != 0: |
| offsetmask[i] = torch.roll(offsetmask[i], shifts=int(shift_x), dims=1) |
| if shift_y != 0: |
| offsetmask[i] = torch.roll(offsetmask[i], shifts=int(shift_y), dims=0) |
| |
| return offsetmask, |
|
|
| class ImageTransformByNormalizedAmplitude: |
| @classmethod |
| def INPUT_TYPES(s): |
| return {"required": { |
| "normalized_amp": ("NORMALIZED_AMPLITUDE",), |
| "zoom_scale": ("FLOAT", { "default": 0.0, "min": -1.0, "max": 1.0, "step": 0.001, "display": "number" }), |
| "x_offset": ("INT", { "default": 0, "min": (1 -MAX_RESOLUTION), "max": MAX_RESOLUTION, "step": 1, "display": "number" }), |
| "y_offset": ("INT", { "default": 0, "min": (1 -MAX_RESOLUTION), "max": MAX_RESOLUTION, "step": 1, "display": "number" }), |
| "cumulative": ("BOOLEAN", { "default": False }), |
| "image": ("IMAGE",), |
| }} |
|
|
| RETURN_TYPES = ("IMAGE",) |
| FUNCTION = "amptransform" |
| CATEGORY = "KJNodes/audio" |
| DESCRIPTION = """ |
| Works as a bridge to the AudioScheduler -nodes: |
| https://github.com/a1lazydog/ComfyUI-AudioScheduler |
| Transforms image based on the normalized amplitude. |
| """ |
|
|
| def amptransform(self, image, normalized_amp, zoom_scale, cumulative, x_offset, y_offset): |
| |
| normalized_amp = np.clip(normalized_amp, 0.0, 1.0) |
| transformed_images = [] |
|
|
| |
| prev_amp = 0.0 |
|
|
| for i in range(image.shape[0]): |
| img = image[i] |
| amp = normalized_amp[i] |
|
|
| |
| if cumulative: |
| prev_amp += amp |
| amp += prev_amp |
|
|
| |
| img = img.permute(2, 0, 1) |
| |
| |
| pil_img = TF.to_pil_image(img) |
| |
| |
| width, height = pil_img.size |
| crop_size = int(min(width, height) * (1 - amp * zoom_scale)) |
| crop_size = max(crop_size, 1) |
| |
| |
| left = (width - crop_size) // 2 |
| top = (height - crop_size) // 2 |
| right = (width + crop_size) // 2 |
| bottom = (height + crop_size) // 2 |
| |
| |
| cropped_img = TF.crop(pil_img, top, left, crop_size, crop_size) |
| resized_img = TF.resize(cropped_img, (height, width)) |
| |
| |
| tensor_img = TF.to_tensor(resized_img) |
| |
| |
| tensor_img = tensor_img.permute(1, 2, 0) |
| |
| |
| offset_amp = amp * 10 |
| shift_x = min(x_offset * offset_amp, img.shape[1] - 1) |
| shift_y = min(y_offset * offset_amp, img.shape[0] - 1) |
|
|
| |
| if shift_x != 0: |
| tensor_img = torch.roll(tensor_img, shifts=int(shift_x), dims=1) |
| if shift_y != 0: |
| tensor_img = torch.roll(tensor_img, shifts=int(shift_y), dims=0) |
|
|
| |
| transformed_images.append(tensor_img) |
| |
| |
| transformed_batch = torch.stack(transformed_images) |
| |
| return (transformed_batch,) |