Commit MaskGradient node

This commit is contained in:
chflame163
2024-01-28 16:31:26 +08:00
committed by GitHub
parent b1764f8f5c
commit 0c16b99cad
9 changed files with 605 additions and 3 deletions
+14
View File
@@ -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
![image](image/mask_edge_compare.png)
### <a id="table1">MaskGradient</a>
Create a gradient for the mask from one side.
![image](image/mask_gradient_example.png)
Node options:
![image](image/mask_gradient_node.png)
* 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
![image](image/mask_invert.png)
+13
View File
@@ -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效果对比
![image](image/mask_edge_compare.png)
### <a id="table1">MaskGradient</a>
使mask从一侧产生渐变。
![image](image/mask_gradient_example.png)
节点选项说明:
![image](image/mask_gradient_node.png)
* invert_mask: 是否反转遮罩。
* gradient_side: 从哪个边产生渐变。有四个方向:顶侧top、底侧bottom、左侧left、右侧right。
* gradient_scale: 渐变距离。默认值100表示渐变产生一侧完全透明,另一侧完全不透明。数值越小,从透明到不透明的距离越短。
* gradient_offset: 渐变位置偏移。
* opacity: 渐变的不透明度。
### <a id="table1">MaskPreview</a>
预览mask
![image](image/mask_invert.png)
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

+1 -1
View File
@@ -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), )
+1 -1
View File
@@ -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
View File
@@ -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)
+133
View File
@@ -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"
}
+442
View File
@@ -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
}