From bf0aaa80ad4fca916dd431370c23ff5e83a23180 Mon Sep 17 00:00:00 2001 From: TeaCrab Date: Wed, 8 Nov 2023 17:36:32 -0500 Subject: [PATCH] Added some random things. Nothing major. --- .vscode/settings.json | 15 ++++ _Portal.bat | 3 + _func.py | 177 +++++++++++++++++++++++++++++++++++++++++- _test.py | 4 + nodes.py | 55 ++++++++++--- 5 files changed, 243 insertions(+), 11 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 _Portal.bat create mode 100644 _test.py diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..584fb66 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,15 @@ +{ + "cSpell.enabled": true, + "cSpell.words": [ + "alfault", + "byfault", + "debouncer", + "fclamp", + "hlsa", + "iclamp", + "ispowof", + "nparray", + "NYBL", + "yiqa" + ] +} \ No newline at end of file diff --git a/_Portal.bat b/_Portal.bat new file mode 100644 index 0000000..9f95d3f --- /dev/null +++ b/_Portal.bat @@ -0,0 +1,3 @@ +@echo off + +python _test.py \ No newline at end of file diff --git a/_func.py b/_func.py index 55e524f..84c3e6e 100644 --- a/_func.py +++ b/_func.py @@ -1,5 +1,16 @@ - +import regex_spm, re, code +import numpy as np from math import ceil, floor, log +from enum import Enum + +from colorsys import ( + rgb_to_hls, + rgb_to_hsv, + rgb_to_yiq, + hls_to_rgb, + hsv_to_rgb, + yiq_to_rgb, +) debouncer = set() @@ -41,3 +52,167 @@ def pixel_approx(primary, secondary, total=1024, ratio=1.0, primary, secondary = pixel_approx(primary, secondary, total, ratio, threshold, _inc, False) return (int(primary), int(secondary)) + +class RE: + HEX = re.compile(r"^\#[\da-fA-F]{1,8}$", re.IGNORECASE) + RGB = re.compile(r"^rgba?\s+(?:\d{1,3}[, ](?=\d)|\d{1,3}(?=$)){1,4}$", re.IGNORECASE) + HSV = re.compile(r"^hsva?\s+(?:\d{1,3}[, ](?=\d)|\d{1,3}(?=$)){1,4}$", re.IGNORECASE) + HLS = re.compile(r"^hlsa?\s+(?:\d{1,3}[, ](?=\d)|\d{1,3}(?=$)){1,4}$", re.IGNORECASE) + YIQ = re.compile(r"^yiqa?\s+(?:\d{1,3}[, ](?=\d)|\d{1,3}(?=$)){1,4}$", re.IGNORECASE) + +class color_format_constant(Enum): + HEX,RGB,HSV,HLS,YIQ = range(5) + +C = color_format_constant + +def partition(array, step=2, indices=None): + if not indices: indices = np.array(range(len(array) // step))*2 + return [array[i:i+step] for i in tuple(indices)] + +def ispowof(value:int, exp=2): + return ceil(log(value, 2)) == floor(log(value, 2)) + +def iclamp(value:int, low=0, high=255) -> int: + return int(max(min(value, high), low)) + +def fclamp(value:float, low=0.0, high=1.0) -> float: + return float(max(min(value, high), low)) + +def baser(value:int) -> int: + ''' + Convert a NYBL integer (0~16) into a BYTE integer (0~255) + Absolutely no error proofing whatsoever. + ''' + return int(value * 16 + value) + +def byte(value:float) -> int: + ''' + Convert FLOAT scaler value to BYTE integer (0~255) + Absolutely no error proofing whatsoever. + ''' + return iclamp(round(fclamp(value) * 255)) + +def scale(value: int) -> float: + ''' + Convert a BYTE integer (0~255) to a FLOAT scaler (0.0~1.0) + Absolutely no error proofing whatsoever. + ''' + return iclamp(value) / 255 + +def normalize(nparray, low=0.0, high=1.0): + return np.array([(((i - min(nparray)) * (high - low)) / (max(nparray) - min(nparray))) + low for i in nparray]) + +class Color(): + default = C.HEX + alfault = False + byfault = False + def __init__(self, color="#0", style=None): + if isinstance(color, type(self)): + self.RGBA = color.RGBA + elif isinstance(color, type(tuple)): + match len(color): + case 1: + self.RGBA = color * 4 + case 2: + self.RGBA = (color[0],) * 3 + (color[1],) + case 3: + self.RGBA = color + (1.0,) + case 4: + self.RGBA = color + case _: + self.RGBA = color[:4] + elif isinstance(color, str): + match regex_spm.fullmatch_in(color): + case RE.HEX: + _color = color.lstrip('#') + if not _color: print(f"Unhandled Case - {color}"); self.RGBA = self.fallback() + match len(_color): + case 1: + self.RGBA = (scale(baser(int(_color, 16))),) * 3 + (1.0,) + case 2: + self.RGBA = (scale(int(_color, 16)),) * 3 + (1.0,) + case 3: + self.RGBA = tuple(scale(baser(int(e, 16))) for e in _color) + (1.0,) + case 4: + self.RGBA = tuple(scale(baser(int(e, 16))) for e in _color) + case 6: + # _Color needs to be sliced in 2 char + self.RGBA = tuple(scale(int(e, 16)) for e in partition(_color)) + (1.0,) + case 8: + # _Color needs to be sliced in 2 char + self.RGBA = tuple(scale(int(e, 16)) for e in partition(_color)) + case _: + print(f"Unhandled Case - {color}") + self.RGBA = self.fallback() + case RE.RGB: + _color = re.split(r" |,", color) + print(_color) + match len(_color[1:]): + case 1: + self.RGBA = tuple(scale(_color[1])) * 3 + (1.0,) + case 2: + self.RGBA = tuple(scale(_color[1])) * 3 + tuple(scale(_color[2])) + case 3: + self.RGBA = tuple(scale(int(e)) for e in _color[1:]) + (1.0,) + case 4: + self.RGBA = tuple(scale(int(e)) for e in _color[1:]) + case _: + print(f"Unhandled Case - {color}") + self.RGBA = self.fallback() + case _: + print(f"Unhandled Case - {color}") + return None + else: + print(f"Unhandled Case - {color}") + return None + + def __str__(self, form=default, alpha=alfault, byte=byfault): + _result = None + match form: + case C.RGB: + _result = self.RGBA + case C.HSV: + _result = self.hsv() + case C.HLS: + _result = self.hls() + case C.YIQ: + _result = self.yiq() + case _: + return self.hex() if alpha else self.hex()[:-2] + if byte: _result = (f"{byte(e):3d}" for e in _result) + else: _result = (f"{round(e, 3):.3f}" for e in _result) + if alpha: _result = _result[:3] + return f'({",".join(_result)})' + + def __repr__(self, form=default): + match form: + case C.RGB: + return self.RGBA + case C.HSV: + return self.hsv() + case C.HLS: + return self.hls() + case C.YIQ: + return self.yiq() + case _: + return self.hex() + + def fallback(self): + try: return self.RGBA + except: return (0.0, 0.0, 0.0, 1.0) + + def hex(self): + R, G, B, A = (byte(e) for e in self.RGBA) + return f"#{R:02x}{G:02x}{B:02x}{A:02x}" + + def hls(self): + R, G, B, A = self.RGBA + return rgb_to_hls(R, G, B) + (A,) + + def hsv(self): + R, G, B, A = self.RGBA + return rgb_to_hsv(R, G, B) + (A,) + + def yiq(self): + R, G, B, A = self.RGBA + return rgb_to_yiq(R, G, B) + (A,) \ No newline at end of file diff --git a/_test.py b/_test.py new file mode 100644 index 0000000..da8bc26 --- /dev/null +++ b/_test.py @@ -0,0 +1,4 @@ +import code +from _func import * + +code.interact(local=locals()) \ No newline at end of file diff --git a/nodes.py b/nodes.py index b61991e..e5f3d83 100644 --- a/nodes.py +++ b/nodes.py @@ -1,10 +1,12 @@ -import os, torch +import os, torch, random import numpy as np import comfy.utils +from colorsys import rgb_to_hsv, hsv_to_rgb + from kornia.enhance import equalize_clahe -from ._func import pixel_approx, po2 -# from .isnet import dis_process +from ._func import pixel_approx, po2, Color, byte +from .isnet import dis_process from PIL import Image class EqualizeCLAHE: @@ -13,12 +15,10 @@ class EqualizeCLAHE: return { "required": { "image": ("IMAGE", ), + "size": ("TUPLE", {"default": (1024, 1024)}), "clip_limit": ("FLOAT", {"default": 64, "min": 0.0, "max": 255, "step": 0.1}), "grid_size": ("INT", {"default": 8, "min": 1, "max": 64, "step": 1}), }, - "optional": { - "size": ("TUPLE", {"default": (1024, 1024)}), - } } RETURN_TYPES = ("IMAGE",) FUNCTION = "equalize" @@ -75,12 +75,10 @@ class ImageResize: return { "required": { "image": ("IMAGE",), + "size": ("TUPLE", {"default": (1024, 1024)}), "width": ("INT", {"default": 1024, "min": 512, "max": 4096, "step": 32}), "height": ("INT", {"default": 1024, "min": 512, "max": 4096, "step": 32}), }, - "optional": { - "size": ("TUPLE", {"default": (1024, 1024)}), - } } RETURN_TYPES = ("IMAGE",) @@ -120,6 +118,42 @@ class ImageScale: return (result,) +class RandomColorFill(): + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "image": ("IMAGE",), + "color": ("STRING", {"default": '#7f7f7fff'}), + "hue_range": ("FLOAT", {"default": 0.005}), + "sat_range": ("FLOAT", {"default": 0.005}), + "val_range": ("FLOAT", {"default": 0.005}) + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "randomize" + CATEGORY = "TeaNodes/Input" + + def randomize(self, image, color, hue_range, sat_range, val_range): + h, s, v, _ = Color(color).hsv() + h += hue_range * (random.random() - 0.5) + s += sat_range * (random.random() - 0.5) + v += val_range * (random.random() - 0.5) + + color_rgba = Color() + color_rgba.RGBA = hsv_to_rgb(h, s, v) + (1.0,) + print(f"Random Color:\t{color_rgba.hex()}") + + height, width = image.shape[1:3] + + _color = tuple(byte(e) for e in color_rgba.RGBA) + _image = Image.new('RGBA', (width, height), _color) + _image = np.array(_image.convert('RGBA')).astype(np.float32) / 255.0 + result = torch.from_numpy(_image).unsqueeze(0) + + return (result,) + class ColorFill(): @classmethod def INPUT_TYPES(cls): @@ -192,5 +226,6 @@ NODE_CLASS_MAPPINGS = { "TC_ImageResize": ImageResize, "TC_ImageScale": ImageScale, "TC_ColorFill": ColorFill, - # "TC_MaskBG_DIS": MaskBG_DIS, + "TC_RandomColorFill": RandomColorFill, + "TC_MaskBG_DIS": MaskBG_DIS, } \ No newline at end of file