two modes
* histogram, has a clip value 0->1. Default is 0.5. * auto == linear auto-level (which is equivalent to a histrogram with clip value 1.0)
This commit is contained in:
@@ -138,6 +138,11 @@ Nodes that have been migrated:
|
||||
|
||||
[Migrated to Jovi_GLSL](https://github.com/Amorano/Jovi_GLSL)
|
||||
|
||||
**2025/09/04** @2.1.24:
|
||||
* `AUTO LEVEL` node
|
||||
* `HISTOGRAM` node
|
||||
* new support for cozy_comfy (v3+ comfy node spec)
|
||||
|
||||
**2025/08/15** @2.1.23:
|
||||
* fixed regression in `FLATTEN` node
|
||||
|
||||
|
||||
+63
-1
@@ -4,6 +4,8 @@ import sys
|
||||
from enum import Enum
|
||||
from typing import Any
|
||||
|
||||
import comfy.model_management
|
||||
from comfy_api.latest import ComfyExtension, io
|
||||
from comfy.utils import ProgressBar
|
||||
|
||||
from cozy_comfyui import \
|
||||
@@ -13,6 +15,10 @@ from cozy_comfyui import \
|
||||
from cozy_comfyui.lexicon import \
|
||||
Lexicon
|
||||
|
||||
from cozy_comfy.node import \
|
||||
COZY_TYPE_IMAGE as COZY_TYPE_IMAGEv3, \
|
||||
CozyImageNode as CozyImageNodev3
|
||||
|
||||
from cozy_comfyui.node import \
|
||||
COZY_TYPE_IMAGE, \
|
||||
CozyImageNode
|
||||
@@ -20,7 +26,7 @@ from cozy_comfyui.node import \
|
||||
from cozy_comfyui.image.adjust import \
|
||||
EnumAdjustBlur, EnumAdjustColor, EnumAdjustEdge, EnumAdjustMorpho, \
|
||||
image_contrast, image_brightness, image_equalize, image_gamma, \
|
||||
image_exposure, image_hsv, image_invert, image_pixelate, image_pixelscale, \
|
||||
image_exposure, image_pixelate, image_pixelscale, \
|
||||
image_posterize, image_quantize, image_sharpen, image_morphology, \
|
||||
image_emboss, image_blur, image_edge, image_color
|
||||
|
||||
@@ -455,3 +461,59 @@ Sharpen the pixels of an image.
|
||||
images.append(cv_to_tensor_full(pA))
|
||||
pbar.update_absolute(idx)
|
||||
return image_stack(images)
|
||||
|
||||
class AdjustSharpenNodev3(CozyImageNodev3):
|
||||
NAME = "ADJUST: SHARPEN (JOV)"
|
||||
CATEGORY = JOV_CATEGORY
|
||||
DESCRIPTION = """
|
||||
Sharpen the pixels of an image.
|
||||
"""
|
||||
@classmethod
|
||||
def define_schema(cls, **kwarg) -> io.Schema:
|
||||
schema = super(**kwarg).define_schema()
|
||||
# schema.
|
||||
schema.inputs.extend([
|
||||
io.MultiType.Input(
|
||||
id=Lexicon.IMAGE,
|
||||
types=COZY_TYPE_IMAGEv3,
|
||||
display_name=Lexicon.IMAGE,
|
||||
optional=True,
|
||||
tooltip=Lexicon.IMAGE[1]
|
||||
),
|
||||
io.Float.Input(
|
||||
id=Lexicon.AMOUNT,
|
||||
display_name=Lexicon.AMOUNT,
|
||||
optional=True,
|
||||
default= 0,
|
||||
min=0,
|
||||
max=1,
|
||||
step=0.01,
|
||||
tooltip=Lexicon.AMOUNT[1]
|
||||
),
|
||||
io.Float.Input(
|
||||
id=Lexicon.THRESHOLD,
|
||||
display_name=Lexicon.THRESHOLD,
|
||||
optional=True,
|
||||
default= 0,
|
||||
min=0,
|
||||
max=1,
|
||||
step=0.01,
|
||||
tooltip=Lexicon.THRESHOLD[1]
|
||||
)
|
||||
|
||||
])
|
||||
return schema
|
||||
|
||||
def run(self, **kw) -> RGBAMaskType:
|
||||
pA = parse_param(kw, Lexicon.IMAGE, EnumConvertType.IMAGE, None)
|
||||
amount = parse_param(kw, Lexicon.AMOUNT, EnumConvertType.FLOAT, 0)
|
||||
threshold = parse_param(kw, Lexicon.THRESHOLD, EnumConvertType.FLOAT, 0)
|
||||
params = list(zip_longest_fill(pA, amount, threshold))
|
||||
images = []
|
||||
pbar = ProgressBar(len(params))
|
||||
for idx, (pA, amount, threshold) in enumerate(params):
|
||||
pA = channel_solid() if pA is None else tensor_to_cv(pA)
|
||||
pA = image_sharpen(pA, amount / 2., threshold=threshold / 25.5)
|
||||
images.append(cv_to_tensor_full(pA))
|
||||
pbar.update_absolute(idx)
|
||||
return image_stack(images)
|
||||
|
||||
+97
-35
@@ -1,5 +1,7 @@
|
||||
""" Jovimetrix - Composition """
|
||||
|
||||
from enum import Enum
|
||||
|
||||
import numpy as np
|
||||
|
||||
from comfy.utils import ProgressBar
|
||||
@@ -21,7 +23,8 @@ from cozy_comfyui.image import \
|
||||
|
||||
from cozy_comfyui.image.adjust import \
|
||||
EnumThreshold, EnumThresholdAdapt, \
|
||||
image_invert, image_filter, image_threshold
|
||||
image_histogram2, image_invert, image_filter, image_threshold, \
|
||||
image_autolevel, image_autolevel_histogram
|
||||
|
||||
from cozy_comfyui.image.channel import \
|
||||
EnumPixelSwizzle, \
|
||||
@@ -29,6 +32,7 @@ from cozy_comfyui.image.channel import \
|
||||
|
||||
from cozy_comfyui.image.compose import \
|
||||
EnumBlendType, EnumScaleMode, EnumScaleInputMode, EnumInterpolation, \
|
||||
image_resize, \
|
||||
image_scalefit, image_split, image_blend, image_matte
|
||||
|
||||
from cozy_comfyui.image.convert import \
|
||||
@@ -43,10 +47,66 @@ from cozy_comfyui.image.misc import \
|
||||
|
||||
JOV_CATEGORY = "COMPOSE"
|
||||
|
||||
# ==============================================================================
|
||||
# === ENUMERATION ===
|
||||
# ==============================================================================
|
||||
|
||||
class EnumAutoLevel(Enum):
|
||||
AUTO = 10
|
||||
HISTOGRAM = 20
|
||||
|
||||
# ==============================================================================
|
||||
# === CLASS ===
|
||||
# ==============================================================================
|
||||
|
||||
class AutoLevelNode(CozyImageNode):
|
||||
NAME = "AUTO LEVEL (JOV)"
|
||||
CATEGORY = JOV_CATEGORY
|
||||
DESCRIPTION = """
|
||||
Automatically adjusts image levels so that the darkest pixel becomes black
|
||||
and the brightest pixel becomes white, enhancing overall contrast.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls) -> InputType:
|
||||
d = super().INPUT_TYPES()
|
||||
d = deep_merge(d, {
|
||||
"required": {
|
||||
Lexicon.IMAGE: (COZY_TYPE_IMAGE, {
|
||||
"tooltip": "Pixel Data (RGBA, RGB, or Grayscale)"
|
||||
}),
|
||||
Lexicon.MODE: (EnumAutoLevel._member_names_, {
|
||||
"default": EnumAutoLevel.AUTO.name,
|
||||
"tooltip": "Autolevel linearly or with Histogram bin values, per channel"
|
||||
}),
|
||||
"clip": ("FLOAT", {
|
||||
"default": 0.5, "min": 0, "max": 1.0, "step": 0.01
|
||||
})
|
||||
}
|
||||
})
|
||||
return Lexicon._parse(d)
|
||||
|
||||
def run(self, **kw) -> RGBAMaskType:
|
||||
pA = parse_param(kw, Lexicon.IMAGE, EnumConvertType.IMAGE, None)
|
||||
mode = parse_param(kw, Lexicon.MODE, EnumAutoLevel, EnumAutoLevel.AUTO.name)
|
||||
clip = parse_param(kw, "clip", EnumConvertType.FLOAT, 0.5, 0, 1)
|
||||
|
||||
params = list(zip_longest_fill(pA, mode, clip))
|
||||
images = []
|
||||
pbar = ProgressBar(len(params))
|
||||
for idx, (pA, mode, clip) in enumerate(params):
|
||||
img = tensor_to_cv(pA)
|
||||
match mode:
|
||||
case EnumAutoLevel.AUTO:
|
||||
leveled = image_autolevel(img)
|
||||
|
||||
case EnumAutoLevel.HISTOGRAM:
|
||||
leveled = image_autolevel_histogram(img, clip)
|
||||
|
||||
images.append(cv_to_tensor_full(leveled))
|
||||
pbar.update_absolute(idx)
|
||||
return image_stack(images)
|
||||
|
||||
class BlendNode(CozyImageNode):
|
||||
NAME = "BLEND (JOV) ⚗️"
|
||||
CATEGORY = JOV_CATEGORY
|
||||
@@ -216,6 +276,42 @@ Create masks based on specific color ranges within an image. Specify the color r
|
||||
pbar.update_absolute(idx)
|
||||
return image_stack(images)
|
||||
|
||||
class HistogramNode(CozyImageNode):
|
||||
NAME = "HISTOGRAM (JOV)"
|
||||
CATEGORY = JOV_CATEGORY
|
||||
DESCRIPTION = """
|
||||
The Histogram Node generates a histogram representation of the input image, showing the distribution of pixel intensity values across different bins. This visualization is useful for understanding the overall brightness and contrast characteristics of an image. Additionally, the node performs histogram normalization, which adjusts the pixel values to enhance the contrast of the image. Histogram normalization can be helpful for improving the visual quality of images or preparing them for further image processing tasks.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls) -> InputType:
|
||||
d = super().INPUT_TYPES()
|
||||
d = deep_merge(d, {
|
||||
"optional": {
|
||||
Lexicon.IMAGE: (COZY_TYPE_IMAGE, {
|
||||
"tooltip": "Pixel Data (RGBA, RGB or Grayscale)"}),
|
||||
Lexicon.WH: ("VEC2", {
|
||||
"default": (512, 512), "mij":IMAGE_SIZE_MIN, "int": True,
|
||||
"label": ["W", "H"]}),
|
||||
}
|
||||
})
|
||||
return Lexicon._parse(d)
|
||||
|
||||
def run(self, **kw) -> RGBAMaskType:
|
||||
pA = parse_param(kw, Lexicon.IMAGE, EnumConvertType.IMAGE, None)
|
||||
wihi = parse_param(kw, Lexicon.WH, EnumConvertType.VEC2INT, (512, 512), IMAGE_SIZE_MIN)
|
||||
params = list(zip_longest_fill(pA, wihi))
|
||||
images = []
|
||||
pbar = ProgressBar(len(params))
|
||||
for idx, (pA, wihi) in enumerate(params):
|
||||
pA = tensor_to_cv(pA) if pA is not None else channel_solid()
|
||||
hist_img = image_histogram2(pA, bins=256)
|
||||
width, height = wihi
|
||||
hist_img = image_resize(hist_img, width, height, EnumInterpolation.NEAREST)
|
||||
images.append(cv_to_tensor_full(hist_img))
|
||||
pbar.update_absolute(idx)
|
||||
return image_stack(images)
|
||||
|
||||
class PixelMergeNode(CozyImageNode):
|
||||
NAME = "PIXEL MERGE (JOV) 🫂"
|
||||
CATEGORY = JOV_CATEGORY
|
||||
@@ -433,37 +529,3 @@ Define a range and apply it to an image for segmentation and feature extraction.
|
||||
images.append(cv_to_tensor_full(pA))
|
||||
pbar.update_absolute(idx)
|
||||
return image_stack(images)
|
||||
|
||||
'''
|
||||
class HistogramNode(JOVImageSimple):
|
||||
NAME = "HISTOGRAM (JOV) 👁🗨"
|
||||
CATEGORY = JOV_CATEGORY
|
||||
RETURN_TYPES = ("IMAGE", )
|
||||
RETURN_NAMES = ("IMAGE",)
|
||||
DESCRIPTION = """
|
||||
The Histogram Node generates a histogram representation of the input image, showing the distribution of pixel intensity values across different bins. This visualization is useful for understanding the overall brightness and contrast characteristics of an image. Additionally, the node performs histogram normalization, which adjusts the pixel values to enhance the contrast of the image. Histogram normalization can be helpful for improving the visual quality of images or preparing them for further image processing tasks.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls) -> InputType:
|
||||
d = super().INPUT_TYPES()
|
||||
d = deep_merge(d, {
|
||||
"optional": {
|
||||
Lexicon.IMAGE": (COZY_TYPE_IMAGE, {
|
||||
"tooltip": "Pixel Data (RGBA, RGB or Grayscale)"}),
|
||||
}
|
||||
})
|
||||
return Lexicon._parse(d)
|
||||
|
||||
def run(self, **kw) -> RGBAMaskType:
|
||||
pA = parse_param(kw, Lexicon.IMAGE", None), EnumConvertType.IMAGE, None)
|
||||
params = list(zip_longest_fill(pA,))
|
||||
images = []
|
||||
pbar = ProgressBar(len(params))
|
||||
for idx, (pA, ) in enumerate(params):
|
||||
pA = image_histogram(pA)
|
||||
pA = image_histogram_normalize(pA)
|
||||
images.append(cv_to_tensor(pA))
|
||||
pbar.update_absolute(idx)
|
||||
return image_stack(images)
|
||||
'''
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"ADJUST: SHARPEN (JOV)": "Sharpen the pixels of an image",
|
||||
"AKASHIC (JOV) \ud83d\udcd3": "Visualize data",
|
||||
"ARRAY (JOV) \ud83d\udcda": "Processes a batch of data based on the selected mode",
|
||||
"AUTO LEVEL (JOV)": "Automatically adjusts image levels so that the darkest pixel becomes black\nand the brightest pixel becomes white, enhancing overall contrast",
|
||||
"BATCH TO LIST (JOV)": "Convert a batch of values into a pure python list of values",
|
||||
"BIT SPLIT (JOV) \u2b44": "Split an input into separate bits",
|
||||
"BLEND (JOV) \u2697\ufe0f": "Combine two input images using various blending modes, such as normal, screen, multiply, overlay, etc",
|
||||
@@ -26,6 +27,7 @@
|
||||
"FLATTEN (JOV) \u2b07\ufe0f": "Combine multiple input images into a single image by summing their pixel values",
|
||||
"GRADIENT MAP (JOV) \ud83c\uddf2\ud83c\uddfa": "Remaps an input image using a gradient lookup table (LUT)",
|
||||
"GRAPH (JOV) \ud83d\udcc8": "Visualize a series of data points over time",
|
||||
"HISTOGRAM (JOV)": "The Histogram Node generates a histogram representation of the input image, showing the distribution of pixel intensity values across different bins",
|
||||
"IMAGE INFO (JOV) \ud83d\udcda": "Exports and Displays immediate information about images",
|
||||
"LERP (JOV) \ud83d\udd30": "Calculate linear interpolation between two values or vectors based on a blending factor (alpha)",
|
||||
"OP BINARY (JOV) \ud83c\udf1f": "Execute binary operations like addition, subtraction, multiplication, division, and bitwise operations on input values, supporting various data types and vector sizes",
|
||||
|
||||
+2
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "jovimetrix"
|
||||
description = "Animation via tick. Parameter manipulation with wave generator. Unary and Binary math support. Value convert int/float/bool, VectorN and Image, Mask types. Shape mask generator. Stack images, do channel ops, split, merge and randomize arrays and batches. Load images & video from anywhere. Dynamic bus routing. Save output anywhere! Flatten, crop, transform; check colorblindness or linear interpolate values."
|
||||
version = "2.1.23"
|
||||
version = "2.1.24"
|
||||
license = { file = "LICENSE" }
|
||||
readme = "README.md"
|
||||
authors = [{ name = "Alexander G. Morano", email = "amorano@gmail.com" }]
|
||||
@@ -19,6 +19,7 @@ requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"aenum",
|
||||
"git+https://github.com/cozy-comfyui/cozy_comfyui@main#egg=cozy_comfyui",
|
||||
"git+https://github.com/cozy-comfyui/cozy_comfy@main#egg=cozy_comfy",
|
||||
"matplotlib",
|
||||
"numpy>=1.25.0",
|
||||
"opencv-contrib-python",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
aenum
|
||||
git+https://github.com/cozy-comfyui/cozy_comfyui@main#egg=cozy_comfyui
|
||||
git+https://github.com/cozy-comfyui/cozy_comfy@main#egg=cozy_comfy
|
||||
matplotlib
|
||||
numpy>=1.25.0
|
||||
opencv-contrib-python
|
||||
|
||||
Reference in New Issue
Block a user