621 lines
23 KiB
Python
621 lines
23 KiB
Python
""" Jovimetrix - Adjust """
|
|
|
|
from enum import Enum
|
|
|
|
import cv2
|
|
|
|
from comfy.utils import ProgressBar
|
|
|
|
from cozy_comfyui import \
|
|
InputType, RGBAMaskType, EnumConvertType, \
|
|
deep_merge, parse_param, zip_longest_fill
|
|
|
|
from cozy_comfyui.lexicon import \
|
|
Lexicon
|
|
|
|
from cozy_comfyui.node import \
|
|
COZY_TYPE_IMAGE, \
|
|
CozyImageNode
|
|
|
|
# EnumThreshold, EnumThresholdAdapt, image_filter, image_threshold,
|
|
|
|
from cozy_comfyui.image.adjust import \
|
|
image_contrast, image_brightness, image_equalize, image_gamma, \
|
|
image_hsv, image_invert, image_pixelate, image_posterize, \
|
|
image_quantize, image_sharpen, image_edge_detect, image_emboss
|
|
|
|
from cozy_comfyui.image.channel import \
|
|
channel_solid
|
|
|
|
from cozy_comfyui.image.compose import \
|
|
image_levels, image_blend
|
|
|
|
from cozy_comfyui.image.convert import \
|
|
tensor_to_cv, cv_to_tensor_full
|
|
|
|
from cozy_comfyui.image.mask import \
|
|
image_mask
|
|
|
|
from cozy_comfyui.image.misc import \
|
|
image_stack
|
|
|
|
# ==============================================================================
|
|
# === GLOBAL ===
|
|
# ==============================================================================
|
|
|
|
JOV_CATEGORY = "ADJUST"
|
|
|
|
# ==============================================================================
|
|
# === ENUMERATION ===
|
|
# ==============================================================================
|
|
|
|
class EnumAdjustBlur(Enum):
|
|
BLUR = 0
|
|
STACK_BLUR = 1
|
|
GAUSSIAN_BLUR = 2
|
|
MEDIAN_BLUR = 3
|
|
|
|
class EnumAdjustEdge(Enum):
|
|
DETECT = 10
|
|
CANNY = 20
|
|
LAPLACIAN = 30
|
|
SOBEL = 40
|
|
PREWITT = 50
|
|
SCHARR = 60
|
|
|
|
class EnumAdjustEnhance(Enum):
|
|
SHARPEN = 10
|
|
EMBOSS = 20
|
|
OUTLINE = 30
|
|
|
|
class EnumAdjustMorpho(Enum):
|
|
DILATE = 10
|
|
ERODE = 20
|
|
OPEN = 30
|
|
CLOSE = 40
|
|
TOPHAT = 50
|
|
BLACKHAT = 60
|
|
GRADIENT = 70
|
|
|
|
class EnumAdjustLight(Enum):
|
|
EXPOSURE = 10
|
|
GAMMA = 20
|
|
BRIGHTNESS = 30
|
|
CONTRAST = 40
|
|
EQUALIZE = 50
|
|
|
|
class EnumAdjustPixel(Enum):
|
|
PIXELATE = 10
|
|
QUANTIZE = 20
|
|
POSTERIZE = 30
|
|
|
|
# ==============================================================================
|
|
# === CLASS ===
|
|
# ==============================================================================
|
|
|
|
'''
|
|
class AdjustNode(CozyImageNode):
|
|
NAME = "ADJUST (JOV) 🕸️"
|
|
CATEGORY = JOV_CATEGORY
|
|
DESCRIPTION = """
|
|
Enhance and modify images with various effects such as blurring, sharpening, color tweaks, and edge detection. Customize parameters like radius, value, and contrast, and use masks for selective effects.
|
|
|
|
Advanced options include pixelation, quantization, and morphological operations like dilation and erosion. Handle transparency effortlessly to ensure seamless blending of effects. This node is ideal for simple adjustments and complex image transformations.
|
|
"""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls) -> InputType:
|
|
d = super().INPUT_TYPES()
|
|
d = deep_merge(d, {
|
|
"optional": {
|
|
Lexicon.IMAGE: (COZY_TYPE_IMAGE, {}),
|
|
Lexicon.MASK: (COZY_TYPE_IMAGE, {}),
|
|
Lexicon.FUNCTION: (EnumAdjustOP._member_names_, {
|
|
"default": EnumAdjustOP.BLUR.name,}),
|
|
Lexicon.RADIUS: ("INT", {
|
|
"default": 3, "min": 3}),
|
|
Lexicon.VALUE: ("FLOAT", {
|
|
"default": 1, "min": 0, "step": 0.01}),
|
|
Lexicon.EDGE: ("VEC2", {
|
|
"default": (0, 1), "mij": 0, "maj": 1,
|
|
"label": ["Low", "HI"]}),
|
|
Lexicon.LEVEL: ("VEC3", {
|
|
"default": (0, 0.5, 1), "mij": 0, "maj": 1,
|
|
"label": ["Low", "MID", "HI"],}),
|
|
Lexicon.HSV: ("VEC3",{
|
|
"default": (0, 1, 1), "mij": 0, "maj": 1,
|
|
"label": ["H", "S", "V"],}),
|
|
Lexicon.CONTRAST: ("FLOAT", {
|
|
"default": 0, "min": 0, "max": 1, "step": 0.01}),
|
|
Lexicon.GAMMA: ("FLOAT", {
|
|
"default": 1, "min": 0.00001, "max": 100, "step": 0.1}),
|
|
Lexicon.MATTE: ("VEC4", {
|
|
"default": (0, 0, 0, 255), "rgb": True,}),
|
|
Lexicon.INVERT: ("BOOLEAN", {
|
|
"default": False,
|
|
"tooltip": "Invert the mask input"})
|
|
}
|
|
})
|
|
return Lexicon._parse(d)
|
|
|
|
def run(self, **kw) -> RGBAMaskType:
|
|
pA = parse_param(kw, Lexicon.IMAGE, EnumConvertType.IMAGE, None)
|
|
mask = parse_param(kw, Lexicon.MASK, EnumConvertType.MASK, None)
|
|
op = parse_param(kw, Lexicon.FUNCTION, EnumAdjustOP, EnumAdjustOP.BLUR.name)
|
|
radius = parse_param(kw, Lexicon.RADIUS, EnumConvertType.INT, 3, 3)
|
|
val = parse_param(kw, Lexicon.VALUE, EnumConvertType.FLOAT, 0, 0)
|
|
|
|
edges = parse_param(kw, Lexicon.EDGE, EnumConvertType.VEC2, (0, 1), 0, 1)
|
|
level = parse_param(kw, Lexicon.LMH, EnumConvertType.VEC3, (0, 0.5, 1), 0, 1)
|
|
equalize = parse_param(kw, Lexicon.EQUALIZE, EnumConvertType.BOOLEAN, False)
|
|
hsv = parse_param(kw, Lexicon.HSV, EnumConvertType.VEC3, (0, 1, 1), 0, 1)
|
|
contrast = parse_param(kw, Lexicon.CONTRAST, EnumConvertType.FLOAT, 1, 0, 1)
|
|
gamma = parse_param(kw, Lexicon.GAMMA, EnumConvertType.FLOAT, 1, 0, 100)
|
|
matte = parse_param(kw, Lexicon.MATTE, EnumConvertType.VEC4INT, (0, 0, 0, 255), 0, 255)
|
|
invert = parse_param(kw, Lexicon.INVERT, EnumConvertType.BOOLEAN, False)
|
|
|
|
params = list(zip_longest_fill(pA, mask, op, radius, val, edges, level, equalize, hsv, contrast, gamma, matte, invert))
|
|
images = []
|
|
pbar = ProgressBar(len(params))
|
|
for idx, (pA, mask, op, radius, val, edges, level, equalize, hsv, contrast, gamma, matte, invert) in enumerate(params):
|
|
pA = tensor_to_cv(pA) if pA is not None else channel_solid()
|
|
img_new = image_convert(pA, 3)
|
|
|
|
match op:
|
|
case EnumAdjustOP.BLUR:
|
|
img_new = cv2.blur(img_new, (radius, radius))
|
|
|
|
case EnumAdjustOP.STACK_BLUR:
|
|
r = min(radius, 1399)
|
|
if r % 2 == 0:
|
|
r += 1
|
|
img_new = cv2.stackBlur(img_new, (r, r))
|
|
|
|
case EnumAdjustOP.GAUSSIAN_BLUR:
|
|
r = min(radius, 999)
|
|
if r % 2 == 0:
|
|
r += 1
|
|
img_new = cv2.GaussianBlur(img_new, (r, r), sigmaX=val)
|
|
|
|
case EnumAdjustOP.MEDIAN_BLUR:
|
|
r = min(radius, 357)
|
|
if r % 2 == 0:
|
|
r += 1
|
|
img_new = cv2.medianBlur(img_new, r)
|
|
|
|
case EnumAdjustOP.SHARPEN:
|
|
r = min(radius, 511)
|
|
if r % 2 == 0:
|
|
r += 1
|
|
img_new = image_sharpen(img_new, kernel_size=r, amount=val)
|
|
|
|
case EnumAdjustOP.EMBOSS:
|
|
img_new = morph_emboss(img_new, val, radius)
|
|
|
|
case EnumAdjustOP.PIXELATE:
|
|
img_new = image_pixelate(img_new, val / 255.)
|
|
|
|
case EnumAdjustOP.QUANTIZE:
|
|
img_new = image_quantize(img_new, int(val))
|
|
|
|
case EnumAdjustOP.POSTERIZE:
|
|
img_new = image_posterize(img_new, int(val))
|
|
|
|
case EnumAdjustOP.OUTLINE:
|
|
img_new = cv2.morphologyEx(img_new, cv2.MORPH_GRADIENT, (radius, radius))
|
|
|
|
case EnumAdjustOP.DILATE:
|
|
img_new = cv2.dilate(img_new, (radius, radius), iterations=int(val))
|
|
|
|
case EnumAdjustOP.ERODE:
|
|
img_new = cv2.erode(img_new, (radius, radius), iterations=int(val))
|
|
|
|
case EnumAdjustOP.OPEN:
|
|
img_new = cv2.morphologyEx(img_new, cv2.MORPH_OPEN, (radius, radius), iterations=int(val))
|
|
|
|
case EnumAdjustOP.CLOSE:
|
|
img_new = cv2.morphologyEx(img_new, cv2.MORPH_CLOSE, (radius, radius), iterations=int(val))
|
|
|
|
h, s, v = hsv
|
|
img_new = image_hsv(img_new, h, s, v)
|
|
|
|
lo, hi = edges
|
|
img_new = morph_edge_detect(img_new, low=lo, high=hi)
|
|
|
|
if equalize:
|
|
img_new = image_equalize(img_new)
|
|
|
|
l, m, h = level
|
|
img_new = image_levels(img_new, l, h, m, gamma)
|
|
|
|
if contrast != 0:
|
|
img_new = image_contrast(img_new, contrast)
|
|
|
|
if gamma != 0:
|
|
img_new = image_gamma(img_new, gamma)
|
|
|
|
if invert:
|
|
img_new = image_invert(img_new, val)
|
|
|
|
if mask is not None:
|
|
mask = tensor_to_cv(mask)
|
|
|
|
img_new = image_blend(pA, img_new, mask)
|
|
if pA.ndim == 3 and pA.shape[2] == 4:
|
|
mask = image_mask(pA)
|
|
img_new = image_convert(img_new, 4)
|
|
img_new[:,:,3] = mask
|
|
|
|
images.append(cv_to_tensor_full(img_new, matte))
|
|
pbar.update_absolute(idx)
|
|
return image_stack(images)
|
|
'''
|
|
|
|
class AdjustBlurNode(CozyImageNode):
|
|
NAME = "BLUR (JOV)"
|
|
CATEGORY = JOV_CATEGORY
|
|
DESCRIPTION = """
|
|
Enhance and modify images with various blur effects.
|
|
"""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls) -> InputType:
|
|
d = super().INPUT_TYPES()
|
|
d = deep_merge(d, {
|
|
"optional": {
|
|
Lexicon.IMAGE: (COZY_TYPE_IMAGE, {}),
|
|
Lexicon.MASK: (COZY_TYPE_IMAGE, {}),
|
|
Lexicon.FUNCTION: (EnumAdjustBlur._member_names_, {
|
|
"default": EnumAdjustBlur.BLUR.name,}),
|
|
Lexicon.RADIUS: ("INT", {
|
|
"default": 3, "min": 3}),
|
|
}
|
|
})
|
|
return Lexicon._parse(d)
|
|
|
|
def run(self, **kw) -> RGBAMaskType:
|
|
pA = parse_param(kw, Lexicon.IMAGE, EnumConvertType.IMAGE, None)
|
|
mask = parse_param(kw, Lexicon.MASK, EnumConvertType.MASK, None)
|
|
op = parse_param(kw, Lexicon.FUNCTION, EnumAdjustBlur, EnumAdjustBlur.BLUR.name)
|
|
radius = parse_param(kw, Lexicon.RADIUS, EnumConvertType.INT, 0, 0)
|
|
params = list(zip_longest_fill(pA, mask, op, radius))
|
|
images = []
|
|
pbar = ProgressBar(len(params))
|
|
for idx, (pA, mask, op, radius) in enumerate(params):
|
|
pA = channel_solid() if pA is None else tensor_to_cv(pA)
|
|
height, width = pA.shape[:2]
|
|
mask = channel_solid(width, height, (255,255,255,255)) if mask is None else tensor_to_cv(mask)
|
|
if radius % 2 == 0:
|
|
radius += 1
|
|
|
|
match op:
|
|
case EnumAdjustBlur.BLUR:
|
|
img_new = cv2.blur(pA, (radius, radius))
|
|
|
|
case EnumAdjustBlur.STACK_BLUR:
|
|
img_new = cv2.stackBlur(pA, (radius, radius))
|
|
|
|
case EnumAdjustBlur.GAUSSIAN_BLUR:
|
|
img_new = cv2.GaussianBlur(pA, (radius, radius))
|
|
|
|
case EnumAdjustBlur.MEDIAN_BLUR:
|
|
img_new = cv2.medianBlur(pA, radius)
|
|
|
|
pA = image_blend(pA, img_new, mask)
|
|
images.append(cv_to_tensor_full(pA))
|
|
pbar.update_absolute(idx)
|
|
return image_stack(images)
|
|
|
|
class AdjustEdgeNode(CozyImageNode):
|
|
NAME = "EDGE (JOV)"
|
|
CATEGORY = JOV_CATEGORY
|
|
DESCRIPTION = """
|
|
Enhanced edge detection.
|
|
"""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls) -> InputType:
|
|
d = super().INPUT_TYPES()
|
|
d = deep_merge(d, {
|
|
"optional": {
|
|
Lexicon.IMAGE: (COZY_TYPE_IMAGE, {}),
|
|
Lexicon.MASK: (COZY_TYPE_IMAGE, {}),
|
|
Lexicon.FUNCTION: (EnumAdjustEdge._member_names_, {
|
|
"default": EnumAdjustEdge.DETECT.name,}),
|
|
Lexicon.RADIUS: ("INT", {
|
|
"default": 3, "min": 3}),
|
|
Lexicon.ITERATION: ("INT", {
|
|
"default": 1, "min": 1, "max": 1000}),
|
|
Lexicon.LOHI: ("VEC2", {
|
|
"default": (0, 1), "mij": 0, "maj": 1., "step": 0.01})
|
|
}
|
|
})
|
|
return Lexicon._parse(d)
|
|
|
|
def run(self, **kw) -> RGBAMaskType:
|
|
pA = parse_param(kw, Lexicon.IMAGE, EnumConvertType.IMAGE, None)
|
|
mask = parse_param(kw, Lexicon.MASK, EnumConvertType.MASK, None)
|
|
op = parse_param(kw, Lexicon.FUNCTION, EnumAdjustEdge, EnumAdjustEdge.DETECT.name)
|
|
radius = parse_param(kw, Lexicon.RADIUS, EnumConvertType.INT, 3, 3)
|
|
count = parse_param(kw, Lexicon.ITERATION, EnumConvertType.INT, 1, 1, 1000)
|
|
lohi = parse_param(kw, Lexicon.LOHI, EnumConvertType.VEC2, 0, 0, 1)
|
|
params = list(zip_longest_fill(pA, mask, op, radius, count, lohi))
|
|
images = []
|
|
pbar = ProgressBar(len(params))
|
|
for idx, (pA, mask, op, radius, count, lohi) in enumerate(params):
|
|
pA = channel_solid() if pA is None else tensor_to_cv(pA)
|
|
alpha = image_mask(pA)
|
|
height, width = pA.shape[:2]
|
|
mask = channel_solid(width, height, (255,255,255,255)) if mask is None else tensor_to_cv(mask, chan=1)
|
|
|
|
if radius % 2 == 0:
|
|
radius += 1
|
|
|
|
match op:
|
|
case EnumAdjustEdge.DETECT:
|
|
lo, hi = lohi
|
|
img_new = image_edge_detect(pA, radius, low=lo, high=hi)
|
|
|
|
case EnumAdjustEdge.CANNY:
|
|
img_new = image_sharpen(pA, radius, amount=count)
|
|
|
|
case EnumAdjustEdge.LAPLACIAN:
|
|
img_new = image_emboss(pA, count, radius)
|
|
|
|
case EnumAdjustEdge.SOBEL:
|
|
img_new = cv2.dilate(pA, (radius, radius), iterations=count)
|
|
|
|
case EnumAdjustEdge.PREWITT:
|
|
img_new = cv2.erode(pA, (radius, radius), iterations=count)
|
|
|
|
case EnumAdjustEdge.SCHARR:
|
|
img_new = cv2.morphologyEx(pA, cv2.MORPH_OPEN, (radius, radius), iterations=count)
|
|
|
|
pA = image_blend(pA, img_new, mask)
|
|
images.append(cv_to_tensor_full(pA))
|
|
pbar.update_absolute(idx)
|
|
return image_stack(images)
|
|
|
|
class AdjustEnhanceNode(CozyImageNode):
|
|
NAME = "ENHANCE (JOV)"
|
|
CATEGORY = JOV_CATEGORY
|
|
DESCRIPTION = """
|
|
Enhanced edge detection.
|
|
"""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls) -> InputType:
|
|
d = super().INPUT_TYPES()
|
|
d = deep_merge(d, {
|
|
"optional": {
|
|
Lexicon.IMAGE: (COZY_TYPE_IMAGE, {}),
|
|
Lexicon.MASK: (COZY_TYPE_IMAGE, {}),
|
|
Lexicon.FUNCTION: (EnumAdjustEdge._member_names_, {
|
|
"default": EnumAdjustEdge.DETECT.name,}),
|
|
Lexicon.RADIUS: ("INT", {
|
|
"default": 3, "min": 3}),
|
|
Lexicon.ITERATION: ("INT", {
|
|
"default": 1, "min": 1, "max": 1000}),
|
|
Lexicon.LOHI: ("VEC2", {
|
|
"default": (0, 1), "mij": 0, "maj": 1., "step": 0.01})
|
|
}
|
|
})
|
|
return Lexicon._parse(d)
|
|
|
|
def run(self, **kw) -> RGBAMaskType:
|
|
pA = parse_param(kw, Lexicon.IMAGE, EnumConvertType.IMAGE, None)
|
|
mask = parse_param(kw, Lexicon.MASK, EnumConvertType.MASK, None)
|
|
op = parse_param(kw, Lexicon.FUNCTION, EnumAdjustEdge, EnumAdjustEdge.DETECT.name)
|
|
radius = parse_param(kw, Lexicon.RADIUS, EnumConvertType.INT, 3, 3)
|
|
count = parse_param(kw, Lexicon.ITERATION, EnumConvertType.INT, 1, 1, 1000)
|
|
lohi = parse_param(kw, Lexicon.LOHI, EnumConvertType.VEC2, 0, 0, 1)
|
|
params = list(zip_longest_fill(pA, mask, op, radius, count, lohi))
|
|
images = []
|
|
pbar = ProgressBar(len(params))
|
|
for idx, (pA, mask, op, radius, count, lohi) in enumerate(params):
|
|
pA = channel_solid() if pA is None else tensor_to_cv(pA)
|
|
height, width = pA.shape[:2]
|
|
mask = channel_solid(width, height, (255,255,255,255)) if mask is None else tensor_to_cv(mask)
|
|
|
|
if radius % 2 == 0:
|
|
radius += 1
|
|
|
|
match op:
|
|
case EnumAdjustEdge.DETECT:
|
|
lo, hi = lohi
|
|
img_new = image_edge_detect(pA, low=lo, high=hi)
|
|
|
|
case EnumAdjustEdge.SHARPEN:
|
|
img_new = image_sharpen(pA, radius, amount=count)
|
|
|
|
case EnumAdjustEdge.EMBOSS:
|
|
img_new = image_emboss(pA, count, radius)
|
|
|
|
case EnumAdjustEdge.DILATE:
|
|
img_new = cv2.dilate(pA, (radius, radius), iterations=count)
|
|
|
|
case EnumAdjustEdge.ERODE:
|
|
img_new = cv2.erode(pA, (radius, radius), iterations=count)
|
|
|
|
case EnumAdjustEdge.OPEN:
|
|
img_new = cv2.morphologyEx(pA, cv2.MORPH_OPEN, (radius, radius), iterations=count)
|
|
|
|
case EnumAdjustEdge.CLOSE:
|
|
img_new = cv2.morphologyEx(pA, cv2.MORPH_CLOSE, (radius, radius), iterations=count)
|
|
|
|
case EnumAdjustEdge.OUTLINE:
|
|
img_new = cv2.morphologyEx(pA, cv2.MORPH_GRADIENT, (radius, radius), iterations=count)
|
|
|
|
pA = image_blend(pA, img_new, mask)
|
|
images.append(cv_to_tensor_full(pA))
|
|
pbar.update_absolute(idx)
|
|
return image_stack(images)
|
|
|
|
class AdjustLevelNode(CozyImageNode):
|
|
NAME = "LEVELS (JOV)"
|
|
CATEGORY = JOV_CATEGORY
|
|
DESCRIPTION = """
|
|
|
|
"""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls) -> InputType:
|
|
d = super().INPUT_TYPES()
|
|
d = deep_merge(d, {
|
|
"optional": {
|
|
Lexicon.IMAGE: (COZY_TYPE_IMAGE, {}),
|
|
Lexicon.MASK: (COZY_TYPE_IMAGE, {}),
|
|
Lexicon.LMH: ("VEC3", {
|
|
"default": (0,0.5,1), "mij": 0, "maj": 1., "step": 0.01,
|
|
"label": ["LOW", "MID", "HIGH"]}),
|
|
Lexicon.RANGE: ("VEC2", {
|
|
"default": (0, 1), "mij": 0, "maj": 1., "step": 0.01,
|
|
"label": ["IN", "OUT"]})
|
|
}
|
|
})
|
|
return Lexicon._parse(d)
|
|
|
|
def run(self, **kw) -> RGBAMaskType:
|
|
pA = parse_param(kw, Lexicon.IMAGE, EnumConvertType.IMAGE, None)
|
|
mask = parse_param(kw, Lexicon.MASK, EnumConvertType.MASK, None)
|
|
LMH = parse_param(kw, Lexicon.LMH, EnumConvertType.VEC3, (0,0.5,1))
|
|
inout = parse_param(kw, Lexicon.RANGE, EnumConvertType.VEC2, (0,1))
|
|
|
|
params = list(zip_longest_fill(pA, mask, LMH, inout))
|
|
images = []
|
|
pbar = ProgressBar(len(params))
|
|
for idx, (pA, mask, LMH, inout) in enumerate(params):
|
|
pA = channel_solid() if pA is None else tensor_to_cv(pA)
|
|
height, width = pA.shape[:2]
|
|
mask = channel_solid(width, height, 255) if mask is None else tensor_to_cv(mask)
|
|
|
|
'''
|
|
h, s, v = hsv
|
|
img_new = image_hsv(img_new, h, s, v)
|
|
'''
|
|
low, mid, high = LMH
|
|
start, end = inout
|
|
# mid = min(high, max(mid, low))
|
|
pA = image_levels(pA, low, mid, high, start, end)
|
|
#print(pA.shape, img_new.shape, mask.shape)
|
|
|
|
#pA = image_blend(pA, img_new, mask)
|
|
images.append(cv_to_tensor_full(pA))
|
|
pbar.update_absolute(idx)
|
|
return image_stack(images)
|
|
|
|
class AdjustLightNode(CozyImageNode):
|
|
NAME = "LIGHT (JOV)"
|
|
CATEGORY = JOV_CATEGORY
|
|
DESCRIPTION = """
|
|
|
|
"""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls) -> InputType:
|
|
d = super().INPUT_TYPES()
|
|
d = deep_merge(d, {
|
|
"optional": {
|
|
Lexicon.IMAGE: (COZY_TYPE_IMAGE, {}),
|
|
Lexicon.MASK: (COZY_TYPE_IMAGE, {}),
|
|
Lexicon.FUNCTION: (EnumAdjustLight._member_names_, {
|
|
"default": EnumAdjustLight.CONTRAST.name,}),
|
|
Lexicon.VALUE: ("FLOAT", {
|
|
"default": 0, "min": -1, "max": 1, "step": 0.001}),
|
|
}
|
|
})
|
|
return Lexicon._parse(d)
|
|
|
|
def run(self, **kw) -> RGBAMaskType:
|
|
pA = parse_param(kw, Lexicon.IMAGE, EnumConvertType.IMAGE, None)
|
|
mask = parse_param(kw, Lexicon.MASK, EnumConvertType.MASK, None)
|
|
op = parse_param(kw, Lexicon.FUNCTION, EnumAdjustLight, EnumAdjustLight.CONTRAST.name)
|
|
val = parse_param(kw, Lexicon.VALUE, EnumConvertType.FLOAT, 0)
|
|
|
|
params = list(zip_longest_fill(pA, mask, op, val))
|
|
images = []
|
|
pbar = ProgressBar(len(params))
|
|
for idx, (pA, mask, op, val) in enumerate(params):
|
|
pA = channel_solid() if pA is None else tensor_to_cv(pA)
|
|
height, width = pA.shape[:2]
|
|
mask = channel_solid(width, height, (255,255,255,255)) if mask is None else tensor_to_cv(mask)
|
|
|
|
match op:
|
|
case EnumAdjustLight.BRIGHTNESS:
|
|
img_new = image_contrast(pA, val)
|
|
|
|
case EnumAdjustLight.CONTRAST:
|
|
img_new = image_contrast(pA, val)
|
|
|
|
case EnumAdjustLight.EQUALIZE:
|
|
img_new = image_equalize(pA)
|
|
|
|
case EnumAdjustLight.EXPOSURE:
|
|
img_new = image_contrast(pA, val)
|
|
|
|
case EnumAdjustLight.GAMMA:
|
|
img_new = image_gamma(pA, val)
|
|
|
|
'''
|
|
h, s, v = hsv
|
|
img_new = image_hsv(img_new, h, s, v)
|
|
|
|
l, m, h = level
|
|
img_new = image_levels(img_new, l, h, m, gamma)
|
|
'''
|
|
|
|
pA = image_blend(pA, img_new, mask)
|
|
images.append(cv_to_tensor_full(pA))
|
|
pbar.update_absolute(idx)
|
|
return image_stack(images)
|
|
|
|
class AdjustPixelNode(CozyImageNode):
|
|
NAME = "PIXEL (JOV)"
|
|
CATEGORY = JOV_CATEGORY
|
|
DESCRIPTION = """
|
|
|
|
"""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls) -> InputType:
|
|
d = super().INPUT_TYPES()
|
|
d = deep_merge(d, {
|
|
"optional": {
|
|
Lexicon.IMAGE: (COZY_TYPE_IMAGE, {}),
|
|
Lexicon.MASK: (COZY_TYPE_IMAGE, {}),
|
|
Lexicon.FUNCTION: (EnumAdjustPixel._member_names_, {
|
|
"default": EnumAdjustPixel.PIXELATE.name,}),
|
|
Lexicon.VALUE: ("INT", {
|
|
"default": 1, "min": 0, "max": 255, "step": 1}),
|
|
}
|
|
})
|
|
return Lexicon._parse(d)
|
|
|
|
def run(self, **kw) -> RGBAMaskType:
|
|
pA = parse_param(kw, Lexicon.IMAGE, EnumConvertType.IMAGE, None)
|
|
mask = parse_param(kw, Lexicon.MASK, EnumConvertType.MASK, None)
|
|
op = parse_param(kw, Lexicon.FUNCTION, EnumAdjustPixel, EnumAdjustPixel.PIXELATE.name)
|
|
val = parse_param(kw, Lexicon.VALUE, EnumConvertType.INT, 0, 255)
|
|
params = list(zip_longest_fill(pA, mask, op, val))
|
|
images = []
|
|
pbar = ProgressBar(len(params))
|
|
for idx, (pA, mask, op, val) in enumerate(params):
|
|
pA = channel_solid() if pA is None else tensor_to_cv(pA)
|
|
height, width = pA.shape[:2]
|
|
mask = channel_solid(width, height, (255,255,255,255)) if mask is None else tensor_to_cv(mask)
|
|
|
|
match op:
|
|
case EnumAdjustPixel.PIXELATE:
|
|
img_new = image_pixelate(pA, val / 255.)
|
|
|
|
case EnumAdjustPixel.QUANTIZE:
|
|
img_new = image_quantize(pA, val)
|
|
|
|
case EnumAdjustPixel.POSTERIZE:
|
|
img_new = image_posterize(pA, val)
|
|
|
|
pA = image_blend(pA, img_new, mask)
|
|
images.append(cv_to_tensor_full(pA))
|
|
pbar.update_absolute(idx)
|
|
return image_stack(images)
|