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:
@@ -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>
|
||||

|
||||
|
||||
@@ -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:
|
||||

|
||||
* 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:
|
||||

|
||||
* 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
|
||||

|
||||
|
||||
|
||||
### <a id="table1">MaskPreview</a>
|
||||
Preview the input mask
|
||||

|
||||
@@ -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.
|
||||

|
||||
|
||||
Node options:
|
||||

|
||||
* 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>
|
||||

|
||||
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
@@ -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进行扩张收缩边缘和模糊处理
|
||||
|
||||
节点选项说明:
|
||||

|
||||
* invert_mask: 是否反转遮罩。
|
||||
* grow: 扩张幅度。正值是向外扩张,负值是向内收缩。
|
||||
* blur: 模糊。
|
||||
|
||||
### <a id="table1">MaskEdgeShrink</a>
|
||||
使mask边缘平滑地过渡收缩,并保留边缘细节。
|
||||
|
||||
节点选项说明:
|
||||

|
||||
* invert_mask: 是否反转遮罩。
|
||||
* shrink_level: 收缩平滑级别。
|
||||
* soft: 平滑幅度。
|
||||
* edge_shrink: 边缘收缩幅度。
|
||||
* edge_reserve: 保留边缘细节幅度, 100为完全保留,0为完全不保留。
|
||||
|
||||
MaskGrow与MaskEdgeShrink效果对比
|
||||

|
||||
|
||||
### <a id="table1">MaskPreview</a>
|
||||
预览mask
|
||||
@@ -267,6 +290,17 @@ mask
|
||||
输出:
|
||||
* string
|
||||
|
||||
### <a id="table1">GetColorTone</a>
|
||||
从图片中获取主颜色或平均色。
|
||||

|
||||
|
||||
节点选项说明:
|
||||

|
||||
* mode: 模式,有两种可选择,主颜色main_color和平均色average。
|
||||
|
||||
输出:
|
||||
* string
|
||||
|
||||
### <a id="table1">ExtendCanvas</a>
|
||||
扩展画布。
|
||||

|
||||
@@ -280,7 +314,7 @@ mask
|
||||
* right: 右侧扩展值。
|
||||
* color; 画布颜色
|
||||
|
||||
### X,Y to <a id="table1">Percent</a>
|
||||
### XY to <a id="table1">Percent</a>
|
||||

|
||||
将绝对坐标转换为百分比坐标。
|
||||
|
||||
@@ -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 |
@@ -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
@@ -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"},),
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -1,9 +1,5 @@
|
||||
from .imagefunc import *
|
||||
|
||||
def process(image:Image) -> Image:
|
||||
|
||||
return image
|
||||
|
||||
class GetImageSize:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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'''
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -1,9 +1,5 @@
|
||||
from .imagefunc import *
|
||||
|
||||
def process(image:Image) -> Image:
|
||||
|
||||
return image
|
||||
|
||||
class XYtoPercent:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
Reference in New Issue
Block a user