diff --git a/py/gradient_map.py b/py/gradient_map.py new file mode 100644 index 0000000..29e116a --- /dev/null +++ b/py/gradient_map.py @@ -0,0 +1,85 @@ +import torch +from PIL import Image +import numpy as np +from .imagefunc import * + +NODE_NAME = 'GradientMap' + +class GradientMap: + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "image": ("IMAGE",), + "start_color": ("STRING", {"default": "#015A52"}), + "mid_color": ("STRING", {"default": "#02AF9F"}), + "end_color": ("STRING", {"default": "#7FFFEC"}), + "mid_point": ("FLOAT", {"default": 0.6, "min": 0.0, "max": 1.0, "step": 0.01}), + "opacity": ("INT", {"default": 100, "min": 0, "max": 100, "step": 1}), + }, + "optional": { + "layer_mask": ("MASK",), + } + } + + RETURN_TYPES = ("IMAGE", "IMAGE") + RETURN_NAMES = ("image", "gradient") + FUNCTION = 'apply_gradient_map' + CATEGORY = '😺dzNodes/LayerStyle' + + def apply_gradient_map(self, image, start_color, mid_color, end_color, mid_point, opacity, layer_mask=None): + def create_gradient_array(start_color, mid_color, end_color, mid_point): + start_rgb = Hex_to_RGB(start_color) + mid_rgb = Hex_to_RGB(mid_color) + end_rgb = Hex_to_RGB(end_color) + + mid_index = int(255 * mid_point) + gradient1 = np.array([np.linspace(start_rgb[i], mid_rgb[i], mid_index + 1) for i in range(3)]).T + gradient2 = np.array([np.linspace(mid_rgb[i], end_rgb[i], 256 - mid_index) for i in range(3)]).T + return np.vstack((gradient1[:-1], gradient2)) + + gradient_array = create_gradient_array(start_color, mid_color, end_color, mid_point) + + gradient_image = Image.fromarray(np.uint8(gradient_array.reshape(1, -1, 3).repeat(50, axis=0))) + gradient_tensor = pil2tensor(gradient_image) + ret_images = [] + for img in image: + pil_image = tensor2pil(img) + + # Convert to grayscale to get luminance + gray_image = np.array(pil_image.convert('L')) + + # Apply gradient map + gradient_mapped = gradient_array[gray_image] + + # Preserve luminance of original image + original_array = np.array(pil_image) + luminance = np.sum(original_array * [0.299, 0.587, 0.114], axis=2, keepdims=True) / 255.0 + gradient_mapped = gradient_mapped * luminance + original_array * (1 - luminance) + + gradient_mapped_image = Image.fromarray(np.uint8(gradient_mapped)) + + # Apply opacity + if opacity < 100: + gradient_mapped_image = Image.blend(pil_image, gradient_mapped_image, opacity / 100) + + # Apply mask if provided + if layer_mask is not None: + mask = tensor2pil(layer_mask).convert('L') + pil_image.paste(gradient_mapped_image, (0, 0), mask) + else: + pil_image = gradient_mapped_image + + ret_images.append(pil2tensor(pil_image)) + + log(f"{NODE_NAME} Processed {len(ret_images)} image(s).", message_type='finish') + return (torch.cat(ret_images, dim=0), gradient_tensor) + +NODE_CLASS_MAPPINGS = { + "LayerStyle: Gradient Map": GradientMap +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "LayerStyle: Gradient Map": "LayerStyle: Gradient Map" +} \ No newline at end of file