diff --git a/README.md b/README.md index 56767ce..d4c699c 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,21 @@ Chromatic Abarration adds a color shift to the image. Useful for making the imag ### Add Text To Image Supply the path to a .ttf file and add text to an image input. Has options for achor placement, rotation, color and more. +### Blending +Blending node that supports various blending modes. Uses [blend-modes](https://github.com/flrs/blend_modes) under the hood. Will install the module upon installation. + +### Displacement +Displacement node that can distort an image using a supplied mask. + +### Generate Noise +Generate various noises to use in masks or blending. + +### Flatten Colors +Flatten the colors of an image to a variable amount of colors. + + + ## Citations -Pixel sort by: [satyarth](https://github.com/satyarth) \ No newline at end of file +Pixel Sort by: [satyarth](https://github.com/satyarth) +Blend Modes by: [flrs](https://github.com/flrs/) \ No newline at end of file diff --git a/custom_nodes/DT_Blending.py b/custom_nodes/DT_Blending.py new file mode 100644 index 0000000..98b2cc2 --- /dev/null +++ b/custom_nodes/DT_Blending.py @@ -0,0 +1,106 @@ +import torch +import numpy as np +from PIL import Image +import subprocess +import sys +try: + import blend_modes +except ModuleNotFoundError: + # install pixelsort in current venv + subprocess.check_call([sys.executable, "-m", "pip", "install", "blend-modes"]) + import blend_modes + +class Blend(): + + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + """ + Input Types + """ + return { + "required": { + "images_1": ("IMAGE",), + "images_2": ("IMAGE",),}, + "optional": { + "blend_mode": ([ + "soft_light", + "lighten_only", + "dodge", + "addition", + "darken_only", + "multiply", + "hard_light", + "difference", + "subtract", + "grain_extract", + "grain_merge", + "divide", + "overlay", + "normal", + ],), + "blend_opacity": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "apply_blend" + + CATEGORY = "VextraNodes" + + def tensor_to_pil(self, img): + if img is not None: + i = 255. * img.cpu().numpy().squeeze() + img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)) + return img + + def hack_alpha_channel(self, pil_image): + # Create a new image with the same size and mode as the original image and fill it with opaque white + new_image = Image.new('RGBA', pil_image.size, (255, 255, 255, 255)) + + # Paste the original image onto the new image + new_image.paste(pil_image, (0, 0)) + return new_image + + def apply_blend(self, images_1, images_2, blend_mode, blend_opacity): + #create empty tensor with the same shape as images + total_images = [] + blend_fn = getattr(blend_modes, blend_mode) + if len(images_1) > len(images_2): + raise Exception("BLEND: Second set of images cannot be less than the first set of images!") + for i, image_1 in enumerate(images_1): + image = self.tensor_to_pil(image_1) + image = self.hack_alpha_channel(image) + + image_2 = self.tensor_to_pil(images_2[i]) + image_2 = self.hack_alpha_channel(image_2) + + if image.size != image_2.size: + raise Exception("BLEND: Images must be the same size!") + + + image = np.array(image) + image = image.astype(float) + + image_2 = np.array(image_2) + image_2 = image_2.astype(float) + + out_image = blend_fn(image, image_2, blend_opacity) + out_image = Image.fromarray(out_image.astype(np.uint8)) + + # convert to tensor + out_image = np.array(out_image.convert("RGB")).astype(np.float32) / 255.0 + out_image = torch.from_numpy(out_image).unsqueeze(0) + total_images.append(out_image) + + + total_images = torch.cat(total_images, 0) + return (total_images,) + + + +NODE_CLASS_MAPPINGS = { + "Blend": Blend, +} diff --git a/custom_nodes/DT_Chromatic_Aberration.py b/custom_nodes/DT_Chromatic_Aberration.py new file mode 100644 index 0000000..2bb87b8 --- /dev/null +++ b/custom_nodes/DT_Chromatic_Aberration.py @@ -0,0 +1,236 @@ +import torch +import numpy as np +from PIL import Image +import math + +class Chromatic_Aberration(): + """ + This node provides a simple interface to apply PixelSort blur to the output image. + """ + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + """ + Input Types + """ + return { + "required": { + "images": ("IMAGE",),}, + "optional": { + "chromatic_strength": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 10.0, "step": 0.1}), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "do_chromatic" + + CATEGORY = "VextraNodes" + + def tensor_to_pil(self, img): + if img is not None: + i = 255. * img.cpu().numpy().squeeze() + img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)) + return img + + def do_chromatic(self, images, chromatic_strength=0): + #create empty tensor with the same shape as images + total_images = [] + for image in images: + image = self.tensor_to_pil(image) + image = add_chromatic(image, chromatic_strength) + # convert to tensor + out_image = np.array(image.convert("RGB")).astype(np.float32) / 255.0 + out_image = torch.from_numpy(out_image).unsqueeze(0) + total_images.append(out_image) + + + total_images = torch.cat(total_images, 0) + return (total_images,) + +def add_chromatic(im, strength: float = 0): + if strength == 0: + return im + r, g, b = im.split() + rdata = np.asarray(r) + rfinal = r + gfinal = g + bfinal = b + + # enlarge the green and blue channels slightly, blue being the most enlarged + gfinal = gfinal.resize((round((1 + 0.018 * strength) * rdata.shape[1]), + round((1 + 0.018 * strength) * rdata.shape[0])), Image.ANTIALIAS) + bfinal = bfinal.resize((round((1 + 0.044 * strength) * rdata.shape[1]), + round((1 + 0.044 * strength) * rdata.shape[0])), Image.ANTIALIAS) + + rwidth, rheight = rfinal.size + gwidth, gheight = gfinal.size + bwidth, bheight = bfinal.size + rhdiff = (bheight - rheight) // 2 + rwdiff = (bwidth - rwidth) // 2 + ghdiff = (bheight - gheight) // 2 + gwdiff = (bwidth - gwidth) // 2 + + # Centre the channels + im = Image.merge("RGB", ( + rfinal.crop((-rwdiff, -rhdiff, bwidth - rwdiff, bheight - rhdiff)), + gfinal.crop((-gwdiff, -ghdiff, bwidth - gwdiff, bheight - ghdiff)), + bfinal)) + + # Crop the image to the original image dimensions + return im.crop((rwdiff, rhdiff, rwidth + rwdiff, rheight + rhdiff)) + + + +NODE_CLASS_MAPPINGS = { + "Chromatic Aberration": Chromatic_Aberration +} + + +def cartesian_to_polar(data: np.ndarray) -> np.ndarray: + """Returns the polar form of + """ + width = data.shape[1] + height = data.shape[0] + assert (width > 2) + assert (height > 2) + assert (width % 2 == 1) + assert (height % 2 == 1) + perimeter = 2 * (width + height - 2) + halfdiag = math.ceil(((width ** 2 + height ** 2) ** 0.5) / 2) + halfw = width // 2 + halfh = height // 2 + ret = np.zeros((halfdiag, perimeter, 3)) + + # Don't want to deal with divide by zero errors... + ret[0:(halfw + 1), halfh] = data[halfh, halfw::-1] + ret[0:(halfw + 1), height + width - 2 + + halfh] = data[halfh, halfw:(halfw * 2 + 1)] + ret[0:(halfh + 1), height - 1 + halfw] = data[halfh:(halfh * 2 + 1), halfw] + ret[0:(halfh + 1), perimeter - halfw] = data[halfh::-1, halfw] + + # Divide the image into 8 triangles, and use the same calculation on + # 4 triangles at a time. This is possible due to symmetry. + # This section is also responsible for the corner pixels + for i in range(0, halfh): + slope = (halfh - i) / (halfw) + diagx = ((halfdiag ** 2) / (slope ** 2 + 1)) ** 0.5 + unit_xstep = diagx / (halfdiag - 1) + unit_ystep = diagx * slope / (halfdiag - 1) + for row in range(halfdiag): + ystep = round(row * unit_ystep) + xstep = round(row * unit_xstep) + if ((halfh >= ystep) and halfw >= xstep): + ret[row, i] = data[halfh - ystep, halfw - xstep] + ret[row, height - 1 - i] = data[halfh + ystep, halfw - xstep] + ret[row, height + width - 2 + + i] = data[halfh + ystep, halfw + xstep] + ret[row, height + width + height - 3 - + i] = data[halfh - ystep, halfw + xstep] + else: + break + + # Remaining 4 triangles + for j in range(1, halfw): + slope = (halfh) / (halfw - j) + diagx = ((halfdiag ** 2) / (slope ** 2 + 1)) ** 0.5 + unit_xstep = diagx / (halfdiag - 1) + unit_ystep = diagx * slope / (halfdiag - 1) + for row in range(halfdiag): + ystep = round(row * unit_ystep) + xstep = round(row * unit_xstep) + if (halfw >= xstep and halfh >= ystep): + ret[row, height - 1 + j] = data[halfh + ystep, halfw - xstep] + ret[row, height + width - 2 - + j] = data[halfh + ystep, halfw + xstep] + ret[row, height + width + height - 3 + + j] = data[halfh - ystep, halfw + xstep] + ret[row, perimeter - j] = data[halfh - ystep, halfw - xstep] + else: + break + return ret + + +def polar_to_cartesian(data: np.ndarray, width: int, height: int) -> np.ndarray: + """Returns the cartesian form of . + + is the original width of the cartesian image + is the original height of the cartesian image + """ + assert (width > 2) + assert (height > 2) + assert (width % 2 == 1) + assert (height % 2 == 1) + perimeter = 2 * (width + height - 2) + halfdiag = math.ceil(((width ** 2 + height ** 2) ** 0.5) / 2) + halfw = width // 2 + halfh = height // 2 + ret = np.zeros((height, width, 3)) + + def div0(): + # Don't want to deal with divide by zero errors... + ret[halfh, halfw::-1] = data[0:(halfw + 1), halfh] + ret[halfh, halfw:(halfw * 2 + 1)] = data[0:(halfw + 1), + height + width - 2 + halfh] + ret[halfh:(halfh * 2 + 1), halfw] = data[0:(halfh + 1), height - 1 + halfw] + ret[halfh::-1, halfw] = data[0:(halfh + 1), perimeter - halfw] + + div0() + + # Same code as above, except the order of the assignments are switched + # Code blocks are split up for easier profiling + def part1(): + for i in range(0, halfh): + slope = (halfh - i) / (halfw) + diagx = ((halfdiag ** 2) / (slope ** 2 + 1)) ** 0.5 + unit_xstep = diagx / (halfdiag - 1) + unit_ystep = diagx * slope / (halfdiag - 1) + for row in range(halfdiag): + ystep = round(row * unit_ystep) + xstep = round(row * unit_xstep) + if ((halfh >= ystep) and halfw >= xstep): + ret[halfh - ystep, halfw - xstep] = \ + data[row, i] + ret[halfh + ystep, halfw - xstep] = \ + data[row, height - 1 - i] + ret[halfh + ystep, halfw + xstep] = \ + data[row, height + width - 2 + i] + ret[halfh - ystep, halfw + xstep] = \ + data[row, height + width + height - 3 - i] + else: + break + + part1() + + def part2(): + for j in range(1, halfw): + slope = (halfh) / (halfw - j) + diagx = ((halfdiag ** 2) / (slope ** 2 + 1)) ** 0.5 + unit_xstep = diagx / (halfdiag - 1) + unit_ystep = diagx * slope / (halfdiag - 1) + for row in range(halfdiag): + ystep = round(row * unit_ystep) + xstep = round(row * unit_xstep) + if (halfw >= xstep and halfh >= ystep): + ret[halfh + ystep, halfw - xstep] = \ + data[row, height - 1 + j] + ret[halfh + ystep, halfw + xstep] = \ + data[row, height + width - 2 - j] + ret[halfh - ystep, halfw + xstep] = \ + data[row, height + width + height - 3 + j] + ret[halfh - ystep, halfw - xstep] = \ + data[row, perimeter - j] + else: + break + + part2() + + # Repairs black/missing pixels in the transformed image + def set_zeros(): + zero_mask = ret[1:-1, 1:-1] == 0 + ret[1:-1, 1:-1] = np.where(zero_mask, (ret[:-2, 1:-1] + ret[2:, 1:-1]) / 2, ret[1:-1, 1:-1]) + + set_zeros() + + return ret \ No newline at end of file diff --git a/custom_nodes/DT_Displacement.py b/custom_nodes/DT_Displacement.py new file mode 100644 index 0000000..f5469a1 --- /dev/null +++ b/custom_nodes/DT_Displacement.py @@ -0,0 +1,96 @@ +import torch +import numpy as np +from PIL import Image + +class Displacement_Map(): + """ + This node provides a simple interface to apply PixelSort blur to the output image. + """ + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + """ + Input Types + """ + return { + "required": { + "images": ("IMAGE",), + "displacement_maps": ("IMAGE",),}, + "optional": { + "scale": ("FLOAT", {"default": 5.0, "min": 1.0, "max": 500.0, "step": 0.1}), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "do_displace" + + CATEGORY = "VextraNodes" + + def tensor_to_pil(self, img): + if img is not None: + i = 255. * img.cpu().numpy().squeeze() + img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)) + return img + + def do_displace(self, images, displacement_maps, scale): + #create empty tensor with the same shape as images + total_images = [] + + if len(images) > len(displacement_maps): + raise Exception("Number of images must be equal or less than the number of displacement maps!") + + for i, image in enumerate(images): + displacement_map = displacement_maps[i] + + displacement_map = self.tensor_to_pil(displacement_map) + image = self.tensor_to_pil(image) + + if displacement_map.size != image.size: + raise Exception("Displacement map and image must be the same size!") + + image = apply_displacement_map(image, displacement_map, scale) + + + # convert to tensor + out_image = np.array(image.convert("RGB")).astype(np.float32) / 255.0 + out_image = torch.from_numpy(out_image).unsqueeze(0) + total_images.append(out_image) + + + total_images = torch.cat(total_images, 0) + return (total_images,) + + + +NODE_CLASS_MAPPINGS = { + "Displacement Map": Displacement_Map +} + +def apply_displacement_map(image, displacement_map, scale): + # Convert PIL images to NumPy arrays + image_array = np.array(image) + displacement_map_array = np.array(displacement_map) + + # Get the dimensions of the image + height, width, _ = image_array.shape + + # Calculate the displacement offsets based on the scale factor + displacement_offsets = (displacement_map_array / 255 - 0.5) * scale + + # Create arrays for the X and Y coordinates of the pixels + x_coords, y_coords = np.meshgrid(np.arange(width), np.arange(height)) + + # Apply the displacement offsets to the X and Y coordinates + x_displaced = (x_coords + displacement_offsets[..., 0]).clip(0, width - 1).astype(int) + y_displaced = (y_coords + displacement_offsets[..., 1]).clip(0, height - 1).astype(int) + + # Create a new array with the same shape as the original image and copy the displaced pixels + displaced_image_array = np.zeros_like(image_array) + displaced_image_array[y_coords, x_coords] = image_array[y_displaced, x_displaced] + + # Convert the displaced image array back to a PIL image + displaced_image = Image.fromarray(displaced_image_array) + + return displaced_image \ No newline at end of file diff --git a/custom_nodes/DT_Flatten_Colors.py b/custom_nodes/DT_Flatten_Colors.py new file mode 100644 index 0000000..b9e1901 --- /dev/null +++ b/custom_nodes/DT_Flatten_Colors.py @@ -0,0 +1,54 @@ +import torch +import numpy as np +from PIL import Image +class Flatten_Colors(): + """ + This node provides a simple interface to apply PixelSort blur to the output image. + """ + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + """ + Input Types + """ + return { + "required": { + "images": ("IMAGE",),}, + "optional": { + "number_of_colors": ("INT", {"default": 5, "min": 1, "max": 4000, "step": 1}), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "flatten" + + CATEGORY = "VextraNodes" + + def tensor_to_pil(self, img): + if img is not None: + i = 255. * img.cpu().numpy().squeeze() + img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)) + return img + + def flatten(self, images, number_of_colors): + #create empty tensor with the same shape as images + total_images = [] + for image in images: + image = self.tensor_to_pil(image) + image = image.convert('P', palette=Image.ADAPTIVE, colors=number_of_colors) + + # convert to tensor + out_image = np.array(image.convert("RGB")).astype(np.float32) / 255.0 + out_image = torch.from_numpy(out_image).unsqueeze(0) + total_images.append(out_image) + + + total_images = torch.cat(total_images, 0) + return (total_images,) + + +NODE_CLASS_MAPPINGS = { + "Flatten Colors": Flatten_Colors +} \ No newline at end of file diff --git a/custom_nodes/DT_GenerateNoise.py b/custom_nodes/DT_GenerateNoise.py new file mode 100644 index 0000000..61af2eb --- /dev/null +++ b/custom_nodes/DT_GenerateNoise.py @@ -0,0 +1,70 @@ +import torch +import numpy as np +from PIL import Image + +def create_noise(mode='gaussian', scale=0.1, width=512, height=512): + # Create empty image + noise = np.zeros((height, width, 3), dtype=np.float32) + + if mode == 'gaussian': + noise = np.random.normal(0, scale * 255, noise.shape).astype(np.float32) + elif mode == 'uniform': + noise = np.random.uniform(-scale * 255, scale * 255, noise.shape).astype(np.float32) + elif mode == 'salt_and_pepper': + salt = np.random.rand(*noise.shape[:2]) < scale / 2 + pepper = np.random.rand(*noise.shape[:2]) < scale / 2 + noise[..., 0] = np.where(salt, 255, 0) + noise[..., 1] = np.where(pepper, 255, 0) + noise[..., 2] = np.where(np.logical_not(salt | pepper), 255, 0) + else: + print(f'Unknown noise mode: {mode}') + + return Image.fromarray(noise.astype(np.uint8), 'RGB') + + +class NoiseImage(): + """ + This node provides a simple interface to apply PixelSort blur to the output image. + """ + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + """ + Input Types + """ + return { + "required": { + "mode": (['gaussian', 'uniform', 'salt_and_pepper'],), + "noise_scale": ("FLOAT", {"default": 1, "min": 0.0, "max": 100.0, "step": 0.01}), + "width": ("INT", {"default": 512, "min": 1, "max": 10000, "step": 1}), + "height": ("INT", {"default": 512, "min": 1, "max": 10000, "step": 1}), + "batch_size": ("INT", {"default": 1, "min": 1, "max": 10000, "step": 1}), + }, + "optional": { + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "do_noise" + + CATEGORY = "VextraNodes" + + def do_noise(self, mode, noise_scale, width, height, batch_size): + #create empty tensor with the same shape as images + total_images = [] + for i in range(batch_size): + image = create_noise(mode, noise_scale, width, height) + # convert to tensor + out_image = np.array(image.convert("RGB")).astype(np.float32) / 255.0 + out_image = torch.from_numpy(out_image).unsqueeze(0) + total_images.append(out_image) + + + total_images = torch.cat(total_images, 0) + return (total_images,) + +NODE_CLASS_MAPPINGS = { + "Generate Noise Image": NoiseImage +} diff --git a/custom_nodes/DT_Hue_Rotation.py b/custom_nodes/DT_Hue_Rotation.py new file mode 100644 index 0000000..62087d8 --- /dev/null +++ b/custom_nodes/DT_Hue_Rotation.py @@ -0,0 +1,83 @@ +import torch +import numpy as np +from PIL import Image +import math + +def or_convert(im, mode): + return im if im.mode == mode else im.convert(mode) + +def hue_rotate(im, deg=0): + cos_hue = math.cos(math.radians(deg)) + sin_hue = math.sin(math.radians(deg)) + + matrix = [ + .213 + cos_hue * .787 - sin_hue * .213, + .715 - cos_hue * .715 - sin_hue * .715, + .072 - cos_hue * .072 + sin_hue * .928, + 0, + .213 - cos_hue * .213 + sin_hue * .143, + .715 + cos_hue * .285 + sin_hue * .140, + .072 - cos_hue * .072 - sin_hue * .283, + 0, + .213 - cos_hue * .213 - sin_hue * .787, + .715 - cos_hue * .715 + sin_hue * .715, + .072 + cos_hue * .928 + sin_hue * .072, + 0, + ] + + rotated = or_convert(im, 'RGB').convert('RGB', matrix) + return or_convert(rotated, im.mode) + + +class HueRotation(): + + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + """ + Input Types + """ + return { + "required": { + "images": ("IMAGE",),}, + "optional": { + "hue_rotation": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 360.0, "step": 0.1}), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "apply_hr" + + CATEGORY = "VextraNodes" + + def tensor_to_pil(self, img): + if img is not None: + i = 255. * img.cpu().numpy().squeeze() + img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)) + return img + + def apply_hr(self, images, hue_rotation): + #create empty tensor with the same shape as images + total_images = [] + for image in images: + image = self.tensor_to_pil(image) + image = hue_rotate(image, hue_rotation) + # convert to tensor + out_image = np.array(image.convert("RGB")).astype(np.float32) / 255.0 + out_image = torch.from_numpy(out_image).unsqueeze(0) + total_images.append(out_image) + + + total_images = torch.cat(total_images, 0) + return (total_images,) + + + + + + +NODE_CLASS_MAPPINGS = { + "Hue Rotation": HueRotation, +} diff --git a/custom_nodes/DT_PILGram.py b/custom_nodes/DT_PILGram.py new file mode 100644 index 0000000..d67d27b --- /dev/null +++ b/custom_nodes/DT_PILGram.py @@ -0,0 +1,88 @@ +import torch +import numpy as np +from PIL import Image +import subprocess +import sys +try: + import pilgram +except ModuleNotFoundError: + # install pixelsort in current venv + subprocess.check_call([sys.executable, "-m", "pip", "install", "pilgram"]) + import pilgram + +class ApplyFilter(): + + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + """ + Input Types + """ + return { + "required": { + "images": ("IMAGE",),}, + "optional": { + "instagram_filter": ([ + "_1977", + "aden", + "brannan", + "brooklyn", + "clarendon", + "earlybird", + "gingham", + "hudson", + "inkwell", + "kelvin", + "lark", + "lofi", + "maven", + "mayfair", + "moon", + "nashville", + "perpetua", + "reyes", + "rise", + "slumber", + "stinson", + "toaster", + "valencia", + "walden", + "willow", + "xpro2", + ],), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "apply_filter" + + CATEGORY = "VextraNodes" + + def tensor_to_pil(self, img): + if img is not None: + i = 255. * img.cpu().numpy().squeeze() + img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)) + return img + + def apply_filter(self, images, instagram_filter): + #create empty tensor with the same shape as images + total_images = [] + filter_fn = getattr(pilgram, instagram_filter) + for image in images: + image = self.tensor_to_pil(image) + image = filter_fn(image) + + # convert to tensor + out_image = np.array(image.convert("RGB")).astype(np.float32) / 255.0 + out_image = torch.from_numpy(out_image).unsqueeze(0) + total_images.append(out_image) + + + total_images = torch.cat(total_images, 0) + return (total_images,) + +NODE_CLASS_MAPPINGS = { + "Apply Instagram Filter": ApplyFilter, +} diff --git a/custom_nodes/DT_Swap_Color_Mode.py b/custom_nodes/DT_Swap_Color_Mode.py index 8da7b5f..e78a17d 100644 --- a/custom_nodes/DT_Swap_Color_Mode.py +++ b/custom_nodes/DT_Swap_Color_Mode.py @@ -46,7 +46,6 @@ class Swap_Color_Mode(): return img def do_swap(self, images, color_mode='default'): - #create empty tensor with the same shape as images total_images = [] for image in images: image = self.tensor_to_pil(image)