Commit MaskGradient node
This commit is contained in:
@@ -12,6 +12,7 @@ Nodes are divided into four groups according to their functions: LayerStyle, Lay
|
||||
[中文说明点这里](./README_CN.MD)
|
||||
|
||||
## Update
|
||||
* Commit [MaskGradient](#MaskGradient) node, can create a gradient in the mask.
|
||||
* Commit [GetColorTone](#GetColorTone) node, can obtain the main color or average color of the image.
|
||||
Commit [MaskGrow](#MaskGrow) and [MaskEdgeShrink](#MaskEdgeShrink) nodes.
|
||||
* Commit [MaskBoxDetect](#MaskBoxDetect) node, which can automatically detect the position through the mask and output it to the composite node.
|
||||
@@ -223,6 +224,19 @@ Comparison of MaskGrow and MaskEdgeShrink
|
||||

|
||||
|
||||
|
||||
### <a id="table1">MaskGradient</a>
|
||||
Create a gradient for the mask from one side.
|
||||

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

|
||||
* invert_mask: Whether to reverse the mask.
|
||||
* gradient_side: Generate gradient from which edge. There are four directions: top, bottom, left and right.
|
||||
* gradient_scale: Gradient distance. The default value of 100 indicates that one side of the gradient is completely transparent and the other side is completely opaque. The smaller the value, the shorter the distance from transparent to opaque.
|
||||
* gradient_offset: Gradient position offset.
|
||||
* opacity: The opacity of the gradient.
|
||||
|
||||
|
||||
### <a id="table1">MaskPreview</a>
|
||||
Preview the input mask
|
||||

|
||||
|
||||
@@ -9,6 +9,7 @@ ComfyUI
|
||||
* [LayerFilter](#LayerFilter)节点组提供图像效果滤镜。
|
||||
|
||||
## 更新说明
|
||||
* 添加[MaskGradient](#MaskGradient)节点,可使mask产生渐变。
|
||||
* 添加[GetColorTone](#GetColorTone)节点,可以获取图片的主色或平均色。添加[MaskGrow](#MaskGrow)和[MaskEdgeShrink](#MaskEdgeShrink)节点。
|
||||
* 添加[MaskBoxDetect](#MaskBoxDetect)节点,可以通过mask自动探测位置并输出到合成节点。添加[XY to Percent](#Percent)节点,将绝对坐标转换为percent坐标。添加[GaussianBlur](#GaussianBlur)节点。添加[GetImageSize](#GetImageSize)节点。
|
||||
* 添加[ExtendCanvas](#ExtendCanvas)节点。
|
||||
@@ -214,6 +215,18 @@ ComfyUI
|
||||
MaskGrow与MaskEdgeShrink效果对比
|
||||

|
||||
|
||||
### <a id="table1">MaskGradient</a>
|
||||
使mask从一侧产生渐变。
|
||||

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

|
||||
* invert_mask: 是否反转遮罩。
|
||||
* gradient_side: 从哪个边产生渐变。有四个方向:顶侧top、底侧bottom、左侧left、右侧right。
|
||||
* gradient_scale: 渐变距离。默认值100表示渐变产生一侧完全透明,另一侧完全不透明。数值越小,从透明到不透明的距离越短。
|
||||
* gradient_offset: 渐变位置偏移。
|
||||
* opacity: 渐变的不透明度。
|
||||
|
||||
### <a id="table1">MaskPreview</a>
|
||||
预览mask
|
||||

|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.0 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 165 KiB |
@@ -28,7 +28,7 @@ class GradientImage:
|
||||
|
||||
def gradient_image(self, width, height, angle, start_color, end_color, ):
|
||||
|
||||
ret_image = gradint(start_color, end_color, width, height, angle)
|
||||
ret_image = gradient(start_color, end_color, width, height, angle)
|
||||
|
||||
return (pil2tensor(ret_image), )
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ class GradientOverlay:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
log('Warning: mask mismatch, droped!')
|
||||
|
||||
_gradient = gradint(start_color, end_color, _layer.width, _layer.height, float(angle))
|
||||
_gradient = gradient(start_color, end_color, _layer.width, _layer.height, float(angle))
|
||||
|
||||
# 合成layer
|
||||
_comp = chop_image(_layer, _gradient, blend_mode, opacity)
|
||||
|
||||
+1
-1
@@ -188,7 +188,7 @@ def create_gradient(start_color_inhex:str, end_color_inhex:str, width:int, heigh
|
||||
draw.line((0, i, width, i), fill=color)
|
||||
return ret_image
|
||||
|
||||
def gradint(start_color_inhex:str, end_color_inhex:str, width:int, height:int, angle:float, ) -> Image:
|
||||
def gradient(start_color_inhex:str, end_color_inhex:str, width:int, height:int, angle:float, ) -> Image:
|
||||
radius = int((width + height) / 4)
|
||||
g = create_gradient(start_color_inhex, end_color_inhex, radius, radius)
|
||||
_canvas = Image.new('RGB', size=(radius, radius*3), color=start_color_inhex)
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
import copy
|
||||
|
||||
from .imagefunc import *
|
||||
|
||||
class MaskGradient:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
side = ['top', 'bottom', 'left', 'right']
|
||||
return {
|
||||
"required": {
|
||||
"mask": ("MASK",),
|
||||
"invert_mask": ("BOOLEAN", {"default": True}), # 反转mask
|
||||
"gradient_side": (side,),
|
||||
"gradient_scale": ("INT", {"default": 100, "min": 1, "max": 9999, "step": 1}),
|
||||
"gradient_offset": ("INT", {"default": 0, "min": -9999, "max": 9999, "step": 1}),
|
||||
"opacity": ("INT", {"default": 100, "min": 0, "max": 100, "step": 1}),
|
||||
},
|
||||
"optional": {
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("MASK",)
|
||||
RETURN_NAMES = ("mask",)
|
||||
FUNCTION = 'mask_gradient'
|
||||
CATEGORY = '😺dzNodes/LayerMask'
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def mask_gradient(self, mask, invert_mask, gradient_side, gradient_scale, gradient_offset, opacity, ):
|
||||
|
||||
if invert_mask:
|
||||
mask = 1 - mask
|
||||
_mask = mask2image(mask).convert('L')
|
||||
_canvas = copy.copy(_mask)
|
||||
width = _mask.width
|
||||
height = _mask.height
|
||||
_gradient = gradient('#000000', '#FFFFFF',
|
||||
_mask.width, _mask.height, 0)
|
||||
(box_x, box_y, box_width, box_height) = min_bounding_rect(_mask)
|
||||
# preview_image = mask2image(mask).convert('RGB')
|
||||
# preview_image = draw_rect(preview_image, box_x, box_y, box_width, box_height,
|
||||
# line_color = "#F00000", line_width = int(preview_image.height / 60))
|
||||
if gradient_side == 'top':
|
||||
boxsize = (width, box_height)
|
||||
_gradient = _gradient.transpose(Image.FLIP_TOP_BOTTOM)
|
||||
_gradient = _gradient.resize(boxsize)
|
||||
_black = Image.new('RGB', size = boxsize, color = 'black')
|
||||
if gradient_scale != 100:
|
||||
_box = Image.new('RGB', size = boxsize, color = 'black')
|
||||
_gradient = _gradient.resize((width, int(box_height * gradient_scale / 100)))
|
||||
_box.paste(_gradient, box = (0, 0))
|
||||
_gradient = _box
|
||||
if gradient_offset != 0:
|
||||
_box = Image.new('RGB', size = boxsize, color = 'black')
|
||||
_boxwhite = Image.new('RGB', size = boxsize, color = 'white')
|
||||
_box.paste(_gradient, box=(0, gradient_offset))
|
||||
_box.paste(_boxwhite, box = (0, gradient_offset - _box.height))
|
||||
_gradient = _box
|
||||
if gradient_offset > box_height:
|
||||
_gradient = Image.new('RGB', size = boxsize, color = 'white')
|
||||
_canvas.paste(_black, box = (0, box_y), mask = _gradient.convert('L'))
|
||||
elif gradient_side == 'bottom':
|
||||
boxsize = (width, box_height)
|
||||
_gradient = _gradient.resize((width, box_height))
|
||||
_black = Image.new('RGB', size = boxsize, color = 'black')
|
||||
if gradient_scale != 100:
|
||||
_box = Image.new('RGB', size = boxsize, color = 'black')
|
||||
_gradient = _gradient.resize((width, int(box_height * gradient_scale / 100)))
|
||||
_box.paste(_gradient, box = (0, box_height - _gradient.height))
|
||||
_gradient = _box
|
||||
if gradient_offset != 0:
|
||||
_box = Image.new('RGB', size = boxsize, color = 'black')
|
||||
_boxwhite = Image.new('RGB', size = boxsize, color = 'white')
|
||||
_box.paste(_gradient, box=(0, gradient_offset))
|
||||
_box.paste(_boxwhite, box = (0, gradient_offset + _box.height))
|
||||
_gradient = _box
|
||||
if gradient_offset < -box_height:
|
||||
_gradient = Image.new('RGB', size=boxsize, color='white')
|
||||
_canvas.paste(_black, box = (0, box_y), mask = _gradient.convert('L'))
|
||||
elif gradient_side == 'left':
|
||||
boxsize = (box_width, height)
|
||||
_gradient = _gradient.transpose(Image.ROTATE_270)
|
||||
_gradient = _gradient.resize(boxsize)
|
||||
_black = Image.new('RGB', size = boxsize, color = 'black')
|
||||
if gradient_scale != 100:
|
||||
_box = Image.new('RGB', size = boxsize, color = 'black')
|
||||
_gradient = _gradient.resize((int(box_width * gradient_scale / 100), height))
|
||||
_box.paste(_gradient, box = (0, 0))
|
||||
_gradient = _box
|
||||
if gradient_offset != 0:
|
||||
_box = Image.new('RGB', size = boxsize, color = 'black')
|
||||
_boxwhite = Image.new('RGB', size = boxsize, color = 'white')
|
||||
_box.paste(_gradient, box=(gradient_offset, 0))
|
||||
_box.paste(_boxwhite, box = (gradient_offset - _box.width, 0))
|
||||
_gradient = _box
|
||||
if gradient_offset > box_width:
|
||||
_gradient = Image.new('RGB', size=boxsize, color='white')
|
||||
_canvas.paste(_black, box = (box_x, 0), mask = _gradient.convert('L'))
|
||||
elif gradient_side == 'right':
|
||||
boxsize = (box_width, height)
|
||||
_gradient = _gradient.transpose(Image.ROTATE_90)
|
||||
_gradient = _gradient.resize(boxsize)
|
||||
_black = Image.new('RGB', size = boxsize, color = 'black')
|
||||
if gradient_scale != 100:
|
||||
_box = Image.new('RGB', size = boxsize, color = 'black')
|
||||
_gradient = _gradient.resize((int(box_width * gradient_scale / 100), height))
|
||||
_box.paste(_gradient, box = (box_width - _gradient.width, 0))
|
||||
_gradient = _box
|
||||
if gradient_offset != 0:
|
||||
_box = Image.new('RGB', size = boxsize, color = 'black')
|
||||
_boxwhite = Image.new('RGB', size = boxsize, color = 'white')
|
||||
_box.paste(_gradient, box=(gradient_offset, 0))
|
||||
_box.paste(_boxwhite, box = (gradient_offset + _box.width, 0))
|
||||
_gradient = _box
|
||||
if gradient_offset < -box_width:
|
||||
_gradient = Image.new('RGB', size=boxsize, color='white')
|
||||
_canvas.paste(_black, box = (box_x, 0), mask = _gradient.convert('L'))
|
||||
# opacity
|
||||
if opacity < 100:
|
||||
_canvas = chop_image(_mask, _canvas, 'normal', opacity)
|
||||
ret_mask = image2mask(_canvas)
|
||||
return (ret_mask,)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerMask: MaskGradient": MaskGradient
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LayerMask: MaskGradient": "LayerMask: MaskGradient"
|
||||
}
|
||||
@@ -0,0 +1,442 @@
|
||||
{
|
||||
"last_node_id": 20,
|
||||
"last_link_id": 37,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 12,
|
||||
"type": "LayerUtility: ImageBlendAdvance",
|
||||
"pos": [
|
||||
1709,
|
||||
-215
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 338
|
||||
},
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "background_image",
|
||||
"type": "IMAGE",
|
||||
"link": 35
|
||||
},
|
||||
{
|
||||
"name": "layer_image",
|
||||
"type": "IMAGE",
|
||||
"link": 17
|
||||
},
|
||||
{
|
||||
"name": "layer_mask",
|
||||
"type": "MASK",
|
||||
"link": 33
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
15
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LayerUtility: ImageBlendAdvance"
|
||||
},
|
||||
"widgets_values": [
|
||||
false,
|
||||
"normal",
|
||||
78,
|
||||
50,
|
||||
100,
|
||||
"vertical",
|
||||
0.8,
|
||||
1,
|
||||
0,
|
||||
"lanczos",
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"type": "ImageScale",
|
||||
"pos": [
|
||||
1306,
|
||||
-209
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 130
|
||||
},
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 34
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
35
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "ImageScale"
|
||||
},
|
||||
"widgets_values": [
|
||||
"nearest-exact",
|
||||
1920,
|
||||
1080,
|
||||
"disabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"type": "LayerMask: MaskGradient",
|
||||
"pos": [
|
||||
1318,
|
||||
189
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 154
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 36
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"links": [
|
||||
32,
|
||||
33
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LayerMask: MaskGradient"
|
||||
},
|
||||
"widgets_values": [
|
||||
true,
|
||||
"top",
|
||||
74,
|
||||
397,
|
||||
100
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
936,
|
||||
-209
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 314
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
34
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": [],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"beach (2).jpg",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"type": "LayerMask: MaskPreview",
|
||||
"pos": [
|
||||
1709,
|
||||
177
|
||||
],
|
||||
"size": [
|
||||
313.1912463378908,
|
||||
338.8637414550783
|
||||
],
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 32
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LayerMask: MaskPreview"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"type": "LayerUtility: ImageBlendAdvance",
|
||||
"pos": [
|
||||
2077,
|
||||
-213
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 338
|
||||
},
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "background_image",
|
||||
"type": "IMAGE",
|
||||
"link": 15
|
||||
},
|
||||
{
|
||||
"name": "layer_image",
|
||||
"type": "IMAGE",
|
||||
"link": 10
|
||||
},
|
||||
{
|
||||
"name": "layer_mask",
|
||||
"type": "MASK",
|
||||
"link": 37
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
18
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LayerUtility: ImageBlendAdvance"
|
||||
},
|
||||
"widgets_values": [
|
||||
true,
|
||||
"normal",
|
||||
100,
|
||||
50,
|
||||
40,
|
||||
"None",
|
||||
0.8,
|
||||
1,
|
||||
0,
|
||||
"lanczos",
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
2452,
|
||||
-213
|
||||
],
|
||||
"size": [
|
||||
743.0909667968749,
|
||||
703.6272277832031
|
||||
],
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 18
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
931,
|
||||
178
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 314
|
||||
},
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
10,
|
||||
17
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": [
|
||||
36,
|
||||
37
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"erji_alpha (8).png",
|
||||
"image"
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
10,
|
||||
2,
|
||||
0,
|
||||
10,
|
||||
1,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
15,
|
||||
12,
|
||||
0,
|
||||
10,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
17,
|
||||
2,
|
||||
0,
|
||||
12,
|
||||
1,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
18,
|
||||
10,
|
||||
0,
|
||||
13,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
32,
|
||||
18,
|
||||
0,
|
||||
16,
|
||||
0,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
33,
|
||||
18,
|
||||
0,
|
||||
12,
|
||||
2,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
34,
|
||||
9,
|
||||
0,
|
||||
19,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
35,
|
||||
19,
|
||||
0,
|
||||
12,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
36,
|
||||
2,
|
||||
1,
|
||||
18,
|
||||
0,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
37,
|
||||
2,
|
||||
1,
|
||||
10,
|
||||
2,
|
||||
"MASK"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {},
|
||||
"version": 0.4
|
||||
}
|
||||
Reference in New Issue
Block a user