init
This commit is contained in:
@@ -1,2 +1,29 @@
|
||||
# ComfyUI Vextra Nodes
|
||||
Custom nodes for ComfyUI
|
||||
Custom nodes for [ComfyUI](https://github.com/comfyanonymous/ComfyUI).
|
||||
|
||||
## Installation
|
||||
1. Install [ComfyUI](https://github.com/comfyanonymous/ComfyUI)
|
||||
2. Download nodes and place them in custom_nodes folder inside your ComfyUI installation.
|
||||
3. Start/Restart ComfyUI
|
||||
|
||||
## Nodes
|
||||
|
||||
### Pixel Sort
|
||||
Pixel sorting effect by [satyarth](https://github.com/satyarth/pixelsort). Will install pixelsort module upon installation.
|
||||
Read about pixel sorting [here](http://satyarth.me/articles/pixel-sorting/).
|
||||
|
||||
### Swap Color Mode (Black & White)
|
||||
Swap the color mode to luminescence or single channel, useful for making mask or simply making stuff black & white.
|
||||
|
||||
### Solid Color
|
||||
Generates an empty solid color image with options for color, size and batch size.
|
||||
|
||||
### Chromatic Aberration
|
||||
Chromatic Abarration adds a color shift to the image. Useful for making the image look more "analog".
|
||||
|
||||
### 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.
|
||||
|
||||
|
||||
## Citations
|
||||
Pixel sort by: [satyarth](https://github.com/satyarth)
|
||||
@@ -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 <data>
|
||||
"""
|
||||
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 <data>.
|
||||
|
||||
<width> is the original width of the cartesian image
|
||||
<height> 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
|
||||
@@ -0,0 +1,94 @@
|
||||
import torch
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
from PIL import ImageDraw
|
||||
from PIL import ImageFont
|
||||
|
||||
class FontText():
|
||||
"""
|
||||
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": {
|
||||
"font_ttf": ("STRING", {"default": 'C:/Windows/Fonts/arial.ttf'}),
|
||||
"size": ("INT", {"default": 50, "min": 2, "max": 1000, "step": 1}),
|
||||
"x": ("INT", {"default": 50, "min": 2, "max": 10000, "step": 1}),
|
||||
"y": ("INT", {"default": 50, "min": 2, "max": 10000, "step": 1}),
|
||||
"text": ("STRING", {"default": "Hello World"}),
|
||||
"color": ("STRING", {"default": 'rgb(255, 255, 255)'}),
|
||||
"anchor": (["Bottom Left Corner", "Center"],),
|
||||
"rotate": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 360.0, "step": 0.1}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
FUNCTION = "do_font"
|
||||
|
||||
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_font(self, images, font_ttf, size, x, y, text, color, anchor, rotate):
|
||||
#create empty tensor with the same shape as images
|
||||
total_images = []
|
||||
center_anchor = True if anchor == 'Center' else False
|
||||
if color.startswith('#'):
|
||||
color_rgb = tuple(int(color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4))
|
||||
else:
|
||||
color_rgb = tuple(map(int, color.strip('rgb()').split(',')))
|
||||
for image in images:
|
||||
image = self.tensor_to_pil(image)
|
||||
|
||||
add_text_to_image(image, font_ttf, size, x, y, text, color_rgb, center_anchor, rotate)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 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 = {
|
||||
"Add Text To Image": FontText
|
||||
}
|
||||
|
||||
def add_text_to_image(img, font_ttf, size, x, y, text, color_rgb, center=False, rotate=0):
|
||||
draw = ImageDraw.Draw(img)
|
||||
myFont = ImageFont.truetype(font_ttf, size)
|
||||
text_width, text_height = draw.textsize(text, font=myFont)
|
||||
|
||||
if center:
|
||||
x -= text_width // 2
|
||||
y -= text_height // 2
|
||||
|
||||
if rotate != 0:
|
||||
text_img = Image.new('RGBA', img.size, (255, 255, 255, 0))
|
||||
text_draw = ImageDraw.Draw(text_img)
|
||||
text_draw.text((x, y), text, font=myFont, fill=color_rgb)
|
||||
text_img = text_img.rotate(rotate, resample=Image.BICUBIC, expand=True)
|
||||
img.paste(text_img, (0, 0), text_img)
|
||||
else:
|
||||
draw.text((x, y), text, font=myFont, fill=color_rgb)
|
||||
|
||||
return img
|
||||
@@ -0,0 +1,83 @@
|
||||
import torch
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
import sys
|
||||
import subprocess
|
||||
try:
|
||||
import pixelsort
|
||||
except ModuleNotFoundError:
|
||||
# install pixelsort in current venv
|
||||
subprocess.check_call([sys.executable, "-m", "pip", "install", "pixelsort"])
|
||||
import pixelsort
|
||||
|
||||
|
||||
class Pixel_Sort:
|
||||
"""
|
||||
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": {
|
||||
"character_length": ("INT", {"default": 50, "min": 2, "max": 1000, "step": 1}),
|
||||
"randomness": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 100.0, "step": 0.1}),
|
||||
"sorting_function": (["lightness", "hue", "saturation", "intensity", "minimum"],),
|
||||
"interval_function": (["threshold", "random", 'edges', 'waves', 'file', 'file-edges', 'none'],),
|
||||
"lower_threshold": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"upper_threshold": ("FLOAT", {"default": 0.8, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"angle": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 360.0, "step": 0.1}),
|
||||
"mask_image": ("IMAGE", {"default": None}),
|
||||
"interval_image": ("IMAGE", {"default": None}),
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
FUNCTION = "do_sort"
|
||||
|
||||
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_sort(self, images, character_length, randomness, sorting_function, interval_function, lower_threshold, upper_threshold, angle, mask_image=None, interval_image=None, color_mode='default'):
|
||||
#create empty tensor with the same shape as images
|
||||
total_images = []
|
||||
for image in images:
|
||||
image = self.tensor_to_pil(image)
|
||||
mask_image = self.tensor_to_pil(mask_image)
|
||||
interval_image = self.tensor_to_pil(interval_image)
|
||||
out_image = pixelsort.pixelsort(
|
||||
image=image,
|
||||
mask_image=mask_image,
|
||||
interval_image=interval_image,
|
||||
randomness=randomness,
|
||||
clength=character_length,
|
||||
sorting_function=sorting_function,
|
||||
interval_function=interval_function,
|
||||
lower_threshold=lower_threshold,
|
||||
upper_threshold=upper_threshold,
|
||||
angle=angle)
|
||||
# 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 = {
|
||||
"Pixel Sort": Pixel_Sort
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import torch
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
from PIL import ImageDraw
|
||||
from PIL import ImageFont
|
||||
|
||||
class SolidColorImage():
|
||||
"""
|
||||
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": {
|
||||
"width": ("INT", {"default": 512, "min": 64, "max": 10000, "step": 64}),
|
||||
"height": ("INT", {"default": 512, "min": 64, "max": 10000, "step": 64}),
|
||||
"color": ("STRING", {"default": 'rgb(255, 255, 255)'}),
|
||||
"batch_size": ("INT", {"default": 1, "min": 1, "max": 64, "step": 1}),
|
||||
},
|
||||
"optional": {
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
FUNCTION = "do_solid"
|
||||
|
||||
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_solid(self, width, height, color, batch_size):
|
||||
#create empty tensor with the same shape as images
|
||||
total_images = []
|
||||
if color.startswith('#'):
|
||||
color_rgb = tuple(int(color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4))
|
||||
else:
|
||||
color_rgb = tuple(map(int, color.strip('rgb()').split(',')))
|
||||
for i in range(batch_size):
|
||||
image = Image.new('RGB', (width, height), color_rgb)
|
||||
# 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 = {
|
||||
"Create Solid Color": SolidColorImage
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import torch
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
COLOR_MODES = {
|
||||
'RGB': 'RGB',
|
||||
'RGBA': 'RGBA',
|
||||
'luminance': 'L',
|
||||
'luminance_alpha': 'LA',
|
||||
'cmyk': 'CMYK',
|
||||
'ycbcr': 'YCbCr',
|
||||
'lab': 'LAB',
|
||||
'hsv': 'HSV',
|
||||
'single_channel': '1',
|
||||
}
|
||||
|
||||
class Swap_Color_Mode():
|
||||
"""
|
||||
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": {
|
||||
"color_mode": (['default', 'luminance', 'single_channel'],),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
FUNCTION = "do_swap"
|
||||
|
||||
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_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)
|
||||
if color_mode != 'default':
|
||||
correct_color_mode = COLOR_MODES[color_mode]
|
||||
image = image.convert(correct_color_mode)
|
||||
# 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 = {
|
||||
"Swap Color Mode": Swap_Color_Mode
|
||||
}
|
||||
Reference in New Issue
Block a user