From 558693fc8fbea6cf160e30dc075637e79835964d Mon Sep 17 00:00:00 2001 From: "Alexander G. Morano" Date: Sun, 4 May 2025 01:20:54 -0400 Subject: [PATCH] cleaned up defaults vector nodes aligned for list output vars complete --- core/calc.py | 34 ++++++------ core/color.py | 39 ++++++++------ core/compose.py | 57 ++++++++++---------- core/create.py | 48 +++++++++-------- core/trans.py | 46 +++++++++-------- core/utility/batch.py | 48 ++++++++--------- core/utility/info.py | 6 +-- core/utility/io.py | 36 +++++++------ core/vars.py | 117 ++++++++++++++++++++++++++---------------- sup/image/adjust.py | 14 +++-- sup/image/compose.py | 20 ++++---- web/util.js | 11 +--- 12 files changed, 262 insertions(+), 214 deletions(-) diff --git a/core/calc.py b/core/calc.py index 13aaff5..cf68989 100644 --- a/core/calc.py +++ b/core/calc.py @@ -240,7 +240,7 @@ IMAGE and MASK will return a TRUE bit for any non-black pixel, as a stream of bi return d def run(self, **kw) -> tuple[List[int], List[bool]]: - value = parse_param(kw, "VALUE", EnumConvertType.ANY, [0]) + value = parse_param(kw, "VALUE", EnumConvertType.ANY, 0) bits = parse_param(kw, "BITS", EnumConvertType.INT, 8, 1, 64) msb = parse_param(kw, "MSB", EnumConvertType.INT, False) params = list(zip_longest_fill(value, bits)) @@ -312,11 +312,11 @@ Evaluates two inputs (A and B) with a specified comparison operators and optiona return d def run(self, **kw) -> tuple[Any, Any]: - A = parse_param(kw, "A", EnumConvertType.ANY, [0]) - B = parse_param(kw, "B", EnumConvertType.ANY, [0]) + A = parse_param(kw, "A", EnumConvertType.ANY, 0) + B = parse_param(kw, "B", EnumConvertType.ANY, 0) size = max(len(A), len(B)) - good = parse_param(kw, "PASS", EnumConvertType.ANY, [0])[:size] - fail = parse_param(kw, "FAIL", EnumConvertType.ANY, [0])[:size] + good = parse_param(kw, "PASS", EnumConvertType.ANY, 0)[:size] + fail = parse_param(kw, "FAIL", EnumConvertType.ANY, 0)[:size] op = parse_param(kw, "COMPARE", EnumComparison, EnumComparison.EQUAL.name)[:size] flip = parse_param(kw, "FLIP", EnumConvertType.BOOLEAN, False)[:size] invert = parse_param(kw, "INVERT", EnumConvertType.BOOLEAN, False)[:size] @@ -453,11 +453,11 @@ Additionally, you can specify the easing function (EASE) and the desired output return d def run(self, **kw) -> tuple[Any, Any]: - A = parse_param(kw, "A", EnumConvertType.ANY, [0]) - B = parse_param(kw, "B", EnumConvertType.ANY, [0]) - a_xyzw = parse_param(kw, "AA", EnumConvertType.VEC4, [(0, 0, 0, 0)]) - b_xyzw = parse_param(kw, "BB", EnumConvertType.VEC4, [(1, 1, 1, 1)]) - alpha = parse_param(kw, "FLOAT",EnumConvertType.VEC4, [(0.5,0.5,0.5,0.5)], 0, 1) + A = parse_param(kw, "A", EnumConvertType.ANY, 0) + B = parse_param(kw, "B", EnumConvertType.ANY, 0) + a_xyzw = parse_param(kw, "AA", EnumConvertType.VEC4, (0, 0, 0, 0)) + b_xyzw = parse_param(kw, "BB", EnumConvertType.VEC4, (1, 1, 1, 1)) + alpha = parse_param(kw, "FLOAT",EnumConvertType.VEC4, (0.5,0.5,0.5,0.5), 0, 1) op = parse_param(kw, "EASE", EnumEase, EnumEase.SIN_IN_OUT.name) typ = parse_param(kw, "TYPE", EnumNumberType, EnumNumberType.FLOAT.name) values = [] @@ -533,7 +533,7 @@ Perform single function operations like absolute value, mean, median, mode, magn def run(self, **kw) -> tuple[bool]: results = [] - A = parse_param(kw, "A", EnumConvertType.ANY, [0]) + A = parse_param(kw, "A", EnumConvertType.ANY, 0) op = parse_param(kw, "FUNCTION", EnumUnaryOperation, EnumUnaryOperation.ABS.name) params = list(zip_longest_fill(A, op)) pbar = ProgressBar(len(params)) @@ -658,8 +658,8 @@ Execute binary operations like addition, subtraction, multiplication, division, results = [] A = parse_param(kw, "A", EnumConvertType.ANY, None) B = parse_param(kw, "B", EnumConvertType.ANY, None) - a_xyzw = parse_param(kw, "AA", EnumConvertType.VEC4, [(0, 0, 0, 0)]) - b_xyzw = parse_param(kw, "BB", EnumConvertType.VEC4, [(0, 0, 0, 0)]) + a_xyzw = parse_param(kw, "AA", EnumConvertType.VEC4, (0, 0, 0, 0)) + b_xyzw = parse_param(kw, "BB", EnumConvertType.VEC4, (0, 0, 0, 0)) op = parse_param(kw, "FUNCTION", EnumBinaryOperation, EnumBinaryOperation.ADD.name) typ = parse_param(kw, "TYPE", EnumConvertType, EnumConvertType.FLOAT.name) flip = parse_param(kw, "FLIP", EnumConvertType.BOOLEAN, False) @@ -796,7 +796,7 @@ Manipulate strings through filtering def run(self, **kw) -> tuple[TensorType, ...]: # turn any all inputs into the - data_list = parse_dynamic(kw, "❔", EnumConvertType.ANY, [""]) + data_list = parse_dynamic(kw, "❔", EnumConvertType.ANY, "") if data_list is None: logger.warn("no data for list") return ([],) @@ -806,7 +806,7 @@ Manipulate strings through filtering op = parse_param(kw, "FUNCTION", EnumConvertString, EnumConvertString.SPLIT.name)[0] key = parse_param(kw, "KEY", EnumConvertType.STRING, "")[0] replace = parse_param(kw, "REPLACE", EnumConvertType.STRING, "")[0] - stenst = parse_param(kw, "RANGE", EnumConvertType.VEC3INT, [(0, -1, 1)])[0] + stenst = parse_param(kw, "RANGE", EnumConvertType.VEC3INT, (0, -1, 1))[0] results = [] match op: case EnumConvertString.SPLIT: @@ -881,8 +881,8 @@ Swap components between two vectors based on specified swizzle patterns and valu return d def run(self, **kw) -> tuple[TensorType, ...]: - pA = parse_param(kw, "A", EnumConvertType.VEC4, [(0,0,0,0)]) - pB = parse_param(kw, "B", EnumConvertType.VEC4, [(0,0,0,0)]) + pA = parse_param(kw, "A", EnumConvertType.VEC4, (0,0,0,0)) + pB = parse_param(kw, "B", EnumConvertType.VEC4, (0,0,0,0)) swap_x = parse_param(kw, "SWAP_X", EnumSwizzle, EnumSwizzle.A_X.name) swap_y = parse_param(kw, "SWAP_Y", EnumSwizzle, EnumSwizzle.A_Y.name) swap_z = parse_param(kw, "SWAP_Z", EnumSwizzle, EnumSwizzle.A_W.name) diff --git a/core/color.py b/core/color.py index 47a7aaa..dbb4cab 100644 --- a/core/color.py +++ b/core/color.py @@ -118,10 +118,10 @@ Adjust the color scheme of one image to match another with the Color Match Node. d = super().INPUT_TYPES() d = deep_merge(d, { "optional": { - "IMAGE_A": (COZY_TYPE_IMAGE, { + "SOURCE": (COZY_TYPE_IMAGE, { "tooltip": "Pixel Data (RGBA, RGB or Grayscale)" }), - "IMAGE_B": (COZY_TYPE_IMAGE, { + "TARGET": (COZY_TYPE_IMAGE, { "tooltip": "Pixel Data (RGBA, RGB or Grayscale)" }), "MODE": (EnumColorMatchMode._member_names_, { @@ -136,25 +136,32 @@ Adjust the color scheme of one image to match another with the Color Match Node. "default": EnumColorMap.HSV.name, "tooltip": "One of two dozen CV2 Built-in Colormap LUT (Look Up Table) Presets" }), - "VAL": ("INT", {"default": 255, "min": 0, "max": 255, "tooltip":"The number of colors to use from the LUT during the remap. Will quantize the LUT range."}), - "FLIP": ("BOOLEAN", {"default": False}), - "INVERT": ("BOOLEAN", {"default": False, - "tooltip": "Invert the color match output"}), - "MATTE": ("VEC4INT", {"default": (0, 0, 0, 255), "rgb": True}), + "VAL": ("INT", { + "default": 255, "min": 0, "max": 255, + "tooltip":"The number of colors to use from the LUT during the remap. Will quantize the LUT range."}), + "FLIP": ("BOOLEAN", { + "default": False, + "tooltip": "Flip the SOURCE and TARGET inputs"}), + "INVERT": ("BOOLEAN", { + "default": False, + "tooltip": "Invert the color match output"}), + "MATTE": ("VEC4", { + "default": (0, 0, 0, 255), "rgb": True, + "tooltip": "Background Color"}), } }) return d def run(self, **kw) -> RGBAMaskType: - pA = parse_param(kw, "IMAGE_A", EnumConvertType.IMAGE, None) - pB = parse_param(kw, "IMAGE_B", EnumConvertType.IMAGE, None) + pA = parse_param(kw, "SOURCE", EnumConvertType.IMAGE, None) + pB = parse_param(kw, "TARGET", EnumConvertType.IMAGE, None) colormatch_mode = parse_param(kw, "MODE", EnumColorMatchMode, EnumColorMatchMode.REINHARD.name) colormatch_map = parse_param(kw, f"MAP", EnumColorMatchMap, EnumColorMatchMap.USER_MAP.name) colormap = parse_param(kw, "COLORMAP", EnumColorMap, EnumColorMap.HSV.name) num_colors = parse_param(kw, "VAL", EnumConvertType.INT, 255) flip = parse_param(kw, "FLIP", EnumConvertType.BOOLEAN, False) invert = parse_param(kw, "INVERT", EnumConvertType.BOOLEAN, False) - matte = parse_param(kw, "MATTE", EnumConvertType.VEC4, [(0, 0, 0, 255)], 0, 255) + matte = parse_param(kw, "MATTE", EnumConvertType.VEC4, (0, 0, 0, 255), 0, 255) params = list(zip_longest_fill(pA, pB, colormap, colormatch_mode, colormatch_map, num_colors, flip, invert, matte)) images = [] pbar = ProgressBar(len(params)) @@ -244,7 +251,7 @@ The top-k colors ordered from most->least used as a strip, tonal palette and 3D kcolors = parse_param(kw, "VAL", EnumConvertType.INT, 12, 1, 255) lut_height = parse_param(kw, "SIZE", EnumConvertType.INT, 32, 1, 256) nodes = parse_param(kw, "COUNT", EnumConvertType.INT, 33, 1, 255) - wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, [(256, 256)], IMAGE_SIZE_MIN) + wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, (256, 256), IMAGE_SIZE_MIN) params = list(zip_longest_fill(pA, kcolors, nodes, lut_height, wihi)) top_colors = [] @@ -302,7 +309,8 @@ Users can customize the angle of separation for color calculations, offering fle "default": 45, "min": -90, "max": 90, "tooltip": "Custom angle of separation to use when calculating colors" }), - "INVERT": ("BOOLEAN", {"default": False}) + "INVERT": ("BOOLEAN", { + "default": False}) } }) return d @@ -362,7 +370,8 @@ The gradient image will be translated into a single row lookup table. "tooltip": "Sampling method for resizing images" }), "MATTE": ("VEC4", { - "default": (0, 0, 0, 255), "rgb": True + "default": (0, 0, 0, 255), "rgb": True, + "tooltip": "Background Color" }) } }) @@ -373,9 +382,9 @@ The gradient image will be translated into a single row lookup table. gradient = parse_param(kw, "GRADIENT", EnumConvertType.IMAGE, None) flip = parse_param(kw, "FLIP", EnumConvertType.BOOLEAN, False) mode = parse_param(kw, "MODE", EnumScaleMode, EnumScaleMode.MATTE.name) - wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, [(512, 512)], IMAGE_SIZE_MIN) + wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, (512, 512), IMAGE_SIZE_MIN) sample = parse_param(kw, "SAMPLE", EnumInterpolation, EnumInterpolation.LANCZOS4.name) - matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, [(0, 0, 0, 255)], 0, 255) + matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, (0, 0, 0, 255), 0, 255) images = [] params = list(zip_longest_fill(pA, gradient, flip, mode, sample, wihi, matte)) pbar = ProgressBar(len(params)) diff --git a/core/compose.py b/core/compose.py index 132e07d..d6fc90a 100644 --- a/core/compose.py +++ b/core/compose.py @@ -91,7 +91,8 @@ Advanced options include pixelation, quantization, and morphological operations "GAMMA": ("FLOAT", { "default": 1, "min": 0.00001, "max": 1, "step": 0.01}), "MATTE": ("VEC4", { - "default": (0, 0, 0, 255), "rgb": True}), + "default": (0, 0, 0, 255), "rgb": True, + "tooltip": "Background Color"}), "INVERT": ("BOOLEAN", { "default": False, "tooltip": "Invert the mask input"}) @@ -105,12 +106,12 @@ Advanced options include pixelation, quantization, and morphological operations op = parse_param(kw, "FUNCTION", EnumAdjustOP, EnumAdjustOP.BLUR.name) radius = parse_param(kw, "RADIUS", EnumConvertType.INT, 3, 3) val = parse_param(kw, "VAL", EnumConvertType.FLOAT, 0, 0) - lohi = parse_param(kw, "LoHi", EnumConvertType.VEC2, [(0, 1)], 0, 1) - lmh = parse_param(kw, "LMH", EnumConvertType.VEC3, [(0, 0.5, 1)], 0, 1) - hsv = parse_param(kw, "HSV", EnumConvertType.VEC3, [(0, 1, 1)], 0, 1) + lohi = parse_param(kw, "LoHi", EnumConvertType.VEC2, (0, 1), 0, 1) + lmh = parse_param(kw, "LMH", EnumConvertType.VEC3, (0, 0.5, 1), 0, 1) + hsv = parse_param(kw, "HSV", EnumConvertType.VEC3, (0, 1, 1), 0, 1) contrast = parse_param(kw, "CONTRAST", EnumConvertType.FLOAT, 1, 0, 1) gamma = parse_param(kw, "GAMMA", EnumConvertType.FLOAT, 1, 0, 1) - matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, [(0, 0, 0, 255)], 0, 255) + matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, (0, 0, 0, 255), 0, 255) invert = parse_param(kw, "INVERT", EnumConvertType.BOOLEAN, False) params = list(zip_longest_fill(pA, mask, op, radius, val, lohi, lmh, hsv, contrast, gamma, matte, invert)) @@ -253,7 +254,8 @@ Combine two input images using various blending modes, such as normal, screen, m "default": EnumInterpolation.LANCZOS4.name, "tooltip": "Sampling method for resizing images"}), "MATTE": ("VEC4", { - "default": (0, 0, 0, 255), "rgb": True}) + "default": (0, 0, 0, 255), "rgb": True, + "tooltip": "Background Color"}) } }) return d @@ -266,9 +268,9 @@ Combine two input images using various blending modes, such as normal, screen, m alpha = parse_param(kw, "ALPHA", EnumConvertType.FLOAT, 1, 0, 1) flip = parse_param(kw, "FLIP", EnumConvertType.BOOLEAN, False) mode = parse_param(kw, "MODE", EnumScaleMode, EnumScaleMode.MATTE.name) - wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, [(512, 512)], IMAGE_SIZE_MIN) + wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, (512, 512), IMAGE_SIZE_MIN) sample = parse_param(kw, "SAMPLE", EnumInterpolation, EnumInterpolation.LANCZOS4.name) - matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, [(0, 0, 0, 255)], 0, 255) + matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, (0, 0, 0, 255), 0, 255) invert = parse_param(kw, "INVERT", EnumConvertType.BOOLEAN, False) params = list(zip_longest_fill(pA, pB, mask, func, alpha, flip, mode, wihi, sample, matte, invert)) images = [] @@ -333,7 +335,7 @@ Create masks based on specific color ranges within an image. Specify the color r d = super().INPUT_TYPES() d = deep_merge(d, { "optional": { - "IMAGE_A": (COZY_TYPE_IMAGE, { + "IMAGE": (COZY_TYPE_IMAGE, { "tooltip": "Pixel Data (RGBA, RGB or Grayscale)" }), "START": ("VEC3", { @@ -347,18 +349,19 @@ Create masks based on specific color ranges within an image. Specify the color r "default": (0.5,0.5,0.5), "mij":0, "maj":1, "tooltip": "the fuzziness use to extend the start and end range(s)"}), "MATTE": ("VEC4", { - "default": (0, 0, 0, 255), "rgb": True}), + "default": (0, 0, 0, 255), "rgb": True, + "tooltip": "Background Color"}), } }) return d def run(self, **kw) -> RGBAMaskType: - pA = parse_param(kw, "IMAGE_A", EnumConvertType.IMAGE, None) - start = parse_param(kw, "START", EnumConvertType.VEC3INT, [(128,128,128)], 0, 255) - use_range = parse_param(kw, "RANGE", EnumConvertType.VEC3, [(0,0,0)], 0, 255) - end = parse_param(kw, "END", EnumConvertType.VEC3INT, [(128,128,128)], 0, 255) - fuzz = parse_param(kw, "FUZZ", EnumConvertType.VEC3, [(0.5,0.5,0.5)], 0, 1) - matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, [(0, 0, 0, 255)], 0, 255) + pA = parse_param(kw, "IMAGE", EnumConvertType.IMAGE, None) + start = parse_param(kw, "START", EnumConvertType.VEC3INT, (128,128,128), 0, 255) + use_range = parse_param(kw, "RANGE", EnumConvertType.BOOLEAN, False, 0, 255) + end = parse_param(kw, "END", EnumConvertType.VEC3INT, (128,128,128), 0, 255) + fuzz = parse_param(kw, "FUZZ", EnumConvertType.VEC3, (0.5,0.5,0.5), 0, 1) + matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, (0, 0, 0, 255), 0, 255) params = list(zip_longest_fill(pA, start, use_range, end, fuzz, matte)) images = [] pbar = ProgressBar(len(params)) @@ -413,7 +416,8 @@ Combines individual color channels (red, green, blue) along with an optional mas "default": EnumInterpolation.LANCZOS4.name, "tooltip": "Sampling method for resizing images"}), "MATTE": ("VEC4", { - "default": (0, 0, 0, 255), "rgb": True}), + "default": (0, 0, 0, 255), "rgb": True, + "tooltip": "Background Color"}), "FLIP": ("VEC4", { "default": (0,0,0,0), "mij":0, "maj":1, "tooltip": "Invert specific input prior to merging. R, G, B, A."}), @@ -431,10 +435,10 @@ Combines individual color channels (red, green, blue) along with an optional mas B = parse_param(kw, "🟦", EnumConvertType.MASK, None) A = parse_param(kw, "⬜", EnumConvertType.MASK, None) mode = parse_param(kw, "MODE", EnumScaleMode, EnumScaleMode.MATTE.name) - wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, [(512, 512)], IMAGE_SIZE_MIN) + wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, (512, 512), IMAGE_SIZE_MIN) sample = parse_param(kw, "SAMPLE", EnumInterpolation, EnumInterpolation.LANCZOS4.name) - matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, [(0, 0, 0, 255)], 0, 255) - flip = parse_param(kw, "FLIP", EnumConvertType.VEC4, [(0, 0, 0, 0)], 0., 1.) + matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, (0, 0, 0, 255), 0, 255) + flip = parse_param(kw, "FLIP", EnumConvertType.VEC4, (0, 0, 0, 0), 0., 1.) invert = parse_param(kw, "INVERT", EnumConvertType.BOOLEAN, False) params = list(zip_longest_fill(rgba, R, G, B, A, mode, wihi, sample, matte, flip, invert)) images = [] @@ -543,7 +547,8 @@ Swap pixel values between two input images based on specified channel swizzle op "default": EnumPixelSwizzle.ALPHA_A.name, "tooltip": "Replace input Alpha channel with target channel or constant"}), "MATTE": ("VEC4", { - "default": (0, 0, 0, 255), "rgb": True}) + "default": (0, 0, 0, 255), "rgb": True, + "tooltip": "Background Color"}) } }) return d @@ -551,11 +556,11 @@ Swap pixel values between two input images based on specified channel swizzle op def run(self, **kw) -> RGBAMaskType: pA = parse_param(kw, "IMAGE_A", EnumConvertType.IMAGE, None) pB = parse_param(kw, "IMAGE_B", EnumConvertType.IMAGE, None) - swap_r = parse_param(kw, Lexicon.SWAP_R, EnumPixelSwizzle, EnumPixelSwizzle.RED_A.name) - swap_g = parse_param(kw, Lexicon.SWAP_G, EnumPixelSwizzle, EnumPixelSwizzle.GREEN_A.name) - swap_b = parse_param(kw, Lexicon.SWAP_B, EnumPixelSwizzle, EnumPixelSwizzle.BLUE_A.name) - swap_a = parse_param(kw, Lexicon.SWAP_A, EnumPixelSwizzle, EnumPixelSwizzle.ALPHA_A.name) - matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, [(0, 0, 0, 255)], 0, 255) + swap_r = parse_param(kw, "SWAP_R", EnumPixelSwizzle, EnumPixelSwizzle.RED_A.name) + swap_g = parse_param(kw, "SWAP_G", EnumPixelSwizzle, EnumPixelSwizzle.GREEN_A.name) + swap_b = parse_param(kw, "SWAP_B", EnumPixelSwizzle, EnumPixelSwizzle.BLUE_A.name) + swap_a = parse_param(kw, "SWAP_A", EnumPixelSwizzle, EnumPixelSwizzle.ALPHA_A.name) + matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, (0, 0, 0, 255), 0, 255) params = list(zip_longest_fill(pA, pB, swap_r, swap_g, swap_b, swap_a, matte)) images = [] pbar = ProgressBar(len(params)) diff --git a/core/create.py b/core/create.py index 1b6fbac..448369f 100644 --- a/core/create.py +++ b/core/create.py @@ -82,8 +82,8 @@ Generate a constant image or mask of a specified size and color. It can be used def run(self, **kw) -> RGBAMaskType: pA = parse_param(kw, "IMAGE", EnumConvertType.IMAGE, None) mask = parse_param(kw, "MASK", EnumConvertType.IMAGE, None) - matte = parse_param(kw, "COLOR", EnumConvertType.VEC4INT, [(0, 0, 0, 255)], 0, 255) - wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, [(512, 512)], IMAGE_SIZE_MIN) + matte = parse_param(kw, "COLOR", EnumConvertType.VEC4INT, (0, 0, 0, 255), 0, 255) + wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, (512, 512), IMAGE_SIZE_MIN) mode = parse_param(kw, "MODE", EnumScaleMode, EnumScaleMode.MATTE.name) sample = parse_param(kw, "SAMPLE", EnumInterpolation, EnumInterpolation.LANCZOS4.name) images = [] @@ -91,21 +91,25 @@ Generate a constant image or mask of a specified size and color. It can be used pbar = ProgressBar(len(params)) for idx, (pA, mask, matte, wihi, mode, sample) in enumerate(params): width, height = wihi - if mask is not None: - mask = tensor_to_cv(mask) + if pA is None: - pA = channel_solid(width, height, matte, EnumImageType.BGRA) - if mask is not None: - pA = image_mask_add(pA, mask) - images.append(cv_to_tensor_full(pA)) + pA = channel_solid(width, height, (0,0,0,255), EnumImageType.BGRA) else: pA = tensor_to_cv(pA) pA = image_convert(pA, 4) - if mask is not None: - pA = image_mask_add(pA, mask) - if mode != EnumScaleMode.MATTE: - pA = image_scalefit(pA, width, height, mode, sample, matte) - images.append(cv_to_tensor_full(pA, matte)) + + pB = channel_solid(width, height, matte, EnumImageType.BGRA) + + if mask is None: + mask = channel_solid(width, height, (255,255,255,255), EnumImageType.GRAYSCALE) + else: + mask = tensor_to_cv(mask) + + pA = image_blend(pA, pB, mask) + + if mode != EnumScaleMode.MATTE: + pA = image_scalefit(pA, width, height, mode, sample, matte) + images.append(cv_to_tensor_full(pA, matte)) pbar.update_absolute(idx) return image_stack(images) @@ -156,11 +160,11 @@ Create n-sided polygons. These shapes can be customized by adjusting parameters sides = parse_param(kw, "SIDES", EnumConvertType.INT, 3, 3, 100) angle = parse_param(kw, "ANGLE", EnumConvertType.FLOAT, 0) edge = parse_param(kw, "EDGE", EnumEdge, EnumEdge.CLIP.name) - offset = parse_param(kw, "XY", EnumConvertType.VEC2, [(0, 0)]) - size = parse_param(kw, "SIZE", EnumConvertType.VEC2, [(1, 1)], zero=0.001) - wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, [(256, 256)], IMAGE_SIZE_MIN) - color = parse_param(kw, "COLOR", EnumConvertType.VEC4INT, [(255, 255, 255, 255)], 0, 255) - matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, [(0, 0, 0, 255)], 0, 255) + offset = parse_param(kw, "XY", EnumConvertType.VEC2, (0, 0)) + size = parse_param(kw, "SIZE", EnumConvertType.VEC2, (1, 1), zero=0.001) + wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, (256, 256), IMAGE_SIZE_MIN) + color = parse_param(kw, "COLOR", EnumConvertType.VEC4INT, (255, 255, 255, 255), 0, 255) + matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, (0, 0, 0, 255), 0, 255) blur = parse_param(kw, "BLUR", EnumConvertType.FLOAT, 0) params = list(zip_longest_fill(shape, sides, offset, angle, edge, size, wihi, color, matte, blur)) images = [] @@ -272,16 +276,16 @@ Generates images containing text based on parameters such as font, size, alignme font_idx = parse_param(kw, "FONT", EnumConvertType.STRING, self.FONT_NAMES[0]) autosize = parse_param(kw, "AUTOSIZE", EnumConvertType.BOOLEAN, False) letter = parse_param(kw, "LETTER", EnumConvertType.BOOLEAN, False) - color = parse_param(kw, "COLOR", EnumConvertType.VEC4INT, [(255,255,255,255)], 0, 255) - matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, [(0,0,0,255)], 0, 255) + color = parse_param(kw, "COLOR", EnumConvertType.VEC4INT, (255,255,255,255), 0, 255) + matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, (0,0,0,255), 0, 255) columns = parse_param(kw, "COLS", EnumConvertType.INT, 0) font_size = parse_param(kw, "SIZE", EnumConvertType.INT, 1) align = parse_param(kw, "ALIGN", EnumAlignment, EnumAlignment.CENTER.name) justify = parse_param(kw, "JUSTIFY", EnumJustify, EnumJustify.CENTER.name) margin = parse_param(kw, "MARGIN", EnumConvertType.INT, 0) line_spacing = parse_param(kw, "SPACING", EnumConvertType.INT, 0) - wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, [(512, 512)], IMAGE_SIZE_MIN) - pos = parse_param(kw, "XY", EnumConvertType.VEC2, [(0, 0)], -1, 1) + wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, (512, 512), IMAGE_SIZE_MIN) + pos = parse_param(kw, "XY", EnumConvertType.VEC2, (0, 0), -1, 1) angle = parse_param(kw, "ANGLE", EnumConvertType.INT, 0) edge = parse_param(kw, "EDGE", EnumEdge, EnumEdge.CLIP.name) invert = parse_param(kw, "INVERT", EnumConvertType.BOOLEAN, False) diff --git a/core/trans.py b/core/trans.py index e01fe37..95b1eea 100644 --- a/core/trans.py +++ b/core/trans.py @@ -92,7 +92,8 @@ Extract a portion of an input image or resize it. It supports various cropping m "label": ["BOTTOM", "LEFT", "BOTTOM", "RIGHT"], "tooltip": "Bottom Left - Bottom Right"}), "MATTE": ("VEC4", { - "default": (0, 0, 0, 255), "rgb": True}) + "default": (0, 0, 0, 255), "rgb": True, + "tooltip": "Background Color"}) } }) return d @@ -101,11 +102,11 @@ Extract a portion of an input image or resize it. It supports various cropping m pA = parse_param(kw, "IMAGE", EnumConvertType.IMAGE, None) func = parse_param(kw, "FUNCTION", EnumCropMode, EnumCropMode.CENTER.name) # if less than 1 then use as scalar, over 1 = int(size) - xy = parse_param(kw, "XY", EnumConvertType.VEC2, [(0, 0,)], 0, 1) - wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, [(512, 512)], IMAGE_SIZE_MIN) - tltr = parse_param(kw, "TLTR", EnumConvertType.VEC4, [(0, 0, 0, 1,)], 0, 1) - blbr = parse_param(kw, "BLBR", EnumConvertType.VEC4, [(1, 0, 1, 1,)], 0, 1) - matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, [(0, 0, 0, 255)], 0, 255) + xy = parse_param(kw, "XY", EnumConvertType.VEC2, (0, 0,), 0, 1) + wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, (512, 512), IMAGE_SIZE_MIN) + tltr = parse_param(kw, "TLTR", EnumConvertType.VEC4, (0, 0, 0, 1,), 0, 1) + blbr = parse_param(kw, "BLBR", EnumConvertType.VEC4, (1, 0, 1, 1,), 0, 1) + matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, (0, 0, 0, 255), 0, 255) params = list(zip_longest_fill(pA, func, xy, wihi, tltr, blbr, matte)) images = [] pbar = ProgressBar(len(params)) @@ -160,7 +161,8 @@ Combine multiple input images into a single image by summing their pixel values. "default": EnumInterpolation.LANCZOS4.name, "tooltip": "Sampling method for resizing images"}), "MATTE": ("VEC4", { - "default": (0, 0, 0, 255), "rgb": True}) + "default": (0, 0, 0, 255), "rgb": True, + "tooltip": "Background Color"}) } }) return d @@ -174,9 +176,9 @@ Combine multiple input images into a single image by summing their pixel values. # be less dumb when merging pA = [tensor_to_cv(i) for i in imgs] mode = parse_param(kw, "MODE", EnumScaleMode, EnumScaleMode.MATTE.name) - wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, [(512, 512)], IMAGE_SIZE_MIN) + wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, (512, 512), IMAGE_SIZE_MIN) sample = parse_param(kw, "SAMPLE", EnumInterpolation, EnumInterpolation.LANCZOS4.name) - matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, [(0, 0, 0, 255)], 0, 255) + matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, (0, 0, 0, 255), 0, 255) images = [] params = list(zip_longest_fill(mode, sample, wihi, matte)) @@ -220,7 +222,8 @@ The axis parameter allows for horizontal, vertical, or grid stacking of images, "default": EnumInterpolation.LANCZOS4.name, "tooltip": "Sampling method for resizing images"}), "MATTE": ("VEC4", { - "default": (0, 0, 0, 255), "rgb": True}) + "default": (0, 0, 0, 255), "rgb": True, + "tooltip": "Background Color"}) } }) return d @@ -235,9 +238,9 @@ The axis parameter allows for horizontal, vertical, or grid stacking of images, axis = parse_param(kw, "AXIS", EnumOrientation, EnumOrientation.GRID.name)[0] stride = parse_param(kw, "STEP", EnumConvertType.INT, 1)[0] mode = parse_param(kw, "MODE", EnumScaleMode, EnumScaleMode.MATTE.name)[0] - wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, [(512, 512)], IMAGE_SIZE_MIN)[0] + wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, (512, 512), IMAGE_SIZE_MIN)[0] sample = parse_param(kw, "SAMPLE", EnumInterpolation, EnumInterpolation.LANCZOS4.name)[0] - matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, [(0, 0, 0, 255)], 0, 255)[0] + matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, (0, 0, 0, 255), 0, 255)[0] img = image_stacker(images, axis, stride) #, matte) if mode != EnumScaleMode.MATTE: w, h = wihi @@ -304,7 +307,8 @@ Apply various geometric transformations to images, including translation, rotati "default": EnumInterpolation.LANCZOS4.name, "tooltip": "Sampling method for resizing images"}), "MATTE": ("VEC4", { - "default": (0, 0, 0, 255), "rgb": True}) + "default": (0, 0, 0, 255), "rgb": True, + "tooltip": "Background Color"}) } }) return d @@ -312,21 +316,21 @@ Apply various geometric transformations to images, including translation, rotati def run(self, **kw) -> RGBAMaskType: pA = parse_param(kw, "IMAGE", EnumConvertType.IMAGE, None) mask = parse_param(kw, "MASK", EnumConvertType.IMAGE, None) - offset = parse_param(kw, "XY", EnumConvertType.VEC2, [(0., 0.)], -2.5, 2.5) + offset = parse_param(kw, "XY", EnumConvertType.VEC2, (0., 0.), -2.5, 2.5) angle = parse_param(kw, "ANGLE", EnumConvertType.FLOAT, 0) - size = parse_param(kw, "SIZE", EnumConvertType.VEC2, [(1., 1.)], 0.001) + size = parse_param(kw, "SIZE", EnumConvertType.VEC2, (1., 1.), 0.001) edge = parse_param(kw, "EDGE", EnumEdge, EnumEdge.CLIP.name) mirror = parse_param(kw, "MIRROR", EnumMirrorMode, EnumMirrorMode.NONE.name) - mirror_pivot = parse_param(kw, "PIVOT", EnumConvertType.VEC2, [(0.5, 0.5)], 0, 1) - tile_xy = parse_param(kw, "TILE", EnumConvertType.VEC2, [(1., 1.)], 1) + mirror_pivot = parse_param(kw, "PIVOT", EnumConvertType.VEC2, (0.5, 0.5), 0, 1) + tile_xy = parse_param(kw, "TILE", EnumConvertType.VEC2, (1., 1.), 1) proj = parse_param(kw, "PROJ", EnumProjection, EnumProjection.NORMAL.name) - tltr = parse_param(kw, "TLTR", EnumConvertType.VEC4, [(0., 0., 1., 0.)], 0, 1) - blbr = parse_param(kw, "BLBR", EnumConvertType.VEC4, [(0., 1., 1., 1.)], 0, 1) + tltr = parse_param(kw, "TLTR", EnumConvertType.VEC4, (0., 0., 1., 0.), 0, 1) + blbr = parse_param(kw, "BLBR", EnumConvertType.VEC4, (0., 1., 1., 1.), 0, 1) strength = parse_param(kw, "STRENGTH", EnumConvertType.FLOAT, 1, 0, 1) mode = parse_param(kw, "MODE", EnumScaleMode, EnumScaleMode.MATTE.name) - wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, [(512, 512)], IMAGE_SIZE_MIN) + wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, (512, 512), IMAGE_SIZE_MIN) sample = parse_param(kw, "SAMPLE", EnumInterpolation, EnumInterpolation.LANCZOS4.name) - matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, [(0, 0, 0, 255)], 0, 255) + matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, (0, 0, 0, 255), 0, 255) params = list(zip_longest_fill(pA, mask, offset, angle, size, edge, tile_xy, mirror, mirror_pivot, proj, strength, tltr, blbr, mode, wihi, sample, matte)) images = [] pbar = ProgressBar(len(params)) diff --git a/core/utility/batch.py b/core/utility/batch.py index eed6b3f..4424bd2 100644 --- a/core/utility/batch.py +++ b/core/utility/batch.py @@ -69,10 +69,6 @@ class EnumBatchMode(Enum): # === CLASS === # ============================================================================== -class ContainsAnyDict(dict): - def __contains__(self, key) -> Literal[True]: - return True - class ArrayNode(CozyBaseNode): NAME = "ARRAY (JOV) 📚" CATEGORY = JOV_CATEGORY @@ -127,7 +123,7 @@ Processes a batch of data based on the selected mode. Merge, pick, slice, random def run(self, **kw) -> tuple[int, list]: data_list = parse_dynamic(kw, "❔", EnumConvertType.ANY, None) mode = parse_param(kw, "MODE", EnumBatchMode, EnumBatchMode.MERGE.name)[0] - slice_range = parse_param(kw, "RANGE", EnumConvertType.VEC3INT, [(0, 0, 1)])[0] + slice_range = parse_param(kw, "RANGE", EnumConvertType.VEC3INT, (0, 0, 1))[0] index = parse_param(kw, "INDEX", EnumConvertType.STRING, "")[0] count = parse_param(kw, "COUNT", EnumConvertType.INT, 0, 0, sys.maxsize)[0] reverse = parse_param(kw, "REVERSE", EnumConvertType.BOOLEAN, False)[0] @@ -251,7 +247,7 @@ class QueueBaseNode(CozyBaseNode): CATEGORY = JOV_CATEGORY RETURN_TYPES = (COZY_TYPE_ANY, COZY_TYPE_ANY, "STRING", "INT", "INT", "BOOLEAN") RETURN_NAMES = ("🦄", "QUEUE", "CURRENT", "INDEX", "TOTAL", "TRIGGER", ) - OUTPUT_IS_LIST = (True, True, True, True, True, True,) + #OUTPUT_IS_LIST = (True, True, True, True, True, True,) VIDEO_FORMATS = ['.wav', '.mp3', '.webm', '.mp4', '.avi', '.wmv', '.mkv', '.mov', '.mxf'] @classmethod @@ -272,9 +268,9 @@ class QueueBaseNode(CozyBaseNode): "BATCH": ("BOOLEAN", { "default": False, "tooltip":"Load all items, if they are loadable items, i.e. batch load images from the Queue's list."}), - "VALUE": ("INT", { + "SELECT": ("INT", { "default": 0, "min": 0, - "tooltip": "The current index for the current queue item"}), + "tooltip": "What index to use for the current queue item. 0 will move to the next item each queue run"}), "HOLD": ("BOOLEAN", { "default": False, "tooltip":"Hold the item at the current queue index"}), @@ -371,13 +367,20 @@ class QueueBaseNode(CozyBaseNode): self.__ident = ident # should work headless as well + + if (new_val := parse_param(kw, "SELECT", EnumConvertType.INT, 0)[0]) > 0: + self.__index = new_val - 1 + reset = parse_reset(ident) > 0 if reset or parse_param(kw, "RESET", EnumConvertType.BOOLEAN, False)[0]: self.__q = None self.__index = 0 - if (new_val := parse_param(kw, "VALUE", EnumConvertType.INT, 0)[0]) > 0: - self.__index = new_val - 1 + mode = parse_param(kw, "MODE", EnumScaleMode, EnumScaleMode.MATTE.name)[0] + sample = parse_param(kw, "SAMPLE", EnumInterpolation, EnumInterpolation.LANCZOS4.name)[0] + wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, (512, 512), IMAGE_SIZE_MIN)[0] + w, h = wihi + matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, (0, 0, 0, 255), 0, 255)[0] if self.__q is None: # process Q into ... @@ -420,26 +423,21 @@ class QueueBaseNode(CozyBaseNode): for idx in range(self.__len): ret = self.process(self.__q[idx]) if isinstance(ret, (np.ndarray,)): - h, w, c = ret.shape - mw, mh, mc = max(mw, w), max(mh, h), max(mc, c) + h2, w2, c = ret.shape + mw, mh, mc = max(mw, w2), max(mh, h2), max(mc, c) data.append(ret) if mw != 0 or mh != 0 or mc != 0: ret = [] - mode = parse_param(kw, "MODE", EnumScaleMode, EnumScaleMode.MATTE.name)[0] - sample = parse_param(kw, "SAMPLE", EnumInterpolation, EnumInterpolation.LANCZOS4.name)[0] - wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, [(512, 512)], IMAGE_SIZE_MIN)[0] - w2, h2 = wihi - matte = parse_param(kw, "MATTE", EnumConvertType.VEC4INT, [(0, 0, 0, 255)], 0, 255)[0] - matte = [matte[0], matte[1], matte[2], 0] + # matte = [matte[0], matte[1], matte[2], 0] pbar = ProgressBar(self.__len) - for idx, d in enumerate(data): d = image_convert(d, mc) if mode != EnumScaleMode.MATTE: - d = image_scalefit(d, w2, h2, mode=mode, sample=sample) + d = image_scalefit(d, w, h, mode, sample, matte) + d = image_scalefit(d, w, h, EnumScaleMode.RESIZE_MATTE, sample, matte) else: - d = image_matte(d, matte, width=mw, height=mh) + d = image_matte(d, matte, mw, mh) ret.append(cv_to_tensor(d)) pbar.update_absolute(idx) data = torch.stack(ret) @@ -448,6 +446,8 @@ class QueueBaseNode(CozyBaseNode): else: data = self.process(self.__q[self.__index]) if isinstance(data, (np.ndarray,)): + if mode != EnumScaleMode.MATTE: + data = image_scalefit(data, w, h, mode, sample) data = cv_to_tensor(data).unsqueeze(0) self.__index += 1 @@ -455,7 +455,7 @@ class QueueBaseNode(CozyBaseNode): comfy_api_post("jovi-queue-ping", ident, self.status) if stop and batched: interrupt_processing() - return data, self.__q, self.__current, self.__index, self.__len, self.__index == self.__index_last or batched + return data, self.__q, self.__current, self.__index, self.__len, self.__index == self.__len or batched @property def status(self) -> dict[str, Any]: @@ -485,8 +485,8 @@ Manage a queue of items, such as file paths or data. Supports various formats in class QueueTooNode(QueueBaseNode): NAME = "QUEUE TOO (JOV) 🗃" RETURN_TYPES = ("IMAGE", "IMAGE", "MASK", "STRING", "INT", "INT", "BOOLEAN") - RETURN_NAMES = ("IMAGE", "RGB", "MASK", "CURRENT", "INDEX", "TOTAL", "TRIGGER", ) - OUTPUT_IS_LIST = (False, False, False, True, True, True, True,) + RETURN_NAMES = ("RGBA", "RGB", "MASK", "CURRENT", "INDEX", "TOTAL", "TRIGGER", ) + #OUTPUT_IS_LIST = (False, False, False, True, True, True, True,) OUTPUT_TOOLTIPS = ( "Full channel [RGBA] image. If there is an alpha, the image will be masked out with it when using this output", "Three channel [RGB] image. There will be no alpha", diff --git a/core/utility/info.py b/core/utility/info.py index 500cdfc..06ca3c1 100644 --- a/core/utility/info.py +++ b/core/utility/info.py @@ -180,7 +180,7 @@ Visualize a series of data points over time. It accepts a dynamic number of valu def run(self, ident, **kw) -> tuple[TensorType]: slice = parse_param(kw, "VAL", EnumConvertType.INT, 60)[0] - wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, [(512, 512)], 1)[0] + wihi = parse_param(kw, "WH", EnumConvertType.VEC2INT, (512, 512), 1)[0] if parse_reset(ident) > 0 or parse_param(kw, "RESET", EnumConvertType.BOOLEAN, False)[0]: self.__history = [] longest_edge = 0 @@ -236,7 +236,7 @@ Exports and Displays immediate information about images. d = super().INPUT_TYPES() d = deep_merge(d, { "optional": { - "IMAGE_A": (COZY_TYPE_IMAGE, { + "IMAGE": (COZY_TYPE_IMAGE, { "default": None, "tooltip":"The image to examine"}) } @@ -244,6 +244,6 @@ Exports and Displays immediate information about images. return d def run(self, **kw) -> tuple[int, list]: - image = parse_param(kw, "IMAGE_A", EnumConvertType.IMAGE, None) + image = parse_param(kw, "IMAGE", EnumConvertType.IMAGE, None) height, width, cc = image[0].shape return (len(image), width, height, cc, (width, height), (width, height, cc)) diff --git a/core/utility/io.py b/core/utility/io.py index 860eee5..5eb1b9f 100644 --- a/core/utility/io.py +++ b/core/utility/io.py @@ -308,27 +308,31 @@ Save the output image along with its metadata to the specified path. Supports sa d = super().INPUT_TYPES(True, True) d = deep_merge(d, { "optional": { - "image": ("IMAGE", {"default": None, - "tooltip":""}), - "path": ("STRING", {"default": "", "dynamicPrompts":False, - "tooltip":"Destination path to save the output"}), - "fname": ("STRING", {"default": "output", "dynamicPrompts":False, - "tooltip":"Filename of the output"}), - "metadata": ("JSON", {"default": None, - "tooltip":"Extra metadata to save in the file"}), - "usermeta": ("STRING", {"default": "", "multiline": True, - "dynamicPrompts":False, - "tooltip":"Custom user metadat to save with the file"}), + "IMAGE": ("IMAGE", { + "default": None, + "tooltip":""}), + "PATH": ("STRING", { + "default": "", "dynamicPrompts":False, + "tooltip":"Destination path to save the output"}), + "NAME": ("STRING", { + "default": "output", "dynamicPrompts":False, + "tooltip":"Filename of the output"}), + "META": ("JSON", { + "default": None, + "tooltip":"Extra metadata to save in the file"}), + "USER": ("STRING", { + "default": "", "multiline": True, "dynamicPrompts":False, + "tooltip":"Custom user metadat to save with the file"}), } }) return d def run(self, **kw) -> dict[str, Any]: - image = parse_param(kw, 'image', EnumConvertType.IMAGE, None) - metadata = parse_param(kw, 'metadata', EnumConvertType.DICT, {}) - usermeta = parse_param(kw, 'usermeta', EnumConvertType.DICT, {}) - path = parse_param(kw, 'path', EnumConvertType.STRING, "") - fname = parse_param(kw, 'fname', EnumConvertType.STRING, "output") + image = parse_param(kw, 'IMAGE', EnumConvertType.IMAGE, None) + path = parse_param(kw, 'PATH', EnumConvertType.STRING, "") + fname = parse_param(kw, 'NAME', EnumConvertType.STRING, "output") + metadata = parse_param(kw, 'META', EnumConvertType.DICT, {}) + usermeta = parse_param(kw, 'USER', EnumConvertType.DICT, {}) prompt = parse_param(kw, 'prompt', EnumConvertType.STRING, "") pnginfo = parse_param(kw, 'extra_pnginfo', EnumConvertType.DICT, {}) params = list(zip_longest_fill(image, path, fname, metadata, usermeta, prompt, pnginfo)) diff --git a/core/vars.py b/core/vars.py index 43d1150..52935e6 100644 --- a/core/vars.py +++ b/core/vars.py @@ -23,7 +23,7 @@ class ValueNode(CozyBaseNode): NAME = "VALUE (JOV) 🧬" CATEGORY = JOV_CATEGORY RETURN_TYPES = (COZY_TYPE_ANY, COZY_TYPE_ANY, COZY_TYPE_ANY, COZY_TYPE_ANY, COZY_TYPE_ANY,) - RETURN_NAMES = ("🦄", "X", "Y", "Z", "W") + RETURN_NAMES = ("🦄", "X", "Y", "Z", "W",) SORT = 5 DESCRIPTION = """ Supplies raw or default values for various data types, supporting vector input with components for X, Y, Z, and W. It also provides a string input option. @@ -71,15 +71,15 @@ Supplies raw or default values for various data types, supporting vector input w return d def run(self, **kw) -> tuple[bool]: - raw = parse_param(kw, "A", EnumConvertType.ANY, [0]) + raw = parse_param(kw, "A", EnumConvertType.ANY, 0) r_x = parse_param(kw, "X", EnumConvertType.FLOAT, None, -sys.maxsize, sys.maxsize) r_y = parse_param(kw, "Y", EnumConvertType.FLOAT, None, -sys.maxsize, sys.maxsize) r_z = parse_param(kw, "Z", EnumConvertType.FLOAT, None, -sys.maxsize, sys.maxsize) r_w = parse_param(kw, "W", EnumConvertType.FLOAT, None, -sys.maxsize, sys.maxsize) typ = parse_param(kw, "TYPE", EnumConvertType, EnumConvertType.BOOLEAN.name) - xyzw = parse_param(kw, "AA", EnumConvertType.VEC4, [(0, 0, 0, 0)]) + xyzw = parse_param(kw, "AA", EnumConvertType.VEC4, (0, 0, 0, 0)) seed = parse_param(kw, "SEED", EnumConvertType.INT, 0, 0) - yyzw = parse_param(kw, "BB", EnumConvertType.VEC4, [(1, 1, 1, 1)]) + yyzw = parse_param(kw, "BB", EnumConvertType.VEC4, (1, 1, 1, 1)) x_str = parse_param(kw, "STRING", EnumConvertType.STRING, "") params = list(zip_longest_fill(raw, r_x, r_y, r_z, r_w, typ, xyzw, seed, yyzw, x_str)) results = [] @@ -170,7 +170,6 @@ Outputs a VECTOR2. def run(self, **kw) -> tuple[tuple[float, ...], tuple[int, ...]]: x = parse_param(kw, "X", EnumConvertType.FLOAT, None, -sys.maxsize, sys.maxsize) y = parse_param(kw, "Y", EnumConvertType.FLOAT, None, -sys.maxsize, sys.maxsize) - a = parse_param(kw, "A", EnumConvertType.FLOAT, 0, -sys.maxsize, sys.maxsize) b = parse_param(kw, "B", EnumConvertType.FLOAT, 0, -sys.maxsize, sys.maxsize) @@ -181,7 +180,6 @@ Outputs a VECTOR2. x = round(a, 9) if x is None else round(x, 9) y = round(b, 9) if y is None else round(y, 9) result.append((x, y,)) - #resultY.append((int(x), int(y))) pbar.update_absolute(idx) return result, @@ -190,6 +188,7 @@ class Vector3Node(CozyBaseNode): CATEGORY = JOV_CATEGORY RETURN_TYPES = ("VEC3",) RETURN_NAMES = ("VEC3",) + OUTPUT_IS_LIST = (True,) OUTPUT_TOOLTIPS = ( "Vector3 with float values", ) @@ -204,41 +203,53 @@ Outputs a VECTOR3. d = deep_merge(d, { "optional": { "X": (COZY_TYPE_NUMBER, { - "default": 0, "min": -sys.maxsize, "max": sys.maxsize, - "tooltip": "1st channel value"}), + "min": -sys.maxsize, "max": sys.maxsize, + "tooltip": "X channel value"}), "Y": (COZY_TYPE_NUMBER, { - "default": 0, "min": -sys.maxsize, "max": sys.maxsize, - "tooltip": "2nd channel value"}), + "min": -sys.maxsize, "max": sys.maxsize, + "tooltip": "Y channel value"}), "Z": (COZY_TYPE_NUMBER, { + "min": -sys.maxsize, "max": sys.maxsize, + "tooltip": "Z channel value"}), + "A": ("FLOAT", { "default": 0, "min": -sys.maxsize, "max": sys.maxsize, - "tooltip": "3rd channel value"}), + "tooltip": "Default X channel value"}), + "B": ("FLOAT", { + "default": 0, "min": -sys.maxsize, "max": sys.maxsize, + "tooltip": "Default Y channel value"}), + "C": ("FLOAT", { + "default": 0, "min": -sys.maxsize, "max": sys.maxsize, + "tooltip": "Default Z channel value"}), } }) return d def run(self, **kw) -> tuple[tuple[float, ...], tuple[int, ...]]: - x = parse_param(kw, "X", EnumConvertType.FLOAT, 0, -sys.maxsize, sys.maxsize) - y = parse_param(kw, "Y", EnumConvertType.FLOAT, 0, -sys.maxsize, sys.maxsize) - z = parse_param(kw, "Z", EnumConvertType.FLOAT, 0, -sys.maxsize, sys.maxsize) - results = [] - params = list(zip_longest_fill(x, y, z)) + x = parse_param(kw, "X", EnumConvertType.FLOAT, None, -sys.maxsize, sys.maxsize) + y = parse_param(kw, "Y", EnumConvertType.FLOAT, None, -sys.maxsize, sys.maxsize) + z = parse_param(kw, "Z", EnumConvertType.FLOAT, None, -sys.maxsize, sys.maxsize) + a = parse_param(kw, "A", EnumConvertType.FLOAT, 0, -sys.maxsize, sys.maxsize) + b = parse_param(kw, "B", EnumConvertType.FLOAT, 0, -sys.maxsize, sys.maxsize) + c = parse_param(kw, "C", EnumConvertType.FLOAT, 0, -sys.maxsize, sys.maxsize) + result = [] + params = list(zip_longest_fill(x, y, z, a, b, c)) pbar = ProgressBar(len(params)) - for idx, (x, y, z) in enumerate(params): - x = round(x, 6) - y = round(y, 6) - z = round(z, 6) - results.append([(x, y, z,), (int(x), int(y), int(z),)]) + for idx, (x, y, z, a, b, c) in enumerate(params): + x = round(a, 9) if x is None else round(x, 9) + y = round(b, 9) if y is None else round(y, 9) + z = round(c, 9) if z is None else round(z, 9) + result.append((x, y, z,)) pbar.update_absolute(idx) - return *list(zip(*results)), + return result, class Vector4Node(CozyBaseNode): NAME = "VECTOR4 (JOV)" CATEGORY = JOV_CATEGORY - RETURN_TYPES = ("VEC4", "VEC4INT", ) - RETURN_NAMES = ("VEC4", "VEC4INT", ) + RETURN_TYPES = ("VEC4",) + RETURN_NAMES = ("VEC4",) + OUTPUT_IS_LIST = (True,) OUTPUT_TOOLTIPS = ( "Vector4 with float values", - "Vector4 with integer values", ) SORT = 294 DESCRIPTION = """ @@ -251,37 +262,53 @@ Outputs a VEC4. d = deep_merge(d, { "optional": { "X": (COZY_TYPE_NUMBER, { - "default": 0, "min": -sys.maxsize, "max": sys.maxsize, - "tooltip": "1st channel value"}), + "min": -sys.maxsize, "max": sys.maxsize, + "tooltip": "X channel value"}), "Y": (COZY_TYPE_NUMBER, { - "default": 0, "min": -sys.maxsize, "max": sys.maxsize, - "tooltip": "2nd channel value"}), + "min": -sys.maxsize, "max": sys.maxsize, + "tooltip": "Y channel value"}), "Z": (COZY_TYPE_NUMBER, { - "default": 0, "min": -sys.maxsize, "max": sys.maxsize, - "tooltip": "3rd channel value"}), + "min": -sys.maxsize, "max": sys.maxsize, + "tooltip": "Z channel value"}), "W": (COZY_TYPE_NUMBER, { + "min": -sys.maxsize, "max": sys.maxsize, + "tooltip": "W channel value"}), + "A": ("FLOAT", { "default": 0, "min": -sys.maxsize, "max": sys.maxsize, - "tooltip": "4th channel value"}), + "tooltip": "Default X channel value"}), + "B": ("FLOAT", { + "default": 0, "min": -sys.maxsize, "max": sys.maxsize, + "tooltip": "Default Y channel value"}), + "C": ("FLOAT", { + "default": 0, "min": -sys.maxsize, "max": sys.maxsize, + "tooltip": "Default Z channel value"}), + "D": ("FLOAT", { + "default": 0, "min": -sys.maxsize, "max": sys.maxsize, + "tooltip": "Default W channel value"}), } }) return d def run(self, **kw) -> tuple[tuple[float, ...], tuple[int, ...]]: - x = parse_param(kw, "X", EnumConvertType.FLOAT, 0, -sys.maxsize, sys.maxsize) - y = parse_param(kw, "Y", EnumConvertType.FLOAT, 0, -sys.maxsize, sys.maxsize) - z = parse_param(kw, "Z", EnumConvertType.FLOAT, 0, -sys.maxsize, sys.maxsize) - w = parse_param(kw, "W", EnumConvertType.FLOAT, 0, -sys.maxsize, sys.maxsize) - results = [] - params = list(zip_longest_fill(x, y, z, w)) + x = parse_param(kw, "X", EnumConvertType.FLOAT, None, -sys.maxsize, sys.maxsize) + y = parse_param(kw, "Y", EnumConvertType.FLOAT, None, -sys.maxsize, sys.maxsize) + z = parse_param(kw, "Z", EnumConvertType.FLOAT, None, -sys.maxsize, sys.maxsize) + w = parse_param(kw, "W", EnumConvertType.FLOAT, None, -sys.maxsize, sys.maxsize) + a = parse_param(kw, "A", EnumConvertType.FLOAT, 0, -sys.maxsize, sys.maxsize) + b = parse_param(kw, "B", EnumConvertType.FLOAT, 0, -sys.maxsize, sys.maxsize) + c = parse_param(kw, "C", EnumConvertType.FLOAT, 0, -sys.maxsize, sys.maxsize) + d = parse_param(kw, "D", EnumConvertType.FLOAT, 0, -sys.maxsize, sys.maxsize) + result = [] + params = list(zip_longest_fill(x, y, z, w, a, b, c, d)) pbar = ProgressBar(len(params)) - for idx, (x, y, z, w,) in enumerate(params): - x = round(x, 6) - y = round(y, 6) - z = round(z, 6) - w = round(w, 6) - results.append([(x, y, z, w,), (int(x), int(y), int(z), int(w),)]) + for idx, (x, y, z, w, a, b, c, d) in enumerate(params): + x = round(a, 9) if x is None else round(x, 9) + y = round(b, 9) if y is None else round(y, 9) + z = round(c, 9) if z is None else round(z, 9) + w = round(d, 9) if w is None else round(w, 9) + result.append((x, y, z, w,)) pbar.update_absolute(idx) - return *list(zip(*results)), + return result, ''' class ParameterNode(CozyBaseNode): diff --git a/sup/image/adjust.py b/sup/image/adjust.py index fbfb411..eebc962 100644 --- a/sup/image/adjust.py +++ b/sup/image/adjust.py @@ -16,7 +16,7 @@ from cozy_comfyui.image import \ from cozy_comfyui.image.convert import \ ImageType, \ - image_matte, image_mask_add, image_convert, image_to_bgr, \ + image_matte, image_mask, image_mask_add, image_convert, image_to_bgr, \ bgr_to_image, cv_to_tensor, tensor_to_cv from cozy_comfyui.image.crop import \ @@ -382,11 +382,15 @@ def image_scalefit(image: ImageType, width: int, height:int, case EnumScaleMode.RESIZE_MATTE: h, w = image.shape[:2] - width = max(width, w) - height = max(height, h) - canvas = np.full((height, width, 4), matte, dtype=image.dtype) - image = image_matte(image, (0,0,0,0), width, height) + w2 = max(width, w) + h2 = max(height, h) + canvas = np.full((h2, w2, 4), matte, dtype=image.dtype) + mask = image_mask(image) + mask = image_matte(mask, (255, 255, 255, 255), w2, h2) + image = image_matte(image, (0,0,0,0), w2, h2) image = image_blend(canvas, image) + image = image_mask_add(image, mask) + image = image_crop_center(image, width, height) case EnumScaleMode.ASPECT: h, w = image.shape[:2] diff --git a/sup/image/compose.py b/sup/image/compose.py index 42ccb61..fb4ae45 100644 --- a/sup/image/compose.py +++ b/sup/image/compose.py @@ -120,19 +120,19 @@ def pixel_convert(color:PixelType, size:int=4, alpha:int=255) -> PixelType: These are core functions that most of the support image libraries require. """ -def image_blend(imageA: ImageType, imageB: ImageType, mask:Optional[ImageType]=None, +def image_blend(background: ImageType, foreground: ImageType, mask:Optional[ImageType]=None, blendOp:BlendType=BlendType.NORMAL, alpha:float=1) -> ImageType: """Blending that will size to the largest input's background.""" # prep A - h, w = imageA.shape[:2] - imageA = image_convert(imageA, 4, w, h) - imageA = cv_to_pil(imageA) + h, w = background.shape[:2] + background = image_convert(background, 4, w, h) + background = cv_to_pil(background) # prep B - cc = imageB.shape[2] if imageB.ndim > 2 else 1 - imageB = image_convert(imageB, 4, w, h) - old_mask = image_mask(imageB, 0) + cc = foreground.shape[2] if foreground.ndim > 2 else 1 + foreground = image_convert(foreground, 4, w, h) + old_mask = image_mask(foreground, 0) if mask is None: mask = old_mask @@ -142,10 +142,10 @@ def image_blend(imageA: ImageType, imageB: ImageType, mask:Optional[ImageType]=N if cc == 4: mask = cv2.bitwise_and(mask, old_mask) - imageB[..., 3] = mask - imageB = cv_to_pil(imageB) + foreground[..., 3] = mask + foreground = cv_to_pil(foreground) alpha = np.clip(alpha, 0, 1) - image = blendLayers(imageA, imageB, blendOp.value, alpha) + image = blendLayers(background, foreground, blendOp.value, alpha) image = pil_to_cv(image) if cc == 4: image = image_mask_add(image, mask) diff --git a/web/util.js b/web/util.js index 0de917d..61b9f41 100644 --- a/web/util.js +++ b/web/util.js @@ -36,12 +36,10 @@ export async function apiJovimetrix(id, cmd, data=null, route="message", ) { } catch (error) { console.error("API call to Jovimetrix failed:", error); - throw error; // or return { success: false, message: error.message } + throw error; } } -//export const widgetFind = (widgets, name) => widgets.find(w => w.name == name); - function widgetShowVector(widget, values={}, type) { if (["FLOAT"].includes(type)) { type = "VEC1"; @@ -86,7 +84,6 @@ function widgetShowVector(widget, values={}, type) { widget.value = {}; for (let i = 0; i < size; i++) { widget.value[i] = (widget.options.precision == 0) ? Number(values[i]) : parseFloat(values[i]).toFixed(widget.options.precision); - //widget.value[i] = !widget.type.endsWith('INT') ? Math.round(values[i]) : Number(values[i]); } } else { widget.value = values[0] ? true : false; @@ -155,7 +152,6 @@ export function widgetHookControl(node, control_key, target, matchFloatSize=fals } const data = { - //track_xyzw: target.options?.default, //initializeTrack(target), track_xyzw: initializeTrack(target), target, combo @@ -164,19 +160,14 @@ export function widgetHookControl(node, control_key, target, matchFloatSize=fals const oldCallback = combo.callback; combo.callback = () => { const me = oldCallback?.apply(this, arguments); - //widgetHide(node, target, "-jov"); - //if (["VEC2", "VEC2INT", "COORD2D", "VEC3", "VEC3INT", "VEC4", "VEC4INT", "BOOLEAN", "INT", "FLOAT"].includes(combo.value)) { if (["VEC2", "VEC3", "VEC4", "BOOLEAN", "INT", "FLOAT"].includes(combo.value)) { let type = combo.value; if (matchFloatSize) { type = "FLOAT"; - // if (["VEC2", "VEC2INT", "COORD2D"].includes(combo.value)) { if (["VEC2"].includes(combo.value)) { type = "VEC2"; - //} else if (["VEC3", "VEC3INT"].includes(combo.value)) { } else if (["VEC3"].includes(combo.value)) { type = "VEC3"; - //} else if (["VEC4", "VEC4INT"].includes(combo.value)) { } else if (["VEC4"].includes(combo.value)) { type = "VEC4"; }