diff --git a/__init__.py b/__init__.py index 90290fb..8b77090 100644 --- a/__init__.py +++ b/__init__.py @@ -1,19 +1,28 @@ -from .nodes import FitSize, FitSizeFromImage, FitResizeImage, FitResizeLatent, LoadToFitResizeLatent +from .nodes import * +from .startup_utils import symlink_web_dir # A dictionary that contains all nodes you want to export with their names # NOTE: names should be globally unique NODE_CLASS_MAPPINGS = { - "Fit Size From Int": FitSize, - "Fit Size From Image": FitSizeFromImage, - "Fit Image And Resize": FitResizeLatent, - "Load Image And Resize To Fit": LoadToFitResizeLatent, + "FS: Fit Size From Int": FitSize, + "FS: Fit Size From Image": FitSizeFromImage, + "FS: Fit Image And Resize": FitResizeLatent, + "FS: Load Image And Resize To Fit": LoadToFitResizeLatent, + "FS: Pick Image From Batch": RandomImageFromBatch, + "FS: Crop Image Into Even Pieces": CropImageIntoEvenPieces, } NODE_DISPLAY_NAME_MAPPINGS = { - "Fit Size From Int": "Fit Size From Int", - "Fit Size From Image": "Fit Size From Image", - "Fit Image And Resize": "Fit Image And Resize", - "Load Image And Resize To Fit": "Load Image And Resize To Fit", + "FS: Fit Size From Int": "Fit Size From Int", + "FS: Fit Size From Image": "Fit Size From Image", + "FS: Fit Image And Resize": "Fit Image And Resize", + "FS: Load Image And Resize To Fit": "Load Image And Resize To Fit", + "FS: Pick Image From Batch": "Pick Image From Batch", + "FS: Crop Image Into Even Pieces": "Crop Image Into Even Pieces", } -__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS'] \ No newline at end of file +__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS'] + +EXTENSION_NAME = "Fitsize" + +symlink_web_dir("js", EXTENSION_NAME) \ No newline at end of file diff --git a/js/widgets.js b/js/widgets.js new file mode 100644 index 0000000..e69de29 diff --git a/nodes.py b/nodes.py index affac57..c0d4d16 100644 --- a/nodes.py +++ b/nodes.py @@ -110,7 +110,7 @@ class FitSize: RETURN_NAMES = ("Fit Width", "Fit Height", "Aspect Ratio") FUNCTION = "fit_to_size" - CATEGORY = "Fitsize" + CATEGORY = "Fitsize/Numbers" def fit_to_size (self, original_width, original_height, max_size, upscale="false"): values = get_max_size(original_width, original_height, max_size, upscale) @@ -134,7 +134,7 @@ class FitSizeFromImage: RETURN_NAMES = ("Fit Width", "Fit Height", "Aspect Ratio") FUNCTION = "fit_to_size_from_image" - CATEGORY = "Fitsize" + CATEGORY = "Fitsize/Numbers" def fit_to_size_from_image (self, image, max_size, upscale="false"): size = get_image_size(image) @@ -160,7 +160,7 @@ class FitResizeImage: RETURN_NAMES = ("Image","Fit Width", "Fit Height", "Aspect Ratio") FUNCTION = "fit_resize_image" - CATEGORY = "Fitsize" + CATEGORY = "Fitsize/Image" def fit_resize_image (self, image, max_size=768, resampling="bicubic", upscale="false", latent=False): size = get_image_size(image) @@ -208,7 +208,7 @@ class FitResizeLatent(): ) FUNCTION = "fit_resize_latent" - CATEGORY = "Fitsize" + CATEGORY = "Fitsize/Image" def fit_resize_latent (self, image, vae, max_size=768, resampling="bicubic", upscale="false", batch_size=1, add_noise=0.0): @@ -252,7 +252,7 @@ class LoadToFitResizeLatent(): ) FUNCTION = "fit_resize_latent" - CATEGORY = "Fitsize" + CATEGORY = "Fitsize/Image" @staticmethod def load_image(image): @@ -299,4 +299,103 @@ class LoadToFitResizeLatent(): new_height, aspect_ratio, mask, - ) \ No newline at end of file + ) + + +class CropImageIntoEvenPieces: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "image": ("IMAGE",), + "rows": ("INT", {"default": 3, "min": 1, "max": 32, "step": 1,}), + "columns": ("INT", {"default": 1, "min": 1, "max": 32, "step": 1,}), + }, + } + + RETURN_TYPES = ("IMAGE",) + + FUNCTION = "run" + + CATEGORY = "Fitsize/Image" + + def run(self, image, rows, columns): + + if rows < 1: + rows = 1 + if columns < 1: + columns = 1 + + w = image.shape[2] # width + h = image.shape[1] # height + + crop_width = int(w / columns) + crop_height = int(h / rows) + + image = image.numpy() + + pieces = [] + for i in range(rows): + for j in range(columns): + y = i * crop_height + x = j * crop_width + + crop = image[: , y : y + crop_height , x : x + crop_width , :] + pieces.append(torch.from_numpy(crop)) + # image[:, y : y + height, x : x + width, :] + + return (torch.cat(pieces, dim=0), ) + + + +class RandomImageFromBatch: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "images": ("IMAGE", ), + "seed": ("INT", {"default": 0}), + "start_index": ("INT", {"default": -1, "min": -1, "max": 32, "step": 1,}), + "select_amount": ("INT", {"default": 1, "min": 1, "max": 32, "step": 1,}), + }, + } + + RETURN_TYPES = ("IMAGE",) + + FUNCTION = "run" + + CATEGORY = "Fitsize/Image" + + def run(self, images, seed, start_index, select_amount): + + # if type(images) == torch.Tensor: + # images = images.numpy() + + if start_index == -1: + start_index = np.random.randint(0, images.shape[0]) + if start_index >= images.shape[0]: + start_index = images.shape[0]-1 + + if select_amount > images.shape[0]: + select_amount = images.shape[0] + if select_amount < 1: + select_amount = 1 + + + print( + f"RandomImageFromBatch: start_index {start_index},", + f"select_amount {select_amount}", + f"total_images {images.shape[0]}",) + + selected = images[start_index:start_index + select_amount] + + print(f"RandomImageFromBatch: selected {selected.shape[0]} images") + + return (selected, ) + diff --git a/startup_utils.py b/startup_utils.py new file mode 100644 index 0000000..76bceb9 --- /dev/null +++ b/startup_utils.py @@ -0,0 +1,31 @@ +import os +from pathlib import Path + +import folder_paths + +# from: https://github.com/M1kep/ComfyLiterals + + +def symlink_web_dir(local_path, extension_name): + comfy_web_ext_root = Path(os.path.join(folder_paths.base_path, "web", "extensions")) + target_dir = Path(os.path.join(comfy_web_ext_root, extension_name)) + extension_path = Path(__file__).parent.resolve() + + if target_dir.exists(): + print(f"Web extensions folder found at {target_dir}") + elif comfy_web_ext_root.exists(): + try: + os.symlink((os.path.join(extension_path, local_path)), target_dir) + except OSError as e: # OSError + print( + f"Error:\n{e}\n" + f"Failed to create symlink to {target_dir}. Please copy the folder manually.\n" + f"Source: {os.path.join(extension_path, local_path)}\n" + f"Target: {target_dir}" + ) + except Exception as e: + print(f"Unexpected error:\n{e}") + else: + print( + f"Failed to find comfy root automatically, please copy the folder {os.path.join(extension_path, 'web')} manually in the web/extensions folder of ComfyUI" + )