Several furhter helper nodes
This commit is contained in:
@@ -7,9 +7,13 @@ A comprehensive collection of useful custom nodes for ComfyUI workflows. Designe
|
||||
|
||||
## 🚀 Current Tools
|
||||
|
||||
### 📁 Directory Image Tools
|
||||
### 📁 Tools
|
||||
- **DirectoryImageLoader**: Load images from a directory by index
|
||||
- **DirectoryImageInfo**: Get detailed information about images in a directory
|
||||
- **Aspect Ratio and Tile size calculator**: Calculates the aspect ratio of an image and determines optimal tile dimensions based on a target megapixel value, upscale factor, and optional padding. Useful for tiled image processing workflows.
|
||||
- **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.
|
||||
|
||||
*More tools coming soon! This is just the beginning of the Skycoder Tools collection.*
|
||||
|
||||
@@ -33,4 +37,6 @@ A comprehensive collection of useful custom nodes for ComfyUI workflows. Designe
|
||||
```bash
|
||||
cd ComfyUI/custom_nodes
|
||||
git clone https://github.com/skycoder/comfyui-skycoder-tools.git
|
||||
# Restart ComfyUI
|
||||
# Restart ComfyUI
|
||||
### 🧠 Image Analysis & Utilities
|
||||
|
||||
|
||||
+17
-2
@@ -4,15 +4,30 @@ A collection of useful custom nodes for ComfyUI workflows
|
||||
"""
|
||||
|
||||
from .nodes.directory_image_loader import NODE_CLASS_MAPPINGS as DIR_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as DIR_DISPLAY_MAPPINGS
|
||||
from .Aspect_Ratio_and_Tile_size_calculator import NODE_CLASS_MAPPINGS as ASPECT_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as ASPECT_DISPLAY_MAPPINGS
|
||||
from .blipimagecaptioning import NODE_CLASS_MAPPINGS as BLIP_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as BLIP_DISPLAY_MAPPINGS
|
||||
from .BooleanToggleNode import NODE_CLASS_MAPPINGS as TOGGLE_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as TOGGLE_DISPLAY_MAPPINGS
|
||||
from .concatenate_and_test_if_empty import NODE_CLASS_MAPPINGS as CONCAT_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as CONCAT_DISPLAY_MAPPINGS
|
||||
|
||||
# Kombiniere alle Node-Mappings (für zukünftige Erweiterungen)
|
||||
# Combine all Node Mappings
|
||||
NODE_CLASS_MAPPINGS = {}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {}
|
||||
|
||||
# Directory Image Loader Nodes
|
||||
NODE_CLASS_MAPPINGS.update(DIR_MAPPINGS)
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(DIR_DISPLAY_MAPPINGS)
|
||||
|
||||
NODE_CLASS_MAPPINGS.update(ASPECT_MAPPINGS)
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(ASPECT_DISPLAY_MAPPINGS)
|
||||
|
||||
NODE_CLASS_MAPPINGS.update(BLIP_MAPPINGS)
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(BLIP_DISPLAY_MAPPINGS)
|
||||
|
||||
NODE_CLASS_MAPPINGS.update(TOGGLE_MAPPINGS)
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(TOGGLE_DISPLAY_MAPPINGS)
|
||||
|
||||
NODE_CLASS_MAPPINGS.update(CONCAT_MAPPINGS)
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(CONCAT_DISPLAY_MAPPINGS)
|
||||
|
||||
# Exportiere alle Node-Mappings für ComfyUI
|
||||
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
from math import sqrt, floor
|
||||
|
||||
class Aspect_Ratio_and_Tile_size_calculator:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
"tile_megapixel": ("FLOAT", {"default": 1.0, "min": 0.01, "max": 100.0, "step": 0.01, "label": "Tile Megapixel"}),
|
||||
"upscale_factor": ("FLOAT", {"default": 1.0, "min": 0.01, "max": 100.0, "step": 0.01, "label": "Upscale Factor"}),
|
||||
"tile_padding": ("INT", {"default": 0, "min": 0, "max": 4096, "step": 1, "label": "Tile Padding"}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FLOAT", "INT", "INT", "FLOAT", "INT", "INT", "INT")
|
||||
RETURN_NAMES = ("aspect_ratio", "width", "height", "upscale_factor", "tile_width", "tile_height", "tile_padding")
|
||||
FUNCTION = "get_aspect_ratio"
|
||||
CATEGORY = "Custom"
|
||||
|
||||
def get_aspect_ratio(self, image, tile_megapixel, upscale_factor, tile_padding):
|
||||
# Tensor to PIL conversion
|
||||
def tensor2pil(img):
|
||||
if hasattr(img, "__len__") and not isinstance(img, (bytes, str)) and len(img) > 0:
|
||||
img = img[0]
|
||||
img = img.detach().cpu().numpy()
|
||||
img = (img * 255).astype(np.uint8)
|
||||
if img.shape[0] == 3:
|
||||
img = np.transpose(img, (1, 2, 0))
|
||||
return Image.fromarray(img)
|
||||
|
||||
pil_img = tensor2pil(image)
|
||||
width, height = pil_img.size
|
||||
aspect = width / height if height else 0.0
|
||||
|
||||
# Variablen
|
||||
a = upscale_factor
|
||||
b = tile_megapixel
|
||||
c = width
|
||||
d = height
|
||||
p = tile_padding
|
||||
|
||||
# Berechnung tile_width und tile_height
|
||||
if c > 0 and d > 0 and b > 0 and a > 0:
|
||||
divisor = floor((c * a) / sqrt(b * 1000000 * c / d)) + 1
|
||||
tile_width = (c * a) / divisor if divisor > 0 else c * a
|
||||
tile_height = tile_width * d / c
|
||||
else:
|
||||
tile_width = 0.0
|
||||
tile_height = 0.0
|
||||
|
||||
# Padding addieren
|
||||
tile_width += p
|
||||
tile_height += p
|
||||
|
||||
return (float(aspect), int(width), int(height), float(upscale_factor), int(tile_width), int(tile_height), int(tile_padding))
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"Aspect_Ratio_and_Tile_size_calculator": Aspect_Ratio_and_Tile_size_calculator,
|
||||
}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"Aspect_Ratio_and_Tile_size_calculator": "Aspect Ratio and Tile size calculator",
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
class BooleanToggle:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"toggle": ("BOOLEAN", {"default": False, "label": "Boolean Toggle"}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BOOLEAN",)
|
||||
RETURN_NAMES = ("toggle_value",)
|
||||
FUNCTION = "toggle_func"
|
||||
CATEGORY = "Custom"
|
||||
|
||||
def toggle_func(self, toggle):
|
||||
return (toggle,)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"BooleanToggle": BooleanToggle,
|
||||
}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"BooleanToggle": "Boolean Toggle",
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import torch
|
||||
from transformers import BlipProcessor, BlipForConditionalGeneration
|
||||
from PIL import Image
|
||||
import folder_paths
|
||||
import numpy as np
|
||||
|
||||
class BLIP2Captioning:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE", {"tooltip": "Input image to generate caption for."})
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("caption",)
|
||||
FUNCTION = "generate_caption"
|
||||
CATEGORY = "BLIP2"
|
||||
|
||||
def __init__(self):
|
||||
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
||||
self.model_id = "Salesforce/blip-image-captioning-base"
|
||||
|
||||
# Laden von Processor & Modell
|
||||
self.processor = BlipProcessor.from_pretrained(self.model_id)
|
||||
self.model = BlipForConditionalGeneration.from_pretrained(self.model_id).to(self.device)
|
||||
|
||||
def generate_caption(self, image):
|
||||
# Sicheres Konvertieren: ComfyUI-Tensor → PIL
|
||||
pil_image = tensor2pil(image).convert("RGB")
|
||||
|
||||
# BLIP Processing + Inferenz
|
||||
inputs = self.processor(images=pil_image, return_tensors="pt").to(self.device)
|
||||
out = self.model.generate(**inputs)
|
||||
caption = self.processor.decode(out[0], skip_special_tokens=True)
|
||||
|
||||
return (caption,)
|
||||
|
||||
def tensor2pil(image):
|
||||
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"BLIP2Captioning": BLIP2Captioning
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"BLIP2Captioning": "BLIP2 Image Caption"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
class ConcatenateAndTestIfEmpty:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"optional": {
|
||||
"text1": ("STRING", {"forceInput": True}),
|
||||
"text2": ("STRING", {"forceInput": True}),
|
||||
"text3": ("STRING", {"forceInput": True}),
|
||||
"text4": ("STRING", {"forceInput": True}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BOOLEAN", "STRING")
|
||||
RETURN_NAMES = ("is_empty", "result")
|
||||
FUNCTION = "concat_and_test"
|
||||
CATEGORY = "Custom"
|
||||
|
||||
def concat_and_test(self, text1=None, text2=None, text3=None, text4=None):
|
||||
result = ""
|
||||
for t in (text1, text2, text3, text4):
|
||||
if t is not None:
|
||||
result += t
|
||||
is_empty = result == ""
|
||||
return (is_empty, result)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"ConcatenateAndTestIfEmpty": ConcatenateAndTestIfEmpty,
|
||||
}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"ConcatenateAndTestIfEmpty": "Concatenate and Test if Empty",
|
||||
}
|
||||
+9
-5
@@ -7,7 +7,7 @@ name = "comfyui-skycoder-tools"
|
||||
version = "1.0.0"
|
||||
description = "A collection of useful custom nodes for ComfyUI workflows"
|
||||
authors = [
|
||||
{name = "Skycoder", email = "skycoder@example.com"}
|
||||
{name = "Skycoder182", email = "skycoder@example.com"}
|
||||
]
|
||||
license = {text = "MIT"}
|
||||
readme = "README.md"
|
||||
@@ -31,7 +31,11 @@ dependencies = [
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/skycoder/comfyui-skycoder-tools"
|
||||
Repository = "https://github.com/skycoder/comfyui-skycoder-tools"
|
||||
Issues = "https://github.com/skycoder/comfyui-skycoder-tools/issues"
|
||||
Discussions = "https://github.com/skycoder/comfyui-skycoder-tools/discussions"
|
||||
Homepage = "https://github.com/skycoder182/comfyui-skycoder-tools"
|
||||
Repository = "https://github.com/skycoder182/comfyui-skycoder-tools"
|
||||
Issues = "https://github.com/skycoder182/comfyui-skycoder-tools/issues"
|
||||
Discussions = "https://github.com/skycoder182/comfyui-skycoder-tools/discussions"
|
||||
|
||||
[tool.comfy]
|
||||
PublisherId = "skycoder"
|
||||
DisplayName = "Skycoder Tools"
|
||||
|
||||
Reference in New Issue
Block a user