diff --git a/README.MD b/README.MD
index 87c65ae..0d0c20a 100644
--- a/README.MD
+++ b/README.MD
@@ -70,7 +70,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.
-
+* [CreateGradientMask](#CreateGradientMask) node add ```center``` option.
* Commit [GetColorToneV2](#GetColorToneV2) node, can select the main and average colors for the background or body.
* Commit [ImageRewardFilter](#ImageRewardFilter) node, can filter out poor quality pictures.
* Ultra nodes add ```VITMatte(local)``` method, You can choose this method to avoid accessing huggingface.co if you have already downloaded the model before.
@@ -1190,15 +1190,16 @@ Node options:
### CreateGradientMask
Create a gradient mask. please note the difference between this node and the MaskGradient node.

+
Node options:

* size_as*: The input image or mask here will generate the output image and mask according to their size. this input takes priority over the width and height below.
* width: Width of the image. If there is a size_as input, this setting will be ignored.
* height: Height of the image. If there is a size_as input, this setting will be ignored.
-* gradient_side: Generate gradient from which edge. There are four directions: top, bottom, left and right.
+* gradient_side: Generate gradient from which edge. There are five directions: top, bottom, left, right and center.
* 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.
+* gradient_offset: Gradient position offset. When ```gradient_side``` is center, the size of the gradient area is adjusted here, positive values are smaller, and negative values are enlarged.
* opacity: The opacity of the gradient.
*Only limited to input image and mask. forcing the integration of other types of inputs will result in node errors.
diff --git a/README_CN.MD b/README_CN.MD
index af4f8c2..d146f53 100644
--- a/README_CN.MD
+++ b/README_CN.MD
@@ -70,6 +70,7 @@ git clone https://github.com/chflame163/ComfyUI_LayerStyle.git
## 更新说明
**如果本插件更新后出现依赖包错误,请重新安装相关依赖包。
+* [CreateGradientMask](#CreateGradientMask) 节点增加 ```center``` 选项。
* 添加 [GetColorToneV2](#GetColorToneV2) 节点, 可选择背景或主体的主色和平均色。
* 添加[ImageRewardFilter](#ImageRewardFilter) 节点, 可过滤掉品质欠佳的图片。
* Ultra 节点增加VITMatte(local)方法,如果之前已经下载了模型,可选择此方法避免访问huggingface.co。
@@ -1187,15 +1188,16 @@ MaskGrow与MaskEdgeShrink效果对比
### CreateGradientMask
创建一个渐变的遮罩。请注意此节点与MaskGradient的区别。

+
节点选项说明:

* size_as*: 此处输入图像或遮罩,将按照其尺寸生成输出图像和遮罩。注意,此输入优先级高于下面的width和height。
* width: 画面的宽度。如果有size_as输入,此设置将被忽略。
* height: 画面的高度。如果有size_as输入,此设置将被忽略。
-* gradient_side: 从哪个边产生渐变。有四个方向:顶侧top、底侧bottom、左侧left、右侧right。
+* gradient_side: 从哪个边产生渐变。有5个方向:顶侧top、底侧bottom、左侧left、右侧right和中央center。
* gradient_scale: 渐变距离。默认值100表示渐变产生一侧完全透明,另一侧完全不透明。数值越小,从透明到不透明的距离越短。
-* gradient_offset: 渐变位置偏移。
+* gradient_offset: 渐变位置偏移。```gradient_side```为center时这里调整渐变区域的大小,正值是变小,负值是扩大。
* opacity: 渐变的不透明度。
*仅限输入image和mask, 如果强制接入其他类型输入,将导致节点错误。
diff --git a/image/create_gradient_mask_example2.png b/image/create_gradient_mask_example2.png
new file mode 100644
index 0000000..a1652dd
Binary files /dev/null and b/image/create_gradient_mask_example2.png differ
diff --git a/py/create_gradient_mask.py b/py/create_gradient_mask.py
index 52d0598..c61a53e 100644
--- a/py/create_gradient_mask.py
+++ b/py/create_gradient_mask.py
@@ -10,7 +10,7 @@ class CreateGradientMask:
@classmethod
def INPUT_TYPES(self):
- side = ['bottom', 'top', 'left', 'right']
+ side = ['bottom', 'top', 'left', 'right', 'center']
return {
"required": {
"width": ("INT", {"default": 512, "min": 4, "max": 99999, "step": 1}),
@@ -25,8 +25,8 @@ class CreateGradientMask:
}
}
- RETURN_TYPES = ("MASK", )
- RETURN_NAMES = ("mask", )
+ RETURN_TYPES = ("MASK",)
+ RETURN_NAMES = ("mask",)
FUNCTION = 'create_gradient_mask'
CATEGORY = '😺dzNodes/LayerMask'
@@ -42,6 +42,8 @@ class CreateGradientMask:
_black = Image.new('L', size=(width, height), color='black')
_white = Image.new('L', size=(width, height), color='white')
_canvas = copy.deepcopy(_black)
+ debug_image1 = copy.deepcopy(_black).convert('RGB')
+ debug_image2 = copy.deepcopy(_black).convert('RGB')
start_color = '#FFFFFF'
end_color = '#000000'
if gradient_side == 'bottom':
@@ -80,11 +82,34 @@ class CreateGradientMask:
_canvas = _white
elif gradient_offset > 0:
_canvas.paste(_white, box=(gradient_offset - width, 0))
-
+ else:
+ _gradient = create_box_gradient(start_color_inhex='#000000', end_color_inhex='#FFFFFF',
+ width=width, height=height, scale=int(gradient_scale))
+ _gradient = _gradient.convert('L')
+ debug_image1 = _gradient
+ _blur_mask = Image.new('L', size=(width*2, height*2), color='black')
+ _blur_mask.paste(_gradient, box=(int(width/2), int(height/2)))
+ _blur_mask = gaussian_blur(_blur_mask, int((width + height) * gradient_scale / 100 / 16))
+ _gamma_mask = gamma_trans(_blur_mask, 0.15)
+ (crop_x, crop_y, crop_width, crop_height) = mask_area(_gamma_mask)
+ crop_box = (crop_x, crop_y, crop_x + crop_width, crop_y + crop_height)
+ _blur_mask = _blur_mask.crop(crop_box)
+ _blur_mask = _blur_mask.resize((width, height), Image.BILINEAR)
+ if gradient_offset != 0:
+ resize_width = int(width - gradient_offset)
+ resize_height = int(height - gradient_offset)
+ if resize_width < 1:
+ resize_width = 1
+ if resize_height < 1:
+ resize_height = 1
+ _blur_mask = _blur_mask.resize((resize_width, resize_height), Image.BILINEAR)
+ paste_box = (int((width - resize_width) / 2), int((height - resize_height) / 2))
+ else:
+ paste_box = (0,0)
+ _canvas.paste(_blur_mask, box=paste_box)
# opacity
if opacity < 100:
_canvas = chop_image(_black, _canvas, 'normal', opacity)
-
log(f"{NODE_NAME} Processed.", message_type='finish')
return (image2mask(_canvas),)
diff --git a/py/imagefunc.py b/py/imagefunc.py
index 7f503f6..ad26949 100644
--- a/py/imagefunc.py
+++ b/py/imagefunc.py
@@ -591,6 +591,26 @@ def image_rotate_extend_with_alpha(image:Image, angle:float, alpha:Image=None, m
ret_image = _image
return (_image, _alpha, ret_image)
+def create_box_gradient(start_color_inhex:str, end_color_inhex:str, width:int, height:int, scale:int=50) -> Image:
+ # scale is percent of border to center for the rectangle
+ if scale > 100:
+ scale = 100
+ elif scale < 1:
+ scale = 1
+ start_color = Hex_to_RGB(start_color_inhex)
+ end_color = Hex_to_RGB(end_color_inhex)
+ ret_image = Image.new("RGB", (width, height), start_color)
+ draw = ImageDraw.Draw(ret_image)
+ step = int(max(width, height) * scale / 100 / 2)
+ if step > 0:
+ for i in range(step):
+ R = int(start_color[0] * (step - i) / step + end_color[0] * i / step)
+ G = int(start_color[1] * (step - i) / step + end_color[1] * i / step)
+ B = int(start_color[2] * (step - i) / step + end_color[2] * i / step)
+ color = (R, G, B)
+ draw.rectangle((i, i, width - i, height - i), fill=color)
+ draw.rectangle((step, step, width - step, height - step), fill=end_color)
+ return ret_image
def create_gradient(start_color_inhex:str, end_color_inhex:str, width:int, height:int, direction:str='bottom') -> Image:
# direction = one of top, bottom, left, right
@@ -1172,10 +1192,10 @@ def mask_area(image:Image) -> tuple:
gray = cv2.cvtColor(cv2_image, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 127, 255, 0)
locs = np.where(thresh == 255)
- x1 = np.min(locs[1])
- x2 = np.max(locs[1])
- y1 = np.min(locs[0])
- y2 = np.max(locs[0])
+ x1 = np.min(locs[1]) if len(locs[1]) > 0 else 0
+ x2 = np.max(locs[1]) if len(locs[1]) > 0 else image.width
+ y1 = np.min(locs[0]) if len(locs[0]) > 0 else 0
+ y2 = np.max(locs[0]) if len(locs[0]) > 0 else image.height
x1, y1, x2, y2 = min(x1, x2), min(y1, y2), max(x1, x2), max(y1, y2)
return (x1, y1, x2 - x1, y2 - y1)
@@ -1256,10 +1276,13 @@ def RGB_to_Hex(RGB) -> str:
return color
def Hex_to_RGB(inhex) -> tuple:
- rval = inhex[1:3]
- gval = inhex[3:5]
- bval = inhex[5:]
- rgb = (int(rval, 16), int(gval, 16), int(bval, 16))
+ if not inhex.startswith('#'):
+ raise ValueError(f'Invalid Hex Code in {inhex}')
+ else:
+ rval = inhex[1:3]
+ gval = inhex[3:5]
+ bval = inhex[5:]
+ rgb = (int(rval, 16), int(gval, 16), int(bval, 16))
return tuple(rgb)
def RGB_to_HSV(RGB:tuple) -> list: