Commit GetColorTone, MaskGrow and MaskEdgeShink nodes.

Commit GetColorTone node, can obtain the main color or average color of the image. 
Commit MaskGrow and MaskEdgeShink node.
This commit is contained in:
chflame163
2024-01-24 20:38:44 +08:00
committed by GitHub
parent 8fb316595b
commit 7ff4f8ee10
16 changed files with 362 additions and 31 deletions
+48 -13
View File
@@ -12,10 +12,12 @@ Nodes are divided into four groups according to their functions: LayerStyle, Lay
[中文说明点这里](./README_CN.MD)
## Update
* Commit [MaskBoxDetect](#MaskBoxDetect)node, which can automatically detect the position through the mask and output it to the composite node.
Commit [X,Y to Percent](#Percent) node to convert absolute coordinates to percent coordinates.
Commit [GaussianBlur](#GaussianBlur)node.
Commit [GetImageSize](#GetImageSize)node.
* Commit [GetColorTone](#GetColorTone) node, can obtain the main color or average color of the image.
Commit [MaskGrow](#MaskGrow) and [MaskEdgeShink](#MaskEdgeShrink) nodes.
* Commit [MaskBoxDetect](#MaskBoxDetect) node, which can automatically detect the position through the mask and output it to the composite node.
Commit [XY to Percent](#Percent) node to convert absolute coordinates to percent coordinates.
Commit [GaussianBlur](#GaussianBlur) node.
Commit [GetImageSize](#GetImageSize) node.
* Commit [ExtendCanvas](#ExtendCanvas) node.
* Commit [ImageBlendAdvance](#ImageBlendAdvance) node. This node allows for the synthesis of background images and layers of different sizes, providing a more free synthesis experience.
Commit [PrintInfo](#PrintInfo) node as a workflow debugging aid.
@@ -173,7 +175,6 @@ Node options:
# <a id="table1">LayerMask</a>
![image](image/layermask_nodes.png)
@@ -198,6 +199,30 @@ Output:
* y: The y-coordinate of the top left corner position.
### <a id="table1">MaskGrow</a>
Grow and shrink edges and blur the mask
Node options:
![image](image/mask_grow_node.png)
* invert_mask: Whether to reverse the mask.
* grow: Positive values expand outward, while negative values contract inward.
* blur: Blur the edge.
### <a id="table1">MaskEdgeShrink</a>
Smooth transition and shrink the mask edges while preserving edge details.
Node options:
![image](image/mask_edge_shrink_node.png)
* invert_mask: Whether to reverse the mask.
* shrink_level: Shrink the smoothness level.
* soft: Smooth amplitude.
* edge_shrink: Edge shrinkage amplitude.
* edge_reserve: Preserve the amplitude of edge details, 100 represents complete preservation, and 0 represents no preservation at all.
Comparison of MaskGrow and MaskEdgeShrink
![image](image/mask_edge_compare.png)
### <a id="table1">MaskPreview</a>
Preview the input mask
![image](image/mask_invert.png)
@@ -270,7 +295,18 @@ Modify web extensions from [mtb nodes](https://github.com/melMass/comfy_mtb). Se
Node options:
* mode: The output format is available in hexadecimal (HEX) and decimal (DEC).
Output Type:
Output type:
* string
### <a id="table1">GetColorTone</a>
Obtain the main color or average color from the image and output RGB values.
![image](image/get_color_tone_example.png)
Node options:
![image](image/get_color_tone_node.png)
* mode: There are two modes to choose from, with the main color and average color.
Output type:
* string
### <a id="table1">ExtendCanvas</a>
@@ -286,7 +322,7 @@ Node options:
* right: Right extension value.
* color; Color of canvas.
### X,Y to <a id="table1">Percent</a>
### XY to <a id="table1">Percent</a>
![image](image/xy2percent_example.png)
Convert absolute coordinates to percentage coordinates.
@@ -337,27 +373,26 @@ image Some JSON workflow files in the workflow directory, that is example for Co
## The following tasks are in the planned list
LayerUtility:
* color image
* gradient image
* image channel split
* image channel merge
LayerMask:
* text mask
* gradient mask
* mask grow&blur
* mask subtract
* mask combine
* mask diffrent
* mask gradient
* mask softedge
LayerFilter:
* blur
* emboss
* contour
* findedge
LayerColor:
RGB adjust
HSV adjust
brightness & contrast adjust
LUT apply
## How to install
+42 -7
View File
@@ -9,7 +9,8 @@ ComfyUI
* [LayerFilter](#LayerFilter)节点组提供图像效果滤镜。
## 更新说明
* 添加[MaskBoxDetect](#MaskBoxDetect)节点,可以通过mask自动探测位置并输出到合成节点。添加[X,Y to Percent](#Percent)节点,将绝对坐标转换为percent坐标。添加[GaussianBlur](#GaussianBlur)节点。添加[GetImageSize](#GetImageSize)节点。
* 添加[GetColorTone](#GetColorTone)节点,可以获取图片的主色或平均色。添加[MaskGrow](#MaskGrow)和[MaskEdgeShink](#MaskEdgeShrink)节点。
* 添加[MaskBoxDetect](#MaskBoxDetect)节点,可以通过mask自动探测位置并输出到合成节点。添加[XY to Percent](#Percent)节点,将绝对坐标转换为percent坐标。添加[GaussianBlur](#GaussianBlur)节点。添加[GetImageSize](#GetImageSize)节点。
* 添加[ExtendCanvas](#ExtendCanvas)节点。
* 添加[ImageBlendAdvance](#ImageBlendAdvance)节点。这个节点允许合成尺寸不同的背景图和图层,提供了更加自由的合成体验。
添加[PrintInfo](#PrintInfo)节点作为工作流调试辅助工具。
@@ -190,6 +191,28 @@ ComfyUI
* x: 左上角位置x坐标输出。
* y: 左上角位置y坐标输出。
### <a id="table1">MaskGrow</a>
对mask进行扩张收缩边缘和模糊处理
节点选项说明:
![image](image/mask_grow_node.png)
* invert_mask: 是否反转遮罩。
* grow: 扩张幅度。正值是向外扩张,负值是向内收缩。
* blur: 模糊。
### <a id="table1">MaskEdgeShrink</a>
使mask边缘平滑地过渡收缩,并保留边缘细节。
节点选项说明:
![image](image/mask_edge_shrink_node.png)
* invert_mask: 是否反转遮罩。
* shrink_level: 收缩平滑级别。
* soft: 平滑幅度。
* edge_shrink: 边缘收缩幅度。
* edge_reserve: 保留边缘细节幅度, 100为完全保留,0为完全不保留。
MaskGrow与MaskEdgeShrink效果对比
![image](image/mask_edge_compare.png)
### <a id="table1">MaskPreview</a>
预览mask
@@ -267,6 +290,17 @@ mask
输出:
* string
### <a id="table1">GetColorTone</a>
从图片中获取主颜色或平均色。
![image](image/get_color_tone_example.png)
节点选项说明:
![image](image/get_color_tone_node.png)
* mode: 模式,有两种可选择,主颜色main_color和平均色average。
输出:
* string
### <a id="table1">ExtendCanvas</a>
扩展画布。
![image](image/extend_canvas_example.png)
@@ -280,7 +314,7 @@ mask
* right: 右侧扩展值。
* color; 画布颜色
### X,Y to <a id="table1">Percent</a>
### XY to <a id="table1">Percent</a>
![image](image/xy2percent_example.png)
将绝对坐标转换为百分比坐标。
@@ -333,26 +367,27 @@ mask
## 以下工作计划中
LayerUtility:
* color image
* gradient image
* image channel split
* image channel merge
LayerMask:
* text mask
* gradient mask
* mask grow&blur
* mask subtract
* mask combine
* mask diffrent
* mask gradient
* mask softedge
LayerFilter:
* emboss
* contour
* findedge
LayerColor:
RGB adjust
HSV adjust
brightness & contrast adjust
LUT apply
## 安装方法
* 在CompyUI插件目录(例如“CompyUI\custom_nodes\”)中打开cmd窗口,键入```git clone https://github.com/chflame163/ComfyUI_LayerStyle.git```安装。或者下载解压zip文件,将得到的文件夹复制到 ComfyUI\custom_nodes\
Binary file not shown.

After

Width:  |  Height:  |  Size: 451 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

+38
View File
@@ -0,0 +1,38 @@
from .imagefunc import *
class ColorImage:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(self):
return {
"required": {
"width": ("INT", {"default": 512, "min": 4, "max": 99999, "step": 1}),
"height": ("INT", {"default": 512, "min": 4, "max": 99999, "step": 1}),
"color": ("STRING", {"default": "#000000"},),
},
"optional": {
}
}
RETURN_TYPES = ("IMAGE", )
RETURN_NAMES = ("image", )
FUNCTION = 'color_image'
CATEGORY = '😺dzNodes/LayerUtility'
OUTPUT_NODE = True
def color_image(self, width, height, color, ):
ret_image = Image.new('RGB', (width, height), color=color)
return (pil2tensor(ret_image), )
NODE_CLASS_MAPPINGS = {
"LayerUtility: ColorImage": ColorImage
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LayerUtility: ColorImage": "LayerUtility: ColorImage"
}
+1 -3
View File
@@ -1,8 +1,6 @@
from .imagefunc import Hex_to_RGB
mode_list = ['HEX', 'DEC']
class ColorPicker:
def __init__(self):
@@ -10,7 +8,7 @@ class ColorPicker:
@classmethod
def INPUT_TYPES(self):
mode_list = ['HEX', 'DEC']
return {
"required": {
"color": ("COLOR", {"default": "#FFFFFF"},),
+43
View File
@@ -0,0 +1,43 @@
from .imagefunc import *
class GetColorTone:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(self):
mode_list = ['main_color', 'average']
return {
"required": {
"image": ("IMAGE", ), #
"mode": (mode_list,), # 主色/平均色
},
"optional": {
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("RGB color in HEX",)
FUNCTION = 'get_color_tone'
CATEGORY = '😺dzNodes/LayerUtility'
OUTPUT_NODE = True
def get_color_tone(self, image, mode,):
_canvas = tensor2pil(image).convert('RGB')
_canvas = gaussian_blur(_canvas, int((_canvas.width + _canvas.height) / 200))
if mode == 'main_color':
ret_color = get_image_color_tone(_canvas)
else:
ret_color = get_image_color_average(_canvas)
return (ret_color,)
NODE_CLASS_MAPPINGS = {
"LayerUtility: GetColorTone": GetColorTone
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LayerUtility: GetColorTone": "LayerUtility: GetColorTone"
}
-4
View File
@@ -1,9 +1,5 @@
from .imagefunc import *
def process(image:Image) -> Image:
return image
class GetImageSize:
def __init__(self):
+41
View File
@@ -0,0 +1,41 @@
from .imagefunc import *
class GradientImage:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(self):
return {
"required": {
"width": ("INT", {"default": 512, "min": 4, "max": 99999, "step": 1}),
"height": ("INT", {"default": 512, "min": 4, "max": 99999, "step": 1}),
"angle": ("INT", {"default": 0, "min": -360, "max": 360, "step": 1}),
"start_color": ("STRING", {"default": "#FFFFFF"},),
"end_color": ("STRING", {"default": "#000000"},),
},
"optional": {
}
}
RETURN_TYPES = ("IMAGE", )
RETURN_NAMES = ("image", )
FUNCTION = 'gradient_image'
CATEGORY = '😺dzNodes/LayerUtility'
OUTPUT_NODE = True
def gradient_image(self, width, height, angle, start_color, end_color, ):
ret_image = gradint(start_color, end_color, width, height, angle)
return (pil2tensor(ret_image), )
NODE_CLASS_MAPPINGS = {
"LayerUtility: GradientImage": GradientImage
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LayerUtility: GradientImage": "LayerUtility: GradientImage"
}
+37
View File
@@ -7,6 +7,7 @@ import scipy.ndimage
import cv2
from typing import Union, List
from PIL import Image, ImageFilter, ImageChops, ImageDraw
import colorsys
def log(message):
name = 'LayerStyle'
@@ -213,6 +214,42 @@ def draw_rect(image:Image, x:int, y:int, width:int, height:int, line_color:str,
draw.rectangle((x, y, x + width, y + height), fill=box_color, outline=line_color, width=line_width, )
return image
def get_image_color_tone(image:Image) -> str:
image = image.convert('RGB')
max_score = 0.0001
dominant_color = None
for count, (r, g, b) in image.getcolors(image.size[0] * image.size[1]):
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)
if y>0.9:
continue
score = (saturation+0.1)*count
if score > max_score:
max_score = score
dominant_color = (r, g, b)
ret_color = RGB_to_Hex(dominant_color)
return ret_color
def get_image_color_average(image:Image) -> str:
image = image.convert('RGB')
width, height = image.size
total_red = 0
total_green = 0
total_blue = 0
for y in range(height):
for x in range(width):
rgb = image.getpixel((x, y))
total_red += rgb[0]
total_green += rgb[1]
total_blue += rgb[2]
average_red = total_red // (width * height)
average_green = total_green // (width * height)
average_blue = total_blue // (width * height)
color = (average_red, average_green, average_blue)
ret_color = RGB_to_Hex(color)
return ret_color
'''Mask Functions'''
+68
View File
@@ -0,0 +1,68 @@
from .imagefunc import *
class MaskEdgeShrink:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(self):
return {
"required": {
"mask": ("MASK", ), #
"invert_mask": ("BOOLEAN", {"default": True}), # 反转mask
"shrink_level": ("INT", {"default": 4, "min": 0, "max": 16, "step": 1}),
"soft": ("INT", {"default": 6, "min": 0, "max": 64, "step": 1}),
"edge_shrink": ("INT", {"default": 1, "min": 0, "max": 999, "step": 1}),
"edge_reserve": ("INT", {"default": 25, "min": 0, "max": 100, "step": 1}), # 透明度
},
"optional": {
}
}
RETURN_TYPES = ("MASK",)
RETURN_NAMES = ("mask",)
FUNCTION = 'mask_edge_shrink'
CATEGORY = '😺dzNodes/LayerMask'
OUTPUT_NODE = True
def mask_edge_shrink(self, mask, invert_mask, shrink_level, soft, edge_shrink, edge_reserve):
glow_range = shrink_level * soft
blur = 12
_inputmask = mask2image(mask)
_canvas = Image.new('RGB', size=_inputmask.size, color='black')
_layer = Image.new('RGB', size=_inputmask.size, color='white')
if invert_mask:
mask = 1 - mask
_mask = mask2image(mask).convert('L')
loop_grow = glow_range
inner_mask = _mask
for x in range(shrink_level):
_color = step_color('#FFFFFF', '#000000', shrink_level, x)
glow_mask = expand_mask(image2mask(inner_mask), -loop_grow, blur / (x+0.1)) #扩张,模糊
# 合成
color_image = Image.new("RGB", _layer.size, color=_color)
alpha = tensor2pil(mask_invert(glow_mask)).convert('L')
_glow = chop_image(_layer, color_image, 'subtract', int(step_value(1, 100, shrink_level, x)))
_layer.paste(_glow, mask=alpha)
loop_grow = loop_grow - int(glow_range / shrink_level)
# 合成layer
_edge = tensor2pil(expand_mask(mask, -edge_shrink, 0)).convert('RGB')
_layer = chop_image(_layer, _edge, 'normal', edge_reserve)
_layer.paste(_canvas, mask=ImageChops.invert(_mask))
ret_mask = image2mask(_layer)
return (ret_mask,)
NODE_CLASS_MAPPINGS = {
"LayerMask: MaskEdgeShrink": MaskEdgeShrink
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LayerMask: MaskEdgeShrink": "LayerMask: MaskEdgeShrink"
}
+44
View File
@@ -0,0 +1,44 @@
from .imagefunc import *
class MaskGrow:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(self):
return {
"required": {
"mask": ("MASK", ), #
"invert_mask": ("BOOLEAN", {"default": True}), # 反转mask
"grow": ("INT", {"default": 4, "min": -999, "max": 999, "step": 1}),
"blur": ("INT", {"default": 4, "min": 0, "max": 999, "step": 1}),
},
"optional": {
}
}
RETURN_TYPES = ("MASK",)
RETURN_NAMES = ("mask",)
FUNCTION = 'mask_grow'
CATEGORY = '😺dzNodes/LayerMask'
OUTPUT_NODE = True
def mask_grow(self, mask, invert_mask, grow, blur,):
if invert_mask:
mask = 1 - mask
# _mask = mask2image(mask).convert('L')
ret_mask = expand_mask(mask, grow, blur) # 扩张,模糊
return (ret_mask,)
NODE_CLASS_MAPPINGS = {
"LayerMask: MaskGrow": MaskGrow
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LayerMask: MaskGrow": "LayerMask: MaskGrow"
}
-4
View File
@@ -1,9 +1,5 @@
from .imagefunc import *
def process(image:Image) -> Image:
return image
class XYtoPercent:
def __init__(self):