diff --git a/README.md b/README.md index ef6cd7a..28b1913 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,9 @@ This repository offers various extension nodes for ComfyUI. Nodes here have diff * `Global Seed (Inspire)`: This is a node that controls the global seed without a separate connection line. It only controls when the widget's name is 'seed' or 'noise_seed'. Additionally, if 'control_before_generate' is checked, it controls the seed before executing the prompt. * Seeds that have been converted into inputs are excluded from the target. If you want to control the seed separately, convert it into an input and control it separately. +* Image Util + * `Load Images From Dir (Inspire)`: This is almost same as `LoadImagesFromDirectory` of [ComfyUI-Advanced-Controlnet](https://github.com/Kosinkadink/ComfyUI-Advanced-ControlNet). This is just a modified version. Just note that this node forcibly normalizes the size of the loaded image to match the size of the first image, even if they are not the same size, to create a batch image. + ## Credits ComfyUI/[ComfyUI](https://github.com/comfyanonymous/ComfyUI) - A powerful and modular stable diffusion GUI. @@ -54,3 +57,4 @@ LucianoCirino[efficiency-nodes-comfyui](https://github.com/LucianoCirino/efficie Fannovel16/[comfyui_controlnet_aux](https://github.com/Fannovel16/comfyui_controlnet_aux) - The wrapper for the controlnet preprocessor in the Inspire Pack depends on these nodes. +Kosinkadink/[ComfyUI-Advanced-Controlnet](https://github.com/Kosinkadink/ComfyUI-Advanced-ControlNet) - `Load Images From Dir (Inspire)` code is came from here. \ No newline at end of file diff --git a/__init__.py b/__init__.py index 6aa0a19..2bd2181 100644 --- a/__init__.py +++ b/__init__.py @@ -7,7 +7,7 @@ import importlib -print(f"### Loading: ComfyUI-Inspire-Pack (V0.17)") +print(f"### Loading: ComfyUI-Inspire-Pack (V0.18)") node_list = [ "lora_block_weight", @@ -15,6 +15,7 @@ node_list = [ "a1111_compat", "prompt_support", "inspire_server", + "image_util", ] NODE_CLASS_MAPPINGS = {} diff --git a/image_util.py b/image_util.py new file mode 100644 index 0000000..df9adf9 --- /dev/null +++ b/image_util.py @@ -0,0 +1,81 @@ +import os +from PIL import Image +from PIL import ImageOps +import numpy as np +import torch +import comfy + +class LoadImagesFromDir: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "directory": ("STRING", {"default": ""}), + }, + "optional": { + "image_load_cap": ("INT", {"default": 0, "min": 0, "step": 1}), + "start_index": ("INT", {"default": 0, "min": 0, "step": 1}), + } + } + + RETURN_TYPES = ("IMAGE", "MASK", "INT") + FUNCTION = "load_images" + + CATEGORY = "Inspire/Batch" + + def load_images(self, directory: str, image_load_cap: int = 0, start_index: int = 0): + if not os.path.isdir(directory): + raise FileNotFoundError(f"Directory '{directory} cannot be found.'") + dir_files = os.listdir(directory) + if len(dir_files) == 0: + raise FileNotFoundError(f"No files in directory '{directory}'.") + + dir_files = sorted(dir_files) + dir_files = [os.path.join(directory, x) for x in dir_files] + + # start at start_index + dir_files = dir_files[start_index:] + + images = [] + masks = [] + + limit_images = False + if image_load_cap > 0: + limit_images = True + image_count = 0 + + for image_path in dir_files: + if os.path.isdir(image_path): + continue + if limit_images and image_count >= image_load_cap: + break + i = Image.open(image_path) + i = ImageOps.exif_transpose(i) + image = i.convert("RGB") + image = np.array(image).astype(np.float32) / 255.0 + image = torch.from_numpy(image)[None,] + if 'A' in i.getbands(): + mask = np.array(i.getchannel('A')).astype(np.float32) / 255.0 + mask = 1. - torch.from_numpy(mask) + else: + mask = torch.zeros((64, 64), dtype=torch.float32, device="cpu") + images.append(image) + masks.append(mask) + image_count += 1 + + if len(images) == 1: + return (images[0], 1) + elif len(images) > 1: + image1 = images[0] + for image2 in images[1:]: + if image1.shape[1:] != image2.shape[1:]: + image2 = comfy.utils.common_upscale(image2.movedim(-1, 1), image1.shape[2], image1.shape[1], "bilinear", "center").movedim(1, -1) + image1 = torch.cat((image1, image2), dim=0) + return (image1, len(images)) + +NODE_CLASS_MAPPINGS = { + "LoadImagesFromDir //Inspire": LoadImagesFromDir, +} +NODE_DISPLAY_NAME_MAPPINGS = { + "LoadImagesFromDir //Inspire": "Load Images From Dir (Inspire)", +}