feat: add Color Palette Extractor and refactor math nodes (v1.2.0)

This commit is contained in:
root
2026-03-24 21:02:49 +01:00
parent 6481ead998
commit 8479288e2a
4 changed files with 130 additions and 34 deletions
+21 -1
View File
@@ -338,6 +338,26 @@ Useful for dynamic resizing, conditional logic based on aspect ratio, or passing
</details>
<details>
<summary><b>🎨 Color Palette Extractor</b> - Get dominant colors from an image</summary>
**Category:** `DebugPadawan/Image`
Analyzes an image and extracts the most dominant colors as hex strings and a visual palette.
**📥 Inputs:**
- `image` *(Image)*: The image to analyze.
- `color_count` *(Integer)*: Number of colors to extract (default: 5, max: 20).
**📤 Outputs:**
- `hex_list` *(String)*: Comma-separated list of top hex colors.
- `dominant_color` *(String)*: The most frequent hex color.
- `palette_image` *(Image)*: A generated image showing the extracted color palette.
Maintain consistent styles, extract themes from reference images, or use colors for conditional prompting.
</details>
---
### 🧮 Math & Random
@@ -522,7 +542,7 @@ Data → Wait (2.0s) → Processing → Wait (1.0s) → Output
| **DebugPadawan/Timing** | Wait | Timing control and delays |
| **DebugPadawan/Utilities** | List Info | Data analysis helpers |
| **DebugPadawan/Logic** | Conditional String, Logic Gate | Conditional operations |
| **DebugPadawan/Image** | Image Info | Image tensor analysis |
| **DebugPadawan/Image** | Image Info, Color Palette Extractor | Image tensor analysis and color extraction |
| **DebugPadawan/Math** | Int/Float Math Operation, Random Generator | Basic arithmetic and random number generation |
| **DebugPadawan/List** | Get List Item, List Slicer | List manipulation |
+5 -1
View File
@@ -13,6 +13,8 @@ from .nodes.json import NODE_CLASS_MAPPINGS as JSON_NODES
from .nodes.json import NODE_DISPLAY_NAME_MAPPINGS as JSON_DISPLAY_NAMES
from .nodes.image import NODE_CLASS_MAPPINGS as IMAGE_NODES
from .nodes.image import NODE_DISPLAY_NAME_MAPPINGS as IMAGE_DISPLAY_NAMES
from .nodes.color_palette import NODE_CLASS_MAPPINGS as COLOR_PALETTE_NODES
from .nodes.color_palette import NODE_DISPLAY_NAME_MAPPINGS as COLOR_PALETTE_DISPLAY_NAMES
from .nodes.math_nodes import NODE_CLASS_MAPPINGS as MATH_NODES
from .nodes.math_nodes import NODE_DISPLAY_NAME_MAPPINGS as MATH_DISPLAY_NAMES
from .nodes.list_nodes import NODE_CLASS_MAPPINGS as LIST_NODES
@@ -30,6 +32,7 @@ NODE_CLASS_MAPPINGS = {
**TIMING_NODES,
**JSON_NODES,
**IMAGE_NODES,
**COLOR_PALETTE_NODES,
**MATH_NODES,
**LIST_NODES,
**JSON_TO_TEXT_NODES,
@@ -42,6 +45,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
**TIMING_DISPLAY_NAMES,
**JSON_DISPLAY_NAMES,
**IMAGE_DISPLAY_NAMES,
**COLOR_PALETTE_DISPLAY_NAMES,
**MATH_DISPLAY_NAMES,
**LIST_DISPLAY_NAMES,
**JSON_TO_TEXT_DISPLAY_NAMES,
@@ -51,5 +55,5 @@ NODE_DISPLAY_NAME_MAPPINGS = {
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
# Version info
__version__ = "1.1.0"
__version__ = "1.2.0"
__author__ = "DebugPadawan"
+75
View File
@@ -0,0 +1,75 @@
import numpy as np
import torch
class ColorPaletteExtractor:
"""
Node for extracting the most dominant colors from an image
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"image": ("IMAGE",),
"color_count": ("INT", {"default": 5, "min": 1, "max": 20}),
}
}
RETURN_TYPES = ("STRING", "STRING", "IMAGE")
RETURN_NAMES = ("hex_list", "dominant_color", "palette_image")
FUNCTION = "extract"
CATEGORY = "DebugPadawan/Image"
def extract(self, image, color_count):
# Image is typically [B, H, W, C]
# We'll take the first image in the batch
img = image[0]
h, w, c = img.shape
# Rescale for performance using torch
img_torch = img.permute(2, 0, 1).unsqueeze(0) # [1, C, H, W]
img_small = torch.nn.functional.interpolate(img_torch, size=(128, 128), mode='area')
img_np = img_small.squeeze(0).permute(1, 2, 0).numpy()
# Flatten and scale to 0-255
pixels = img_np.reshape(-1, c) * 255.0
# Simple quantization
pixels = (pixels / 16).astype(int) * 16
# Convert to hex strings
hex_colors = []
for p in pixels:
r, g, b = p
hex_colors.append(f'#{r:02x}{g:02x}{b:02x}')
# Count frequencies
unique, counts = np.unique(hex_colors, return_counts=True)
sorted_indices = np.argsort(-counts)
top_hex = unique[sorted_indices[:color_count]]
dominant = top_hex[0] if len(top_hex) > 0 else "#000000"
# Create a palette image
palette_h = 64
palette_w = color_count * 64
palette_img = np.zeros((palette_h, palette_w, 3), dtype=np.float32)
for i, hex_color in enumerate(top_hex):
r = int(hex_color[1:3], 16) / 255.0
g = int(hex_color[3:5], 16) / 255.0
b = int(hex_color[5:7], 16) / 255.0
palette_img[:, i*64:(i+1)*64, 0] = r
palette_img[:, i*64:(i+1)*64, 1] = g
palette_img[:, i*64:(i+1)*64, 2] = b
palette_tensor = torch.from_numpy(palette_img).unsqueeze(0)
return (", ".join(top_hex), dominant, palette_tensor)
NODE_CLASS_MAPPINGS = {
"DebugPadawan_ColorPalette": ColorPaletteExtractor,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"DebugPadawan_ColorPalette": "Color Palette Extractor",
}
+29 -32
View File
@@ -1,7 +1,32 @@
import random
import math
class IntMathOperation:
class BaseMathOperation:
"""
Base class for math operations to reduce duplication
"""
def _perform_calculation(self, a, b, operation, is_int=True):
if operation == "add":
res = a + b
elif operation == "subtract":
res = a - b
elif operation == "multiply":
res = a * b
elif operation == "divide":
res = a / b if b != 0 else 0
elif operation == "modulo":
res = a % b if b != 0 else 0
elif operation == "power":
res = math.pow(a, b)
else:
res = 0
if is_int:
return (int(res), float(res))
else:
return (float(res), int(res))
class IntMathOperation(BaseMathOperation):
"""
Node for performing basic integer math operations
"""
@@ -21,25 +46,10 @@ class IntMathOperation:
CATEGORY = "DebugPadawan/Math"
def perform_math(self, a, b, operation):
if operation == "add":
res = a + b
elif operation == "subtract":
res = a - b
elif operation == "multiply":
res = a * b
elif operation == "divide":
res = a / b if b != 0 else 0
elif operation == "modulo":
res = a % b if b != 0 else 0
elif operation == "power":
res = math.pow(a, b)
else:
res = 0
return (int(res), float(res))
return self._perform_calculation(a, b, operation, is_int=True)
class FloatMathOperation:
class FloatMathOperation(BaseMathOperation):
"""
Node for performing basic float math operations
"""
@@ -59,20 +69,7 @@ class FloatMathOperation:
CATEGORY = "DebugPadawan/Math"
def perform_math(self, a, b, operation):
if operation == "add":
res = a + b
elif operation == "subtract":
res = a - b
elif operation == "multiply":
res = a * b
elif operation == "divide":
res = a / b if b != 0 else 0.0
elif operation == "power":
res = math.pow(a, b)
else:
res = 0.0
return (float(res), int(res))
return self._perform_calculation(a, b, operation, is_int=False)
class RandomGenerator: