Range Editor

This commit is contained in:
Terry Jia
2026-02-26 08:22:46 -05:00
parent de67bb0870
commit 4d721bff59
2 changed files with 157 additions and 0 deletions

View File

@@ -1252,6 +1252,41 @@ class Curve(ComfyTypeIO):
return super().as_dict()
@comfytype(io_type="RANGE")
class Range(ComfyTypeIO):
Type = dict # {"min": float, "max": float, "midpoint"?: float}
class Input(WidgetInput):
def __init__(self, id: str, display_name: str=None, optional=False, tooltip: str=None,
socketless: bool=True, default: dict=None,
display: str=None,
gradient_stops: list=None,
show_midpoint: bool=None,
midpoint_scale: str=None,
value_min: float=None,
value_max: float=None,
advanced: bool=None):
super().__init__(id, display_name, optional, tooltip, None, default, socketless, None, None, None, None, advanced)
if default is None:
self.default = {"min": 0.0, "max": 1.0}
self.display = display
self.gradient_stops = gradient_stops
self.show_midpoint = show_midpoint
self.midpoint_scale = midpoint_scale
self.value_min = value_min
self.value_max = value_max
def as_dict(self):
return super().as_dict() | prune_dict({
"display": self.display,
"gradient_stops": self.gradient_stops,
"show_midpoint": self.show_midpoint,
"midpoint_scale": self.midpoint_scale,
"value_min": self.value_min,
"value_max": self.value_max,
})
@comfytype(io_type="COLOR_CURVES")
class ColorCurves(ComfyTypeIO):
class ColorCurvesDict(TypedDict):

122
nodes.py
View File

@@ -2057,6 +2057,122 @@ class TestCurveWidget:
return {"ui": {"text": [result]}, "result": (result,)}
class TestRangePlain:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"range": ("RANGE", {"default": {"min": 0.0, "max": 1.0}}),
"range_midpoint": ("RANGE", {
"default": {"min": 0.2, "max": 0.8, "midpoint": 0.5},
"show_midpoint": True,
}),
}
}
RETURN_TYPES = ("STRING",)
FUNCTION = "execute"
OUTPUT_NODE = True
CATEGORY = "testing"
def execute(self, **kwargs):
import json
result = json.dumps(kwargs, indent=2)
return {"ui": {"text": [result]}, "result": (result,)}
class TestRangeGradient:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"range": ("RANGE", {
"default": {"min": 0.0, "max": 1.0},
"display": "gradient",
"gradient_stops": [
{"offset": 0.0, "color": [0, 0, 0]},
{"offset": 1.0, "color": [255, 255, 255]}
],
}),
"range_midpoint": ("RANGE", {
"default": {"min": 0.0, "max": 1.0, "midpoint": 0.5},
"display": "gradient",
"gradient_stops": [
{"offset": 0.0, "color": [0, 0, 0]},
{"offset": 1.0, "color": [255, 255, 255]}
],
"show_midpoint": True,
"midpoint_scale": "gamma",
}),
}
}
RETURN_TYPES = ("STRING",)
FUNCTION = "execute"
OUTPUT_NODE = True
CATEGORY = "testing"
def execute(self, **kwargs):
import json
result = json.dumps(kwargs, indent=2)
return {"ui": {"text": [result]}, "result": (result,)}
class TestRangeHistogram:
RANGE_OPTS = {
"display": "histogram",
"show_midpoint": True,
"midpoint_scale": "gamma",
"value_min": 0,
"value_max": 255,
}
@classmethod
def INPUT_TYPES(s):
default = {"min": 0, "max": 255, "midpoint": 0.5}
return {
"required": {
"image": ("IMAGE",),
"rgb": ("RANGE", {"default": {**default}, **s.RANGE_OPTS}),
"red": ("RANGE", {"default": {**default}, **s.RANGE_OPTS}),
"green": ("RANGE", {"default": {**default}, **s.RANGE_OPTS}),
"blue": ("RANGE", {"default": {**default}, **s.RANGE_OPTS}),
}
}
RETURN_TYPES = ("STRING",)
FUNCTION = "execute"
OUTPUT_NODE = True
CATEGORY = "testing"
def execute(self, image, rgb, red, green, blue):
import json
import numpy as np
img = image[0].cpu().numpy() # (H, W, C)
# Per-channel histograms
hist_r, _ = np.histogram(img[:, :, 0].flatten(), bins=256, range=(0.0, 1.0))
hist_g, _ = np.histogram(img[:, :, 1].flatten(), bins=256, range=(0.0, 1.0))
hist_b, _ = np.histogram(img[:, :, 2].flatten(), bins=256, range=(0.0, 1.0))
# Luminance histogram (BT.709)
luminance = 0.2126 * img[:, :, 0] + 0.7152 * img[:, :, 1] + 0.0722 * img[:, :, 2]
hist_rgb, _ = np.histogram(luminance.flatten(), bins=256, range=(0.0, 1.0))
result = json.dumps({"rgb": rgb, "red": red, "green": green, "blue": blue}, indent=2)
return {
"ui": {
"text": [result],
"range_histogram_rgb": hist_rgb.astype(np.uint32).tolist(),
"range_histogram_red": hist_r.astype(np.uint32).tolist(),
"range_histogram_green": hist_g.astype(np.uint32).tolist(),
"range_histogram_blue": hist_b.astype(np.uint32).tolist(),
},
"result": (result,)
}
NODE_CLASS_MAPPINGS = {
"KSampler": KSampler,
"CheckpointLoaderSimple": CheckpointLoaderSimple,
@@ -2126,6 +2242,9 @@ NODE_CLASS_MAPPINGS = {
"ConditioningSetTimestepRange": ConditioningSetTimestepRange,
"LoraLoaderModelOnly": LoraLoaderModelOnly,
"TestCurveWidget": TestCurveWidget,
"TestRangePlain": TestRangePlain,
"TestRangeGradient": TestRangeGradient,
"TestRangeHistogram": TestRangeHistogram,
}
NODE_DISPLAY_NAME_MAPPINGS = {
@@ -2195,6 +2314,9 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"VAEDecodeTiled": "VAE Decode (Tiled)",
"VAEEncodeTiled": "VAE Encode (Tiled)",
"TestCurveWidget": "Test Curve Widget",
"TestRangePlain": "Test Range (Plain)",
"TestRangeGradient": "Test Range (Gradient)",
"TestRangeHistogram": "Test Range (Histogram)",
}
EXTENSION_WEB_DIRS = {}