diff --git a/README.md b/README.md index e51d466..b5caacc 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,9 @@ Currently we just have a few nodes used by other nodes I maintain. - [Image Download and Load](#1-image-download-and-load) - [Face Composite](#2-face-composite) - [Face Composite (frame by frame)](#3-face-composite-frame-by-frame) + - [Normalize Image to ImageNet](#4-normalize-image-to-imagenet) + - [Normalize Image to [-0.5, 0.5]](#5-normalize-image-to-05-05) + - [Normalize Image to [-1, 1]](#6-normalize-image-to-1-1) - 📝 [Usage Notes](#-usage-notes) - 📜 [Project History](#-project-history) - ⚖️ [License](#️-license) @@ -81,6 +84,50 @@ Currently we just have a few nodes used by other nodes I maintain. - **How it Works:** The node iterates from frame 0 to N-1. In each step, it takes the i-th `animated` frame and the i-th `reference` frame. It then pastes the animated frame onto the reference frame using the coordinates from the single, static bounding box. +### 4. Normalize Image to ImageNet + - **Display Name:** `"Normalize Image to ImageNet` + - **Internal Name:** `SET_NormalizeToImageNetDataset` + - **Category:** `image/normalization` + - **Description:** Normalizes an image tensor using the mean and standard deviation of the ImageNet dataset. + - **Purpose:** Essential for pre-processing images before feeding them into models that were pre-trained on ImageNet (e.g., most ResNet, VGG, EfficientNet models). + - **Inputs:** + - `image` (`IMAGE`): A standard ComfyUI image tensor in the `[0, 1]` range. + - **Output:** + - `image` (`IMAGE`): The normalized image tensor. The value range will be altered significantly. + - **How it Works:** For each channel, it performs the operation `output = (input - mean) / std`, using the standard ImageNet values: + - **Mean:** `[0.485, 0.456, 0.406]` + - **Std Dev:** `[0.229, 0.224, 0.225]` + +### 5. Normalize Image to [-0.5, 0.5] + +- **Display Name:** `Normalize Image to [-0.5, 0.5]` +- **Internal Name:** `SET_NormalizeToMinus05_05` +- **Category:** `image/normalization` +- **Description:** Normalizes an image tensor by centering its values around zero. +- **Purpose:** Useful for models trained from scratch or those that expect input data in the `[-0.5, 0.5]` range. This can help stabilize training. +- **Inputs:** + - `image` (`IMAGE`): A standard ComfyUI image tensor in the `[0, 1]` range. +- **Output:** + - `image` (`IMAGE`): The normalized image tensor, with values in the `[-0.5, 0.5]` range. +- **How it Works:** For each channel, it performs the operation `output = (input - mean) / std`, using: + - **Mean:** `[0.5, 0.5, 0.5]` + - **Std Dev:** `[1.0, 1.0, 1.0]` + +### 6. Normalize Image to [-1, 1] + +- **Display Name:** `Normalize Image to [-1, 1]` +- **Internal Name:** `SET_NormalizeToMinus1_1` +- **Category:** `image/normalization` +- **Description:** Normalizes an image tensor to the `[-1, 1]` range. +- **Purpose:** A common requirement for certain model architectures, particularly Generative Adversarial Networks (GANs) and models using the `tanh` activation function in their output layer. +- **Inputs:** + - `image` (`IMAGE`): A standard ComfyUI image tensor in the `[0, 1]` range. +- **Output:** + - `image` (`IMAGE`): The normalized image tensor, with values in the `[-1, 1]` range. +- **How it Works:** For each channel, it performs the operation `output = (input - mean) / std`, using: + - **Mean:** `[0.5, 0.5, 0.5]` + - **Std Dev:** `[0.5, 0.5, 0.5]` + ## 🚀 Installation You can install the nodes from the ComfyUI nodes manager, the name is *Image Misc*, or just do it manually: diff --git a/src/nodes/nodes_img.py b/src/nodes/nodes_img.py index 3a21442..ae3e861 100644 --- a/src/nodes/nodes_img.py +++ b/src/nodes/nodes_img.py @@ -11,6 +11,7 @@ from seconohe.downloader import download_file # We are the main source, so we use the main_logger from . import main_logger import torch +import torchvision.transforms.functional as TF from typing import Optional try: from folder_paths import get_input_directory # To get the ComfyUI input directory @@ -29,6 +30,7 @@ logger = main_logger BASE_CATEGORY = "image" IO_CATEGORY = "io" MANIPULATION_CATEGORY = "manipulation" +NORMALIZATION = "normalization" def tensor_to_pil(tensor: torch.Tensor) -> Image.Image: @@ -315,3 +317,73 @@ class CompositeFaceFrameByFrame(CompositeFace): final_batch = torch.stack(output_images) return (final_batch,) + + +class NormalizeToImageNetDataset(): + """ + A ComfyUI node to normalize the values to the mean/std of the ImageNet dataset + """ + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "image": ("IMAGE",), + }, + } + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("image",) + FUNCTION = "normalize" + CATEGORY = BASE_CATEGORY + "/" + NORMALIZATION + DESCRIPTION = ("Normalize the image to the ImageNet dataset") + UNIQUE_NAME = "SET_NormalizeToImageNetDataset" + DISPLAY_NAME = "Normalize Image to ImageNet" + imagenet_normalize = None + + def normalize(self, image: torch.Tensor): + return (TF.normalize(image.permute(0, 3, 1, 2), # BHWC -> BCHW + mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]).permute(0, 2, 3, 1),) # BCHW -> BHWC + + +class NormalizeToRangeMinus05to05(): + """ + A ComfyUI node to normalize the values to the [-0.5, 0.5] range + """ + @classmethod + def INPUT_TYPES(cls): + return {"required": {"image": ("IMAGE",), }, } + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("image",) + FUNCTION = "normalize" + CATEGORY = BASE_CATEGORY + "/" + NORMALIZATION + DESCRIPTION = ("Normalize the image to [-0.5, 0.5]") + UNIQUE_NAME = "SET_NormalizeToRangeMinus05to05" + DISPLAY_NAME = "Normalize Image to [-0.5, 0.5]" + imagenet_normalize = None + + def normalize(self, image: torch.Tensor): + return (TF.normalize(image.permute(0, 3, 1, 2), # BHWC -> BCHW + mean=[0.5, 0.5, 0.5], + std=[1.0, 1.0, 1.0]).permute(0, 2, 3, 1),) # BCHW -> BHWC + + +class NormalizeToRangeMinus1to1(): + """ + A ComfyUI node to normalize the values to the [-1, 1] range + """ + @classmethod + def INPUT_TYPES(cls): + return {"required": {"image": ("IMAGE",), }, } + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("image",) + FUNCTION = "normalize" + CATEGORY = BASE_CATEGORY + "/" + NORMALIZATION + DESCRIPTION = ("Normalize the image to [-1, 1]") + UNIQUE_NAME = "SET_NormalizeToRangeMinus1to1" + DISPLAY_NAME = "Normalize Image to [-1, 1] (i.e. GAN)" + imagenet_normalize = None + + def normalize(self, image: torch.Tensor): + return (TF.normalize(image.permute(0, 3, 1, 2), # BHWC -> BCHW + mean=[0.5, 0.5, 0.5], + std=[0.5, 0.5, 0.5]).permute(0, 2, 3, 1),) # BCHW -> BHWC