From 20159167d87b9df1b1c495dbf55e8286d797309b Mon Sep 17 00:00:00 2001 From: skycoder182 <212504411+skycoder182@users.noreply.github.com> Date: Fri, 30 May 2025 14:57:39 +0200 Subject: [PATCH] New Node ImageBasicNode --- README.md | 1 + __init__.py | 3 ++ nodes/ImageBasicNode.py | 89 +++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 nodes/ImageBasicNode.py diff --git a/README.md b/README.md index 5000657..03198ee 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ A comprehensive collection of useful custom nodes for ComfyUI workflows. Designe - **BLIP2 Image Caption**: Automatically generates a descriptive caption for an input image using Salesforce’s BLIP image captioning model. Ideal for semantic image analysis or annotation tasks. - **Boolean Toggle**: Returns a boolean value from a toggle input. Useful for conditionally controlling workflow branches. - **Concatenate and Test if Empty**: Concatenates up to four text inputs and checks whether the result is empty. Outputs both the combined string and a boolean indicating if it's empty. +- **ImageBasicNode**: Perform basic image operations such as resizing (with optional aspect ratio preservation) and grayscale conversion. Returns the processed image along with useful metadata like width, height, aspect ratio, color mode, and format. Ideal for simple preprocessing tasks within your ComfyUI workflows. *More tools coming soon! This is just the beginning of the Skycoder Tools collection.* diff --git a/__init__.py b/__init__.py index 0878364..5296e16 100644 --- a/__init__.py +++ b/__init__.py @@ -8,6 +8,7 @@ from .nodes.Aspect_Ratio_and_Tile_size_calculator import NODE_CLASS_MAPPINGS as from .nodes.blipimagecaptioning import NODE_CLASS_MAPPINGS as BLIP_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as BLIP_DISPLAY_MAPPINGS from .nodes.BooleanToggleNode import NODE_CLASS_MAPPINGS as TOGGLE_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as TOGGLE_DISPLAY_MAPPINGS from .nodes.concatenate_and_test_if_empty import NODE_CLASS_MAPPINGS as CONCAT_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as CONCAT_DISPLAY_MAPPINGS +from .nodes.ImageBasicNode import NODE_CLASS_MAPPINGS as BASIC_MAPPINGS # Combine all Node Mappings NODE_CLASS_MAPPINGS = {} @@ -28,6 +29,8 @@ NODE_DISPLAY_NAME_MAPPINGS.update(TOGGLE_DISPLAY_MAPPINGS) NODE_CLASS_MAPPINGS.update(CONCAT_MAPPINGS) NODE_DISPLAY_NAME_MAPPINGS.update(CONCAT_DISPLAY_MAPPINGS) +NODE_CLASS_MAPPINGS.update(BASIC_MAPPINGS) + # Exportiere alle Node-Mappings für ComfyUI __all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS'] diff --git a/nodes/ImageBasicNode.py b/nodes/ImageBasicNode.py new file mode 100644 index 0000000..3ee24e1 --- /dev/null +++ b/nodes/ImageBasicNode.py @@ -0,0 +1,89 @@ +from PIL import Image +import numpy as np +import torch + +class ImageBasicNode: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "image": ("IMAGE",), # ComfyUI-internes Bildobjekt + "operation": (["no operation", "resize", "grayscale"],), # Auswahlfunktion + "keep_aspect_ratio": ("BOOLEAN", {"default": True}), + }, + "optional": { + "new_width": ("INT", {"default": 256, "min": 1, "max": 4096}), + "new_height": ("INT", {"default": 256, "min": 1, "max": 4096}), + } + } + + RETURN_TYPES = ("INT", "INT", "FLOAT", "STRING", "STRING", "IMAGE") + RETURN_NAMES = ("width", "height", "ratio", "mode", "format", "output_image") + FUNCTION = "process_image" + CATEGORY = "Beispiele/Bild" + + def tensor2pil(self, image): + if hasattr(image, "__len__") and not isinstance(image, (bytes, str)) and len(image) > 0: + image = image[0] + image = image.detach().cpu().numpy() + image = (image*255).astype(np.uint8) + return Image.fromarray(image) + + def pil2tensor(self, image): + image_np = np.array(image) + image_np = image_np.astype(np.float32) / 255.0 + return torch.from_numpy(image_np).unsqueeze(0) + + def bwimage(self, image): + px=image.load() + for y in range(image.height): + for x in range(image.width): + value = px[x,y] + if isinstance(value, tuple): + R, G, B = value[:3] + avg = int(0.299 * R + 0.587 * G + 0.114 * B) + px[x,y] = (avg, avg, avg) + #print(value) + return + + def correctDimensions(self, width, height, ratio): + if ratio > width / height: + o_width = int(width) + o_height = int(width / ratio) + elif ratio <= width / height: + o_width = int(height * ratio) + o_height = int(height) + return (o_width, o_height) + + def process_image(self, image, operation, keep_aspect_ratio, new_width=None, new_height=None): + # ComfyUI gibt ein numpy-Array zurück, PIL braucht das als Image + pil_image = self.tensor2pil(image) + + width, height = pil_image.size + ratio = width / height + output_image = pil_image # Default + + if operation == "resize" and new_width and new_height: + if keep_aspect_ratio: + new_width, new_height = self.correctDimensions(new_width, new_height, ratio) + + output_image = pil_image.resize((new_width, new_height)) + width, height = output_image.size + # (Wenn nicht resized wird, bleibt Bild wie es ist) + + elif operation == "grayscale": + self.bwimage(output_image) + + ratio = width / height + mode = output_image.mode + form = output_image.format + + # Für ComfyUI zurück in Tensor wandeln + img_tensor = self.pil2tensor(output_image) + + return (width, height, ratio, mode, form, img_tensor) + + +NODE_CLASS_MAPPINGS = { + "ImageBasicNode": ImageBasicNode, +} diff --git a/pyproject.toml b/pyproject.toml index ef310b8..b604c34 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "comfyui-skycoder-tools" -version = "1.0.3" +version = "1.0.4" description = "A collection of useful custom nodes for ComfyUI workflows" authors = [ {name = "Skycoder182", email = "skycoder@example.com"}