diff --git a/README.MD b/README.MD index 15452ae..3edc113 100644 --- a/README.MD +++ b/README.MD @@ -7,6 +7,8 @@ Currently completed Drop Shadow and OuterGlow nodes, and follow-up work is in pr [中文说明点这里](./README_CN.MD) ## Update +* Now, layer_mask is not a mandatory input. without inputting will use the layer_image's alpha channel. It is allowed to use layers and masks with different shapes, but the size must be consistent. +![image](image/title.png) * Commit ImageBlend node. * Remove the mask channel output from the DropShadow node; Commit OuterGlow node. * Commit DropShadow node. diff --git a/README_CN.MD b/README_CN.MD index 9bf7f1c..6501ede 100644 --- a/README_CN.MD +++ b/README_CN.MD @@ -3,6 +3,8 @@ ComfyUI µ±Ç°ÒÑÍê³ÉDrop ShadowºÍOuterGlow½Úµã£¬ºóÐø¹¤×÷½øÐÐÖС£ ## ¸üÐÂ˵Ã÷£º +* ÏÖÔÚlayer_mask²»ÊÇÊäÈë±ØÑ¡¡£²»ÊäÈ뽫ʹÓÃlayer_imageµÄalphaͨµÀ¡£ÔÊÐíʹÓÃÐÎ×´²»Í¬µÄlayerºÍmask£¬µ«ÊÇ³ß´ç±ØÐëÒ»Ö¡£ +![image](image/title.png) * Ìí¼ÓImageBlend½Úµã * DropShadow½ÚµãÈ¥µôµ÷ÊÔmaskͨµÀÊä³ö£»Ìí¼ÓOuterGlow½Úµã¡£ * DropShadow½ÚµãÉÏÏß¡£ diff --git a/py/__pycache__/drop_shadow.cpython-311.pyc b/py/__pycache__/drop_shadow.cpython-311.pyc index f8f4174..ce405c7 100644 Binary files a/py/__pycache__/drop_shadow.cpython-311.pyc and b/py/__pycache__/drop_shadow.cpython-311.pyc differ diff --git a/py/__pycache__/image_blend.cpython-311.pyc b/py/__pycache__/image_blend.cpython-311.pyc new file mode 100644 index 0000000..2e2f277 Binary files /dev/null and b/py/__pycache__/image_blend.cpython-311.pyc differ diff --git a/py/__pycache__/imagefunc.cpython-311.pyc b/py/__pycache__/imagefunc.cpython-311.pyc index 362acfd..2d3a2cf 100644 Binary files a/py/__pycache__/imagefunc.cpython-311.pyc and b/py/__pycache__/imagefunc.cpython-311.pyc differ diff --git a/py/__pycache__/outer_glow.cpython-311.pyc b/py/__pycache__/outer_glow.cpython-311.pyc index e240113..9c48645 100644 Binary files a/py/__pycache__/outer_glow.cpython-311.pyc and b/py/__pycache__/outer_glow.cpython-311.pyc differ diff --git a/py/drop_shadow.py b/py/drop_shadow.py index 13af4b3..881e6fa 100644 --- a/py/drop_shadow.py +++ b/py/drop_shadow.py @@ -12,7 +12,6 @@ class DropShadow: "required": { "background_image": ("IMAGE", ), # "layer_image": ("IMAGE",), # - "layer_mask": ("MASK",), # "invert_mask": ("BOOLEAN", {"default": True}), # å��转mask "blend_mode": (chop_mode,), # æ··å�ˆæ¨¡å¼� "opacity": ("INT", {"default": 50, "min": 0, "max": 100, "step": 1}), # é€�明度 @@ -23,6 +22,7 @@ class DropShadow: "shadow_color": ("STRING", {"default": "#000000"}), # 背景颜色 }, "optional": { + "layer_mask": ("MASK",), # } } @@ -34,34 +34,37 @@ class DropShadow: CATEGORY = '😺dzNodes' OUTPUT_NODE = True - def drop_shadow(self, background_image, layer_image, layer_mask, + def drop_shadow(self, background_image, layer_image, invert_mask, blend_mode, opacity, distance_x, distance_y, grow, blur, shadow_color, + layer_mask=None, ): distance_x = -distance_x distance_y = -distance_y - # 处ç�†é˜´å½±mask - if invert_mask: - layer_mask = 1 - layer_mask - _layer = tensor2pil(layer_image) - _mask = mask2image(layer_mask) + _canvas = tensor2pil(background_image).convert('RGB') + _layer = tensor2pil(layer_image).convert('RGB') + _mask = tensor2pil(layer_image).convert('RGBA').split()[-1] + # 处ç�†mask + if layer_mask is not None: + if invert_mask: + layer_mask = 1 - layer_mask + _mask = mask2image(layer_mask).convert('L') + if distance_x != 0 or distance_y != 0: - _mask = shift_image(_mask, distance_x, distance_y) # ä½�ç§» - shadow_mask = expand_mask(image2mask(_mask), grow, blur, 0) #扩张,模糊 + __mask = shift_image(_mask, distance_x, distance_y) # ä½�ç§» + shadow_mask = expand_mask(image2mask(__mask), grow, blur, 0) #扩张,模糊 # å�ˆæˆ�阴影 shadow_color = Image.new("RGB", _layer.size, color=shadow_color) alpha = tensor2pil(shadow_mask).convert('L') - _canvas = tensor2pil(background_image) - _shadow = chop_image(tensor2pil(background_image), shadow_color, blend_mode, opacity) + _shadow = chop_image(_canvas, shadow_color, blend_mode, opacity) _canvas.paste(_shadow, mask=alpha) # å�ˆæˆ�layer - alpha = tensor2pil(layer_mask).convert('L') - _canvas.paste(_layer, mask=alpha) + _canvas.paste(_layer, mask=_mask) ret_image = _canvas - ret_mask = shadow_mask + # ret_mask = shadow_mask # return (pil2tensor(ret_image), ret_mask,) return (pil2tensor(ret_image),) diff --git a/py/image_blend.py b/py/image_blend.py new file mode 100644 index 0000000..aec27b9 --- /dev/null +++ b/py/image_blend.py @@ -0,0 +1,59 @@ +from .imagefunc import * + +class ImageBlend: + + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(self): + chop_mode = ['normal','multply','screen','add','subtract','difference','darker','lighter'] + return { + "required": { + "background_image": ("IMAGE", ), # + "layer_image": ("IMAGE",), # + + "invert_mask": ("BOOLEAN", {"default": True}), # å��转mask + "blend_mode": (chop_mode,), # æ··å�ˆæ¨¡å¼� + "opacity": ("INT", {"default": 100, "min": 0, "max": 100, "step": 1}), # é€�明度 + }, + "optional": { + "layer_mask": ("MASK",), # + } + } + + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("image",) + FUNCTION = 'image_blend' + CATEGORY = '😺dzNodes' + OUTPUT_NODE = True + + def image_blend(self, background_image, layer_image, + invert_mask, blend_mode, opacity, + layer_mask=None, + ): + + _canvas = tensor2pil(background_image).convert('RGB') + _layer = tensor2pil(layer_image).convert('RGB') + _mask = tensor2pil(layer_image).convert('RGBA').split()[-1] + if layer_mask is not None: + if invert_mask: + layer_mask = 1 - layer_mask + _mask = mask2image(layer_mask).convert('L') + + # å�ˆæˆ�layer + _comp = chop_image(_canvas, _layer, blend_mode, opacity) + + _canvas.paste(_comp, mask=_mask) + + + ret_image = _canvas + return (pil2tensor(ret_image),) + +NODE_CLASS_MAPPINGS = { + "LayerStyle_ImageBlend": ImageBlend +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "LayerStyle_ImageBlend": "LayerStyle: ImageBlend" +} \ No newline at end of file diff --git a/py/imagefunc.py b/py/imagefunc.py index 4929fe1..0fb7f47 100644 --- a/py/imagefunc.py +++ b/py/imagefunc.py @@ -35,7 +35,6 @@ def image2mask(image:Image) -> torch.Tensor: def mask2image(mask:torch.Tensor) -> Image: masks = tensor2np(mask) - # images = [] for m in masks: _mask = Image.fromarray(m).convert("L") _image = Image.new("RGBA", _mask.size, color='white') diff --git a/py/outer_glow.py b/py/outer_glow.py index b06baa4..fab82fe 100644 --- a/py/outer_glow.py +++ b/py/outer_glow.py @@ -12,7 +12,6 @@ class OuterGlow: "required": { "background_image": ("IMAGE", ), # "layer_image": ("IMAGE",), # - "layer_mask": ("MASK",), # "invert_mask": ("BOOLEAN", {"default": True}), # å��转mask "blend_mode": (chop_mode,), # æ··å�ˆæ¨¡å¼� "opacity": ("INT", {"default": 100, "min": 0, "max": 100, "step": 1}), # é€�明度 @@ -21,6 +20,7 @@ class OuterGlow: "glow_color": ("STRING", {"default": "#FFFFFF"}), # 背景颜色 }, "optional": { + "layer_mask": ("MASK",), # } } @@ -32,31 +32,34 @@ class OuterGlow: CATEGORY = '😺dzNodes' OUTPUT_NODE = True - def outer_glow(self, background_image, layer_image, layer_mask, + def outer_glow(self, background_image, layer_image, invert_mask, blend_mode, opacity, grow, blur, glow_color, + layer_mask=None, ): + _canvas = tensor2pil(background_image).convert('RGB') + _layer = tensor2pil(layer_image).convert('RGB') + _mask = tensor2pil(layer_image).convert('RGBA').split()[-1] # 处ç�†mask - if invert_mask: - layer_mask = 1 - layer_mask - _layer = tensor2pil(layer_image) - _mask = mask2image(layer_mask) + if layer_mask is not None: + if invert_mask: + layer_mask = 1 - layer_mask + _mask = mask2image(layer_mask).convert('L') + glow_mask = expand_mask(image2mask(_mask), grow, blur, 0) #扩张,模糊 # å�ˆæˆ�glow glow_color = Image.new("RGB", _layer.size, color=glow_color) alpha = tensor2pil(glow_mask).convert('L') - _canvas = tensor2pil(background_image) _glow = chop_image(tensor2pil(background_image), glow_color, blend_mode, opacity) _canvas.paste(_glow, mask=alpha) # å�ˆæˆ�layer - alpha = tensor2pil(layer_mask).convert('L') - _canvas.paste(_layer, mask=alpha) + _canvas.paste(_layer, mask=_mask) ret_image = _canvas - ret_mask = glow_mask + # ret_mask = glow_mask # return (pil2tensor(ret_image), ret_mask,) return (pil2tensor(ret_image),)