Commit MaskMotionBlur node and SoftLight node.

The MaskMotionBlur node create motion blur on the mask.  The SoftLight node create the bright highlights on the screen appear blurry.
This commit is contained in:
chflame163
2024-01-29 23:31:14 +08:00
committed by GitHub
parent 6a86a7ec31
commit 750724ac0d
14 changed files with 172 additions and 4 deletions
+24
View File
@@ -12,6 +12,8 @@ Nodes are divided into four groups according to their functions: LayerStyle, Lay
[中文说明点这里](./README_CN.MD)
## Update
* Commit [MaskMotionBlur](#MaskMotionBlur) node.
* Commit [SoftLight](#SoftLight) node.
* Commit [ChannelShake](#ChannelShake) node, that is filter, can produce channel dislocation effect similar like Tiktok logo.
* 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.
@@ -205,6 +207,7 @@ Output:
### <a id="table1">MaskGrow</a>
Grow and shrink edges and blur the mask
![image](image/mask_grow_example.png)
Node options:
![image](image/mask_grow_node.png)
@@ -214,6 +217,7 @@ Node options:
### <a id="table1">MaskEdgeShrink</a>
Smooth transition and shrink the mask edges while preserving edge details.
![image](image/mask_edge_shrink_example.png)
Node options:
![image](image/mask_edge_shrink_node.png)
@@ -226,6 +230,15 @@ Node options:
Comparison of MaskGrow and MaskEdgeShrink
![image](image/mask_edge_compare.png)
### <a id="table1">MaskMotionBlur</a>
Create motion blur on the mask.
![image](image/mask_motion_blur_example.png)
Node options:
![image](image/mask_motion_blur_node.png)
* invert_mask: Whether to reverse the mask.
* blur: The size of blur.
* angle: The angle of blur.
### <a id="table1">MaskGradient</a>
Create a gradient for the mask from one side.
@@ -387,6 +400,17 @@ This node allows any type of input.
![image](image/layerfilter_nodes.png)
### <a id="table1">SoftLight</a>
Soft light effect, the bright highlights on the screen appear blurry.
![image](image/soft_light_example.png)
Node options:
![image](image/soft_light_node.png)
* soft: Size of soft light.
* threshold: Soft light range. the light appears from the brightest part of the picture. in lower value, the range will be larger, and in higher value, the range will be smaller.
* opacity: Opacity of the soft light.
### <a id="table1">ChannelShake</a>
Channel misalignment. similar to the effect of Tiktok logo.
![image](image/channel_shake_example.png)
+24 -3
View File
@@ -9,6 +9,8 @@ ComfyUI
* [LayerFilter](#LayerFilter)节点组提供图像效果滤镜。
## 更新说明
* 添加[MaskMotionBlur](#MaskMotionBlur)节点。
* 添加[SoftLight](#SoftLight)节点。
* 添加[ChannelShake](#ChannelShake)节点,这是一个滤镜,能产生类似抖音logo的通道错位效果。
* 添加[MaskGradient](#MaskGradient)节点,可使mask产生渐变。
* 添加[GetColorTone](#GetColorTone)节点,可以获取图片的主色或平均色。添加[MaskGrow](#MaskGrow)和[MaskEdgeShrink](#MaskEdgeShrink)节点。
@@ -198,6 +200,7 @@ ComfyUI
### <a id="table1">MaskGrow</a>
对mask进行扩张收缩边缘和模糊处理
![image](image/mask_grow_example.png)
节点选项说明:
![image](image/mask_grow_node.png)
@@ -207,6 +210,7 @@ ComfyUI
### <a id="table1">MaskEdgeShrink</a>
使mask边缘平滑地过渡收缩,并保留边缘细节。
![image](image/mask_edge_shrink_example.png)
节点选项说明:
![image](image/mask_edge_shrink_node.png)
@@ -219,6 +223,16 @@ ComfyUI
MaskGrow与MaskEdgeShrink效果对比
![image](image/mask_edge_compare.png)
### <a id="table1">MaskMotionBlur</a>
使mask产生运动模糊。
![image](image/mask_motion_blur_example.png)
节点选项说明:
![image](image/mask_motion_blur_node.png)
* invert_mask: 是否反转遮罩。
* blur: 模糊大小。
* angle: 模糊角度。
### <a id="table1">MaskGradient</a>
使mask从一侧产生渐变。
![image](image/mask_gradient_example.png)
@@ -377,6 +391,16 @@ mask
![image](image/layerfilter_nodes.png)
### <a id="table1">SoftLight</a>
柔光效果。
![image](image/soft_light_example.png)
节点选项说明:
![image](image/soft_light_node.png)
* soft: 柔光大小。
* threshold: 柔光范围。柔光从画面最明亮的部分呈现。数值越低范围越大,越高范围越小。
* opacity: 柔光的不透明度。
### <a id="table1">ChannelShake</a>
通道错位。类似抖音logo的效果。
![image](image/channel_shake_example.png)
@@ -421,9 +445,6 @@ mask
LayerMask:
* text mask
* mask subtract
* mask combine
* mask diffrent
LayerFilter:
* emboss
Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 303 KiB

After

Width:  |  Height:  |  Size: 313 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

+1 -1
View File
@@ -14,7 +14,7 @@ class ChannelShake:
"required": {
"image": ("IMAGE", ), #
"distance": ("INT", {"default": 20, "min": 1, "max": 999, "step": 1}), # 距离
"angle": ("FLOAT", {"default": 40, "min": -360, "max": 360, "step": 0.01}), # 角度
"angle": ("FLOAT", {"default": 40, "min": -360, "max": 360, "step": 0.1}), # 角度
"mode": (channel_mode,), # 模式
},
"optional": {
+24
View File
@@ -250,6 +250,19 @@ def get_image_color_average(image:Image) -> str:
ret_color = RGB_to_Hex(color)
return ret_color
def get_image_bright_average(image:Image) -> int:
image = image.convert('L')
width, height = image.size
total_bright = 0
pixels = 0
for y in range(height):
for x in range(width):
b = image.getpixel((x, y))
if b > 1: # 排除死黑
pixels += 1
total_bright += b
return int(total_bright / pixels)
'''Mask Functions'''
def expand_mask(mask:torch.Tensor, grow:int, blur:int) -> torch.Tensor:
@@ -345,6 +358,17 @@ def max_inscribed_rect(image:Image) -> tuple:
x1, y1, x2, y2 = min(x1, x2), min(y1, y2), max(x1, x2), max(y1, y2)
return (x1, y1, x2 - x1, y2 - y1)
def gray_threshold(image:Image, thresh:int=127, otsu:bool=False) -> Image:
cv2_image = pil2cv2(image)
gray = cv2.cvtColor(cv2_image, cv2.COLOR_BGR2GRAY)
if otsu:
_, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
else:
_, thresh = cv2.threshold(gray, thresh, 255, cv2.THRESH_TOZERO)
return cv22pil(thresh).convert('L')
'''Color Functions'''
def RGB_to_Hex(RGB) -> str:
+45
View File
@@ -0,0 +1,45 @@
import copy
from .imagefunc import *
class MaskMotionBlur:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(self):
return {
"required": {
"mask": ("MASK",),
"invert_mask": ("BOOLEAN", {"default": True}), # 反转mask
"blur": ("INT", {"default": 20, "min": 1, "max": 9999, "step": 1}),
"angle": ("FLOAT", {"default": 0, "min": -360, "max": 360, "step": 0.1}),
},
"optional": {
}
}
RETURN_TYPES = ("MASK",)
RETURN_NAMES = ("mask",)
FUNCTION = 'mask_motion_blur'
CATEGORY = '😺dzNodes/LayerMask'
OUTPUT_NODE = True
def mask_motion_blur(self, mask, invert_mask, blur, angle,):
if invert_mask:
mask = 1 - mask
_mask = mask2image(mask).convert('RGB')
_blurimage = motion_blur(_mask, angle, blur)
ret_mask = image2mask(_blurimage)
return (ret_mask,)
NODE_CLASS_MAPPINGS = {
"LayerMask: MaskMotionBlur": MaskMotionBlur
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LayerMask: MaskMotionBlur": "LayerMask: MaskMotionBlur"
}
+54
View File
@@ -0,0 +1,54 @@
from .imagefunc import *
class SoftLight:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(self):
return {
"required": {
"image": ("IMAGE", ), #
"soft": ("FLOAT", {"default": 1, "min": 0.2, "max": 10, "step": 0.01}), # 模糊
"threshold": ("INT", {"default": -10, "min": -255, "max": 255, "step": 1}), # 高光阈值
"opacity": ("INT", {"default": 100, "min": 0, "max": 100, "step": 1}), # 透明度
},
"optional": {
}
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("image",)
FUNCTION = 'soft_light'
CATEGORY = '😺dzNodes/LayerFilter'
OUTPUT_NODE = True
def soft_light(self, image, soft, threshold, opacity,):
blend_mode = 'screen'
_canvas = tensor2pil(image).convert('RGB')
blur = int((_canvas.width + _canvas.height) / 200 * soft)
_otsumask = gray_threshold(_canvas, otsu=True)
_removebkgd = remove_background(_canvas, _otsumask, '#000000').convert('L')
auto_threshold = get_image_bright_average(_removebkgd)
light_mask = gray_threshold(_canvas, auto_threshold + threshold)
highlight_mask = gray_threshold(_canvas, auto_threshold + (255 - auto_threshold) // 2 + threshold // 2)
blurimage = gaussian_blur(_canvas, soft).convert('RGB')
light = chop_image(_canvas, blurimage, blend_mode=blend_mode, opacity=opacity)
highlight = chop_image(light, blurimage, blend_mode=blend_mode, opacity=opacity)
_canvas.paste(highlight, mask=gaussian_blur(light_mask, blur * 2).convert('L'))
_canvas.paste(highlight, mask=gaussian_blur(highlight_mask, blur).convert('L'))
ret_image = _canvas
log('SoftLight Processed.')
return (pil2tensor(ret_image),)
NODE_CLASS_MAPPINGS = {
"LayerFilter: SoftLight": SoftLight
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LayerFilter: SoftLight": "LayerFilter: SoftLight"
}