diff --git a/README.MD b/README.MD index 93ed538..ccae004 100644 --- a/README.MD +++ b/README.MD @@ -70,6 +70,7 @@ When this error has occurred, please check the network environment. ## Update **If the dependency package error after updating, please reinstall the relevant dependency packages. for details, please refer to [here](https://github.com/chflame163/ComfyUI_LayerStyle/issues/5).
+* Commit [HDR Effect](#HDR) node,it enhances the dynamic range and visual appeal of input images. this node is repackage of [HDR Effects (SuperBeasts.AI)](https://github.com/SuperBeastsAI/ComfyUI-SuperBeasts). * Commit [CropBoxResolve](#CropBoxResolve) node. * Commit [BiRefNetUltra](#BiRefNetUltra) node, it using the BiRefNet model to remove background has better recognition ability and ultra-high edge details. * Commit [ImageAutoCropV2](#ImageAutoCropV2) node, it can choose not to remove the background, support mask input, and scale by long or short side size. @@ -1246,6 +1247,22 @@ Node options: * angle: Angle of channel separation. * mode: Channel shift arrangement order. + +### HDR Effects +enhances the dynamic range and visual appeal of input images. +This node is reorganize and encapsulate of [HDR Effects (SuperBeasts.AI)](https://github.com/SuperBeastsAI/ComfyUI-SuperBeasts), thanks to the original author. +![image](image/hdr_effects_example.png) + +Node options: +![image](image/hdr_effects_node.png) +* hdr_intensity: Range: 0.0 to 5.0, Controls the overall intensity of the HDR effect, Higher values result in a more pronounced HDR effect. +* shadow_intensity: Range: 0.0 to 1.0,Adjusts the intensity of shadows in the image,Higher values darken the shadows and increase contrast. +* highlight_intensity: Range: 0.0 to 1.0,Adjusts the intensity of highlights in the image,Higher values brighten the highlights and increase contrast. +* gamma_intensity: Range: 0.0 to 1.0,Controls the gamma correction applied to the image,Higher values increase the overall brightness and contrast. +* contrast: Range: 0.0 to 1.0,Enhances the contrast of the image, Higher values result in more pronounced contrast. +* enhance_color: Range: 0.0 to 1.0,Enhances the color saturation of the image, Higher values result in more vibrant colors. + + ### Film Simulate the grain, dark edge, and blurred edge of the film, support input depth map to simulate defocus. This node is reorganize and encapsulate of [digitaljohn/comfyui-propost](https://github.com/digitaljohn/comfyui-propost), thanks to the original author. diff --git a/README_CN.MD b/README_CN.MD index 85cf2a8..f5254cc 100644 --- a/README_CN.MD +++ b/README_CN.MD @@ -70,7 +70,7 @@ git clone https://github.com/chflame163/ComfyUI_LayerStyle.git ## 更新说明 **如果本插件更新后出现依赖包错误,请重新安装相关依赖包。 - +* 添加 [HDR Effect](#HDR) 节点,增强图片动态范围。这个节点是[HDR Effects (SuperBeasts.AI)](https://github.com/SuperBeastsAI/ComfyUI-SuperBeasts)的重新封装。感谢原作者。 * 添加 [CropBoxResolve](#CropBoxResolve) 节点。 * 添加 [BiRefNetUltra](#BiRefNetUltra) 节点, 使用BiRefNet模型去除背景,有更好的识别能力,同时具有超高的边缘细节。 * 添加 [ImageAutoCropV2](#ImageAutoCropV2) 节点,可选择不去除背景,支持mask输入,支持按长边或短边尺寸缩放。 @@ -1245,6 +1245,22 @@ mask反转 * angle: 通道分离的角度。 * mode: 通道错位排列顺序。 +* +### HDR Effects +增强图像的动态范围。 +这个节点是[HDR Effects (SuperBeasts.AI)](https://github.com/SuperBeastsAI/ComfyUI-SuperBeasts)的重新封装。感谢原作者。 +![image](image/hdr_effects_example.png) + +节点选项说明: +![image](image/hdr_effects_node.png) +* hdr_intensity: 范围0-5, 控制HDR效果的整体强度, 数值越高,效果越明显。 +* shadow_intensity: 范围0-1,调整图像阴影部分的强度,较高的值会使阴影变暗并增加对比度。 +* highlight_intensity: 范围0-1,调整图像高光部分的强度,较高的值可使高光变亮并增加对比度。 +* gamma_intensity: 范围0-1,用于图像的伽玛校正,值越高,整体亮度和对比度越高。 +* contrast: 范围0-1,增强图像的对比度, 值越高,对比度越明显。 +* enhance_color: 范围0-1,增强图像的色彩饱和度, 值越高,颜色越鲜艳。 + + ### Film 模拟胶片的颗粒、暗边和边缘模糊,支持输入深度图模拟虚焦。 这个节点是[digitaljohn/comfyui-propost](https://github.com/digitaljohn/comfyui-propost)的重新封装,感谢原作者。 diff --git a/image/hdr_effects_example.png b/image/hdr_effects_example.png new file mode 100644 index 0000000..c205f52 Binary files /dev/null and b/image/hdr_effects_example.png differ diff --git a/image/hdr_effects_node.png b/image/hdr_effects_node.png new file mode 100644 index 0000000..fa4e7dc Binary files /dev/null and b/image/hdr_effects_node.png differ diff --git a/py/hdr_effects.py b/py/hdr_effects.py new file mode 100644 index 0000000..553d8d1 --- /dev/null +++ b/py/hdr_effects.py @@ -0,0 +1,162 @@ +from .imagefunc import * +from PIL import ImageCms +from PIL.PngImagePlugin import PngInfo + +NODE_NAME = 'HDR Effects' + +sRGB_profile = ImageCms.createProfile("sRGB") +Lab_profile = ImageCms.createProfile("LAB") + +def adjust_shadows(luminance_array, shadow_intensity, hdr_intensity): + # Darken shadows more as shadow_intensity increases, scaled by hdr_intensity + return np.clip(luminance_array - luminance_array * shadow_intensity * hdr_intensity * 0.5, 0, 255) + + +def adjust_highlights(luminance_array, highlight_intensity, hdr_intensity): + # Brighten highlights more as highlight_intensity increases, scaled by hdr_intensity + return np.clip(luminance_array + (255 - luminance_array) * highlight_intensity * hdr_intensity * 0.5, 0, 255) + + +def apply_adjustment(base, factor, intensity_scale): + """Apply positive adjustment scaled by intensity.""" + # Ensure the adjustment increases values within [0, 1] range, scaling by intensity + adjustment = base + (base * factor * intensity_scale) + # Ensure adjustment stays within bounds + return np.clip(adjustment, 0, 1) + + +def multiply_blend(base, blend): + """Multiply blend mode.""" + return np.clip(base * blend, 0, 255) + + +def overlay_blend(base, blend): + """Overlay blend mode.""" + # Normalize base and blend to [0, 1] for blending calculation + base = base / 255.0 + blend = blend / 255.0 + return np.where(base < 0.5, 2 * base * blend, 1 - 2 * (1 - base) * (1 - blend)) * 255 + + +def adjust_shadows_non_linear(luminance, shadow_intensity, max_shadow_adjustment=1.5): + lum_array = np.array(luminance, dtype=np.float32) / 255.0 # Normalize + # Apply a non-linear darkening effect based on shadow_intensity + shadows = lum_array ** (1 / (1 + shadow_intensity * max_shadow_adjustment)) + return np.clip(shadows * 255, 0, 255).astype(np.uint8) # Re-scale to [0, 255] + + +def adjust_highlights_non_linear(luminance, highlight_intensity, max_highlight_adjustment=1.5): + lum_array = np.array(luminance, dtype=np.float32) / 255.0 # Normalize + # Brighten highlights more aggressively based on highlight_intensity + highlights = 1 - (1 - lum_array) ** (1 + highlight_intensity * max_highlight_adjustment) + return np.clip(highlights * 255, 0, 255).astype(np.uint8) # Re-scale to [0, 255] + + +def merge_adjustments_with_blend_modes(luminance, shadows, highlights, hdr_intensity, shadow_intensity, + highlight_intensity): + # Ensure the data is in the correct format for processing + base = np.array(luminance, dtype=np.float32) + + # Scale the adjustments based on hdr_intensity + scaled_shadow_intensity = shadow_intensity ** 2 * hdr_intensity + scaled_highlight_intensity = highlight_intensity ** 2 * hdr_intensity + + # Create luminance-based masks for shadows and highlights + shadow_mask = np.clip((1 - (base / 255)) ** 2, 0, 1) + highlight_mask = np.clip((base / 255) ** 2, 0, 1) + + # Apply the adjustments using the masks + adjusted_shadows = np.clip(base * (1 - shadow_mask * scaled_shadow_intensity), 0, 255) + adjusted_highlights = np.clip(base + (255 - base) * highlight_mask * scaled_highlight_intensity, 0, 255) + + # Combine the adjusted shadows and highlights + adjusted_luminance = np.clip(adjusted_shadows + adjusted_highlights - base, 0, 255) + + # Blend the adjusted luminance with the original luminance based on hdr_intensity + final_luminance = np.clip(base * (1 - hdr_intensity) + adjusted_luminance * hdr_intensity, 0, 255).astype(np.uint8) + + return Image.fromarray(final_luminance) + + +def apply_gamma_correction(lum_array, intensity, base_gamma): + """ + Apply gamma correction to the luminance array. + :param lum_array: Luminance channel as a NumPy array. + :param intensity: HDR intensity factor. + :param base_gamma: Base gamma value for correction. + """ + if intensity == 0: # If intensity is 0, return the array as is. + return lum_array + + gamma = 1 + (base_gamma - 1) * intensity # Scale gamma based on intensity. + adjusted = 255 * (lum_array / 255) ** gamma + return np.clip(adjusted, 0, 255).astype(np.uint8) + + +class LS_HDREffects: + @classmethod + def INPUT_TYPES(cls): + return {'required': {'image': ('IMAGE', {'default': None}), + 'hdr_intensity': ('FLOAT', {'default': 0.5, 'min': 0.0, 'max': 5.0, 'step': 0.01}), + 'shadow_intensity': ('FLOAT', {'default': 0.25, 'min': 0.0, 'max': 1.0, 'step': 0.01}), + 'highlight_intensity': ('FLOAT', {'default': 0.75, 'min': 0.0, 'max': 1.0, 'step': 0.01}), + 'gamma_intensity': ('FLOAT', {'default': 0.25, 'min': 0.0, 'max': 1.0, 'step': 0.01}), + 'contrast': ('FLOAT', {'default': 0.1, 'min': 0.0, 'max': 1.0, 'step': 0.01}), + 'enhance_color': ('FLOAT', {'default': 0.25, 'min': 0.0, 'max': 1.0, 'step': 0.01}) + }} + + RETURN_TYPES = ('IMAGE',) + RETURN_NAMES = ('image',) + FUNCTION = 'hdr_effects' + CATEGORY = '😺dzNodes/LayerFilter' + + @apply_to_batch + def hdr_effects(self, image, hdr_intensity=0.5, shadow_intensity=0.25, highlight_intensity=0.75, + gamma_intensity=0.25, contrast=0.1, enhance_color=0.25): + # Load the image + img = tensor2pil(image) + + # Step 1: Convert RGB to LAB for better color preservation + img_lab = ImageCms.profileToProfile(img, sRGB_profile, Lab_profile, outputMode='LAB') + + # Extract L, A, and B channels + luminance, a, b = img_lab.split() + + # Convert luminance to a NumPy array for processing + lum_array = np.array(luminance, dtype=np.float32) + + # Preparing adjustment layers (shadows, midtones, highlights) + # This example assumes you have methods to extract or calculate these adjustments + shadows_adjusted = adjust_shadows_non_linear(luminance, shadow_intensity) + highlights_adjusted = adjust_highlights_non_linear(luminance, highlight_intensity) + + merged_adjustments = merge_adjustments_with_blend_modes(lum_array, shadows_adjusted, highlights_adjusted, + hdr_intensity, shadow_intensity, highlight_intensity) + + # Apply gamma correction with a base_gamma value (define based on desired effect) + gamma_corrected = apply_gamma_correction(np.array(merged_adjustments), hdr_intensity, gamma_intensity) + + # Merge L channel back with original A and B channels + adjusted_lab = Image.merge('LAB', (merged_adjustments, a, b)) + + # Step 3: Convert LAB back to RGB + img_adjusted = ImageCms.profileToProfile(adjusted_lab, Lab_profile, sRGB_profile, outputMode='RGB') + + # Enhance contrast + enhancer = ImageEnhance.Contrast(img_adjusted) + contrast_adjusted = enhancer.enhance(1 + contrast) + + # Enhance color saturation + enhancer = ImageEnhance.Color(contrast_adjusted) + color_adjusted = enhancer.enhance(1 + enhance_color * 0.2) + + return pil2tensor(color_adjusted) + + +NODE_CLASS_MAPPINGS = { + "LayerFilter: HDREffects": LS_HDREffects +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "LayerFilter: HDREffects": "LayerFilter: HDR Effects" +} \ No newline at end of file diff --git a/py/imagefunc.py b/py/imagefunc.py index e970e04..0f10aef 100644 --- a/py/imagefunc.py +++ b/py/imagefunc.py @@ -53,6 +53,20 @@ except ImportError as e: f"\nFor detail refer to \033[4mhttps://github.com/chflame163/ComfyUI_LayerStyle/issues/5\033[0m") + +'''warpper''' + +# create a wrapper function that can apply a function to multiple images in a batch while passing all other arguments to the function +def apply_to_batch(func): + def wrapper(self, image, *args, **kwargs): + images = [] + for img in image: + images.append(func(self, img, *args, **kwargs)) + batch_tensor = torch.cat(images, dim=0) + return (batch_tensor,) + return wrapper + + '''pickle'''