import os import numpy as np import torch from PIL import Image, ImageOps class LoadLoopImagesFromDir: last_index = 0 # Class attribute to track the last accessed image @classmethod def INPUT_TYPES(cls): """ The function `INPUT_TYPES` defines required and optional input types with default values and labels for a specified class. :param cls: The `INPUT_TYPES` function defines input parameters for a class. The parameters are categorized as "required" and "optional" inputs :return: A dictionary containing information about input types for a class. The dictionary has two keys: "required" and "optional", each containing a dictionary of input types with their corresponding data types and optional parameters such as default values and labels. """ return { "required": { "directory": ("STRING", {"default": ""}), }, "optional": { "load_always": ("BOOLEAN", {"default": False, "label_on": "enabled", "label_off": "disabled"}), } } RETURN_TYPES = ("IMAGE", "MASK", "STRING") RETURN_NAMES = ("IMAGE", "MASK", "FILE PATH") OUTPUT_IS_LIST = (False, False, False) # Only return one image at a time FUNCTION = "load_images" CATEGORY = "image" @classmethod def IS_CHANGED(cls, **kwargs): """ The function `IS_CHANGED` increments `last_index` every time it is called, looping when needed, and returns a hash based on the index and input arguments. :param cls: The `cls` parameter in the given function represents the class itself. It is used to access class variables and methods within the function :return: The function `IS_CHANGED` returns the hash value of a tuple containing `cls.last_index` and a frozenset of the keyword arguments `kwargs.items()`. """ if kwargs.get("load_always", False): return float("NaN") directory = kwargs.get("directory", "") if not os.path.isdir(directory): return hash(frozenset(kwargs.items())) valid_extensions = {'.jpg', '.jpeg', '.png', '.webp'} dir_files = sorted( f for f in os.listdir(directory) if any(f.lower().endswith(ext) for ext in valid_extensions) ) if dir_files: cls.last_index = (cls.last_index + 1) % len(dir_files) return hash((cls.last_index, frozenset(kwargs.items()))) def load_images(cls, directory: str, load_always=False): """ The function `load_images` loads image files from a specified directory, processes them, and returns the image, mask (if alpha channel exists), and the image path. :param directory: The `directory` parameter in the `load_images` method is a string that represents the path to the directory containing the image files that you want to load. This method loads images from the specified directory based on the valid image file extensions (.jpg, .jpeg, .png, .webp) :type directory: str :param load_always: The `load_always` parameter in the `load_images` method is a boolean flag that determines whether to always load images from the specified directory or not. By default, it is set to `False`, which means that images will be loaded cyclically from the directory based on the `, defaults to False (optional) :return: The `load_images` method returns three values: `image`, `mask`, and `image_path`. """ if not os.path.isdir(directory): raise FileNotFoundError(f"Directory '{directory}' not found.") # Get all valid image files valid_extensions = {'.jpg', '.jpeg', '.png', '.webp'} dir_files = sorted( f for f in os.listdir(directory) if any(f.lower().endswith(ext) for ext in valid_extensions) ) if not dir_files: raise FileNotFoundError(f"No image files found in directory '{directory}'.") # Cycle through images using `last_index` image_count = len(dir_files) image_path = os.path.join(directory, dir_files[cls.last_index]) # Update index for the next call (loop back if at the end) cls.last_index = (cls.last_index + 1) % image_count # Load image img = Image.open(image_path) img = ImageOps.exif_transpose(img) image = img.convert("RGB") image = np.array(image).astype(np.float32) / 255.0 image = torch.from_numpy(image)[None,] # Add batch dimension # Load mask (if alpha channel exists) if 'A' in img.getbands(): mask = np.array(img.getchannel('A')).astype(np.float32) / 255.0 mask = 1.0 - torch.from_numpy(mask) else: mask = torch.zeros((64, 64), dtype=torch.float32, device="cpu") return image, mask, str(image_path) # Dictionary to map node classes to their names NODE_CLASS_MAPPINGS = { "LoadLoopImagesFromDir": LoadLoopImagesFromDir, } # Dictionary to map node names to their friendly display names NODE_DISPLAY_NAME_MAPPINGS = { "LoadLoopImagesFromDir": "Load Loop Images From Dir", }