diff --git a/README.MD b/README.MD index ac57ff2..f922877 100644 --- a/README.MD +++ b/README.MD @@ -80,6 +80,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.
+* [GetColorToneV2](#GetColorToneV2) node add the ```mask``` method to the color selection option, which can accurately obtain the main color and average color within the mask. * [ImageScaleByAspectRatioV2](#ImageScaleByAspectRatioV2) node add the "background_color" option. * [LUT Apply](#LUT) Add the "strength" option. * Commit [AutoAdjustV2](#AutoAdjustV2) node, add optional mask input and support for multiple automatic color adjustment modes. @@ -814,13 +815,14 @@ V2 upgrade of GetColorTone. You can specify the dominant or average color to get The following changes have been made on the basis of GetColorTong: ![image](image/get_color_tone_v2_node.jpg) -* color_of: Provides three options, entire, background, and subject, to select the color of the entire picture, background, or body, respectively. +* color_of: Provides 4 options, mask, entire, background, and subject, to select the color of the mask area, entire picture, background, or subject, respectively. * remove_background_method: There are two methods of background recognition: BiRefNet and RMBG V1.4. * invert_mask: Whether to reverse the mask. * mask_grow: Mask expansion. For subject, a larger value brings the obtained color closer to the color at the center of the body. Output: * image: Solid color picture output, the size is the same as the input picture. +* mask: Mask output. ### ExtendCanvas Extend the canvas diff --git a/README_CN.MD b/README_CN.MD index cd6c3fd..fb9bc9a 100644 --- a/README_CN.MD +++ b/README_CN.MD @@ -80,6 +80,7 @@ git clone https://github.com/chflame163/ComfyUI_LayerStyle.git ## 更新说明 **如果本插件更新后出现依赖包错误,请重新安装相关依赖包。 +* [GetColorToneV2](#GetColorToneV2) 节点的取色选项增加```mask```方法,可精确获取遮罩内的主色和平均色。 * [ImageScaleByAspectRatioV2](#ImageScaleByAspectRatioV2) 节点增加background_color选项。 * [LUT Apply](#LUT) 节点增加strenght选项。 * 添加 [AutoAdjustV2](#AutoAdjustV2) 节点,增加可选遮罩输入,增加多种自动调色模式支持。 @@ -800,18 +801,19 @@ ImageScaleByAspectRatio的V2升级版 ### GetColorToneV2 GetColorTone的V2升级版。可以指定获取主体或背景的主色或平均色。 -![image](image/get_color_tone_v2_example.jpg) +![image](image/get_color_tone_v2_example.jpg) +![image](image/get_color_tone_v2_example2.jpg) 在GetColorTong基础上做了如下改变: ![image](image/get_color_tone_v2_node.jpg) -* color_of: 提供三个选项,entire, background和subject, 分别表示选择整个图片,背景,或主体的颜色。 +* color_of: 提供4个选项,mask, entire, background和subject, 分别表示选择遮罩区域,整个图片,背景,或主体的颜色。 * remove_background_method: 背景识别的方法, 有BiRefNet和RMBG V1.4两种可以选择。 * invert_mask: 是否反转遮罩。 * mask_grow: 遮罩扩张。对于subject, 更大的值使获得的颜色更接近主体中心的颜色。 输出: * image: 纯色图片输出, 尺寸与输入的图片相同。 - +* mask: 遮罩输出。 ### ExtendCanvas 扩展画布。 diff --git a/image/get_color_tone_v2_example2.jpg b/image/get_color_tone_v2_example2.jpg new file mode 100644 index 0000000..606ebc9 Binary files /dev/null and b/image/get_color_tone_v2_example2.jpg differ diff --git a/image/get_color_tone_v2_node.jpg b/image/get_color_tone_v2_node.jpg index 8717c99..5f656a9 100644 Binary files a/image/get_color_tone_v2_node.jpg and b/image/get_color_tone_v2_node.jpg differ diff --git a/py/get_color_tone_v2.py b/py/get_color_tone_v2.py index be16ecc..630548b 100644 --- a/py/get_color_tone_v2.py +++ b/py/get_color_tone_v2.py @@ -9,8 +9,8 @@ class GetColorToneV2: @classmethod def INPUT_TYPES(self): - remove_background_list = ['BiRefNet', 'RMBG 1.4',] - subject_list = ['entire', 'background', 'subject'] + remove_background_list = ['none','BiRefNet', 'RMBG 1.4',] + subject_list = ['mask','entire', 'background', 'subject'] mode_list = ['main_color', 'average'] return { "required": { @@ -19,15 +19,15 @@ class GetColorToneV2: "color_of": (subject_list,), "remove_bkgd_method": (remove_background_list,), "invert_mask": ("BOOLEAN", {"default": False}), # 反转mask# - "mask_grow": ("INT", {"default": 16, "min": -999, "max": 999, "step": 1}), + "mask_grow": ("INT", {"default": 0, "min": -999, "max": 999, "step": 1}), }, "optional": { "mask": ("MASK",), # } } - RETURN_TYPES = ("IMAGE", "STRING", "LIST",) - RETURN_NAMES = ("image", "color_in_hex", "HSV color in list",) + RETURN_TYPES = ("IMAGE", "STRING", "LIST", "MASK") + RETURN_NAMES = ("image", "color_in_hex", "HSV color in list", "mask",) FUNCTION = 'get_color_tone_v2' CATEGORY = '😺dzNodes/LayerUtility' @@ -38,6 +38,7 @@ class GetColorToneV2: _images = [] _masks = [] ret_images = [] + ret_masks = [] need_rmbg = False for i in image: _images.append(torch.unsqueeze(i, 0)) @@ -45,14 +46,15 @@ class GetColorToneV2: if m.mode == 'RGBA': _masks.append(1 - image2mask(m.split()[-1])) else: - need_rmbg = True + _masks.append(pil2tensor(Image.new("L", (m.width, m.height), color="white"))) + if remove_bkgd_method != 'none': + need_rmbg = True + if mask is not None: if mask.dim() == 2: mask = torch.unsqueeze(mask, 0) _masks = [] for m in mask: - if not invert_mask: - m = 1 - m _masks.append(torch.unsqueeze(m, 0)) need_rmbg = False @@ -71,32 +73,40 @@ class GetColorToneV2: else: _mask = RMBG(_image) _mask = image2mask(_mask) - _mask = 1 - _mask else: _mask = _masks[i] if i < len(_masks) else _masks[-1] + if invert_mask: + _mask = 1 - _mask + if mask_grow != 0: _mask = expand_mask(_mask, mask_grow, 0) # 扩张,模糊 - if color_of == 'subject': - _mask = 1 - _mask - if color_of == 'entire': blured_image = gaussian_blur(_image, int((_image.width + _image.height) / 400)) else: + if color_of == 'background': + _mask = 1 - _mask _mask = tensor2pil(_mask) pixel_spread_image = pixel_spread(_image, _mask.convert('RGB')) blured_image = gaussian_blur(pixel_spread_image, int((_image.width + _image.height) / 400)) - if mode == 'main_color': + + ret_color = '#000000' + if mode == 'main_color' and color_of != 'mask': ret_color = get_image_color_tone(blured_image) - else: + elif mode == 'average' and color_of != 'mask': ret_color = get_image_color_average(blured_image) + elif mode == 'main_color' and color_of == 'mask': + ret_color = get_image_color_tone(blured_image, mask=_mask) + elif mode == 'average' and color_of == 'mask': + ret_color = get_image_color_average(blured_image, mask=_mask) ret_image = Image.new('RGB', size=_image.size, color=ret_color) ret_images.append(pil2tensor(ret_image)) + ret_masks.append(pil2tensor(_mask)) log(f"{NODE_NAME} Processed {len(ret_images)} image(s).", message_type='finish') hsv_color = RGB_to_HSV(Hex_to_RGB(ret_color)) - return (torch.cat(ret_images, dim=0), ret_color, hsv_color,) + return (torch.cat(ret_images, dim=0), ret_color, hsv_color, torch.cat(ret_masks, dim=0),) NODE_CLASS_MAPPINGS = { "LayerUtility: GetColorToneV2": GetColorToneV2 diff --git a/py/imagefunc.py b/py/imagefunc.py index ceb400f..1fa46e7 100644 --- a/py/imagefunc.py +++ b/py/imagefunc.py @@ -893,12 +893,22 @@ def adjust_levels(image:Image, input_black:int=0, input_white:int=255, midtones: img = img.astype(np.uint8) return cv22pil(img) - -def get_image_color_tone(image:Image) -> str: +def get_image_color_tone(image:Image, mask:Image=None) -> str: image = image.convert('RGB') max_score = 0.0001 dominant_color = (255, 255, 255) - for count, (r, g, b) in image.getcolors(image.width * image.height): + if mask is not None: + if mask.mode != 'L': + mask = mask.convert('L') + canvas = Image.new('RGB', size=image.size, color='black') + canvas.paste(image, mask=mask) + image = canvas + + all_colors = image.getcolors(image.width * image.height) + for count, (r, g, b) in all_colors: + if mask is not None: + if r + g + b < 2: # 忽略黑色 + continue saturation = colorsys.rgb_to_hsv(r / 255.0, g / 255.0, b / 255.0)[1] y = min(abs(r * 2104 + g * 4130 + b * 802 + 4096 + 131072) >> 13,235) y = (y - 16.0) / (235 - 16) @@ -909,22 +919,29 @@ def get_image_color_tone(image:Image) -> str: ret_color = RGB_to_Hex(dominant_color) return ret_color -def get_image_color_average(image:Image) -> str: +def get_image_color_average(image:Image, mask:Image=None) -> str: image = image.convert('RGB') width, height = image.size total_red = 0 total_green = 0 total_blue = 0 + total_pixel =0 for y in range(height): for x in range(width): + if mask is not None: + if mask.mode != 'L': + mask = mask.convert('L') + if mask.getpixel((x, y)) <= 127: + continue rgb = image.getpixel((x, y)) total_red += rgb[0] total_green += rgb[1] total_blue += rgb[2] + total_pixel += 1 - average_red = total_red // (width * height) - average_green = total_green // (width * height) - average_blue = total_blue // (width * height) + average_red = total_red // total_pixel + average_green = total_green // total_pixel + average_blue = total_blue // total_pixel color = (average_red, average_green, average_blue) ret_color = RGB_to_Hex(color) return ret_color diff --git a/pyproject.toml b/pyproject.toml index 7f297a1..99ddead 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui_layerstyle" description = "A set of nodes for ComfyUI it generate image like Adobe Photoshop's Layer Style. the Drop Shadow is first completed node, and follow-up work is in progress." -version = "1.0.12" +version = "1.0.13" license = "MIT" dependencies = ["numpy", "pillow", "torch", "matplotlib", "Scipy", "scikit_image", "opencv-contrib-python", "pymatting", "segment_anything", "timm", "addict", "yapf", "colour-science", "wget", "mediapipe", "loguru", "typer_config", "fastapi", "rich", "google-generativeai", "diffusers", "omegaconf", "tqdm", "transformers", "kornia", "image-reward", "ultralytics", "blend_modes", "blind-watermark", "qrcode", "pyzbar", "psd-tools"]