diff --git a/README.MD b/README.MD index 7207333..0e01516 100644 --- a/README.MD +++ b/README.MD @@ -2,18 +2,23 @@ A set of nodes for ComfyUI that generate image like Adobe Photoshop's Layer Style. -the Drop Shadow is first completed node, and follow-up work is in progress. +Currently completed Drop Shadow and OuterGlow nodes, and follow-up work is in progress. [中文说明点这里](./README_CN.MD) +## Update +Remove the mask channel output from the DropShadow node; Add the OuterGlow node. + ## Node Description ### Drop Shadow: Generate shadow -![image](image/drop_shadow_node.png) +![image](image/drop_shadow_example.png) + Node options: +![image](image/drop_shadow_node.png) * background_image1: The background image. * layer_image1: Layer image for composite. * layer_mask1: Mask for layer_image, shadows are generated according to their shape. @@ -29,11 +34,31 @@ Node options: 1 The layer_image, layer_mask and the background_image, These three items must be of the same size. Output type: -* image:Completed image. -* shadow_mask:Shadow's mask. +* image + + +### OuterGlow: +Generate outer glow +![image](image/outer_glow_example.png) + +Node options: +![image](image/outer_glow_node.png) +* background_image1: The background image. +* layer_image1: Layer image for composite. +* layer_mask1: Mask for layer_image, shadows are generated according to their shape. +* invert_mask: Whether to reverse the mask. +* blend_mode: Blending method of glow, there are **_normal, multply, screen, add, subtract, difference, darker_** and **_lighter_**. +* opacity: Opacity of Glow. +* grow: Glow expansion amplitude. +* blur:Gkow blur level. +* glow_color: Glow color, described in hexadecimal RGB format. + +1 The layer_image, layer_mask and the background_image, These three items must be of the same size. + +输出: +* image ## Example workflow -![image](image/drop_shadow.png) image Some JSON workflow files in the workflow directory, that is example for ComfyUI. ## How to install diff --git a/README_CN.MD b/README_CN.MD index 6ae06e6..5ab602c 100644 --- a/README_CN.MD +++ b/README_CN.MD @@ -1,15 +1,20 @@ # ComfyUI Layer Style£º ComfyUI²å¼þ£¬Í¨¹ýÒ»×é½ÚµãʵÏÖ·ÂÕÕAdobe PhotoshopµÄͼ²ãÑùʽ¡£ -Drop ShadowÊÇÊ׸öÍê³ÉµÄ½Úµã£¬ºóÐø¹¤×÷½øÐÐÖС£ +µ±Ç°ÒÑÍê³ÉDrop ShadowºÍOuterGlow½Úµã£¬ºóÐø¹¤×÷½øÐÐÖС£ + +## ¸üÐÂ˵Ã÷£º +DropShadow½ÚµãÈ¥µôµ÷ÊÔmaskͨµÀÊä³ö£»Ìí¼ÓOuterGlow½Úµã¡£ ## ½Úµã˵Ã÷£º -### Drop Shadow: +### DropShadow: Éú³ÉÒõÓ°¡£ +![image](image/drop_shadow_example.png) + + + +½ÚµãÑ¡Ïî˵Ã÷£º ![image](image/drop_shadow_node.png) - - -Ñ¡Ïî˵Ã÷£º * background_image1: ±³¾°Í¼Ïñ¡£ * layer_image1: ÓÃÓںϳɵIJãͼÏñ¡£ * layer_mask1: ²ãͼÏñµÄÕÚÕÖ£¬ÒõÓ°°´´ËÉú³É¡£ @@ -25,11 +30,32 @@ Drop Shadow 1 background_image, layer_image, layer_maskÕâÈýÏî±ØÐëÊÇÏàͬµÄ³ß´ç¡£ Êä³ö£º -* image£ºÍê³ÉµÄͼÏñ¡£ -* shadow_mask£ºÒõÓ°µÄͨµÀ¡£ +* image + +### OuterGlow£º +Éú³ÉÍâ·¢¹â¡£ +![image](image/outer_glow_example.png) + +½ÚµãÑ¡Ïî˵Ã÷£º +![image](image/outer_glow_node.png) +* background_image1: ±³¾°Í¼Ïñ¡£ +* layer_image1: ÓÃÓںϳɵIJãͼÏñ¡£ +* layer_mask1: ²ãͼÏñµÄÕÚÕÖ£¬ÒõÓ°°´´ËÉú³É¡£ +* invert_mask: ÊÇ·ñ·´×ªÕÚÕÖ¡£ +* blend_mode: Íâ·¢¹âµÄ»ìºÏ·½Ê½£¬°üÀ¨normal¡¢multply¡¢screen¡¢add¡¢subtract¡¢difference¡¢darkerºÍlighter¡£ +* opacity: Íâ·¢¹âµÄ²»Í¸Ã÷¶È¡£ +* grow: Íâ·¢¹âÀ©ÕÅ·ù¶È¡£ +* blur£ºÍâ·¢¹âÄ£ºý³Ì¶È¡£ +* glow_color: Íâ·¢¹âÑÕÉ«£¬Ê¹ÓÃ16½øÖÆRGB¸ñʽÃèÊö¡£ + +1 background_image, layer_image, layer_maskÕâÈýÏî±ØÐëÊÇÏàͬµÄ³ß´ç¡£ + +Êä³ö£º +* image + ## ʹÓÃʾÀý£º -![image](image/drop_shadow.png) + ÔÚworkflowĿ¼ÏÂÓÐjson¸ñʽµÄ¹¤×÷Á÷ʾÀýÎļþ¡£ ## °²×°·½·¨£º diff --git a/image/drop_shadow_example.png b/image/drop_shadow_example.png new file mode 100644 index 0000000..5756603 Binary files /dev/null and b/image/drop_shadow_example.png differ diff --git a/image/drop_shadow_node.png b/image/drop_shadow_node.png index f1fd688..c481aff 100644 Binary files a/image/drop_shadow_node.png and b/image/drop_shadow_node.png differ diff --git a/image/outer_glow_example.png b/image/outer_glow_example.png new file mode 100644 index 0000000..14aa6b8 Binary files /dev/null and b/image/outer_glow_example.png differ diff --git a/image/outer_glow_node.png b/image/outer_glow_node.png new file mode 100644 index 0000000..b728c48 Binary files /dev/null and b/image/outer_glow_node.png differ diff --git a/py/__pycache__/drop_shadow.cpython-311.pyc b/py/__pycache__/drop_shadow.cpython-311.pyc index 8278705..f8f4174 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__/imagefunc.cpython-311.pyc b/py/__pycache__/imagefunc.cpython-311.pyc index 71921f3..362acfd 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 new file mode 100644 index 0000000..ca2d2f3 Binary files /dev/null and b/py/__pycache__/outer_glow.cpython-311.pyc differ diff --git a/py/drop_shadow.py b/py/drop_shadow.py index a4461f5..13af4b3 100644 --- a/py/drop_shadow.py +++ b/py/drop_shadow.py @@ -23,12 +23,13 @@ class DropShadow: "shadow_color": ("STRING", {"default": "#000000"}), # 背景颜色 }, "optional": { - # "test_mask": ("MASK",), # } } - RETURN_TYPES = ("IMAGE", "MASK",) - RETURN_NAMES = ("image", "shadow_mask",) + # RETURN_TYPES = ("IMAGE", "MASK",) + # RETURN_NAMES = ("image", "shadow_mask",) + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("image",) FUNCTION = 'drop_shadow' CATEGORY = '😺dzNodes' OUTPUT_NODE = True @@ -61,12 +62,13 @@ class DropShadow: ret_image = _canvas ret_mask = shadow_mask - return (pil2tensor(ret_image), ret_mask,) + # return (pil2tensor(ret_image), ret_mask,) + return (pil2tensor(ret_image),) NODE_CLASS_MAPPINGS = { "LayerStyle_DropShadow": DropShadow } NODE_DISPLAY_NAME_MAPPINGS = { - "LayerStyle_DropShadow": "Layer Style: Drop Shadow" + "LayerStyle_DropShadow": "LayerStyle: DropShadow" } \ No newline at end of file diff --git a/py/outer_glow.py b/py/outer_glow.py new file mode 100644 index 0000000..b06baa4 --- /dev/null +++ b/py/outer_glow.py @@ -0,0 +1,69 @@ +from .imagefunc import * + +class OuterGlow: + + 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",), # + "layer_mask": ("MASK",), # + "invert_mask": ("BOOLEAN", {"default": True}), # å��转mask + "blend_mode": (chop_mode,), # æ··å�ˆæ¨¡å¼� + "opacity": ("INT", {"default": 100, "min": 0, "max": 100, "step": 1}), # é€�明度 + "grow": ("INT", {"default": 16, "min": -9999, "max": 9999, "step": 1}), # 扩张 + "blur": ("INT", {"default": 20, "min": 0, "max": 100, "step": 1}), # 模糊 + "glow_color": ("STRING", {"default": "#FFFFFF"}), # 背景颜色 + }, + "optional": { + } + } + + # RETURN_TYPES = ("IMAGE", "MASK",) + # RETURN_NAMES = ("image", "glow_mask",) + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("image",) + FUNCTION = 'outer_glow' + CATEGORY = '😺dzNodes' + OUTPUT_NODE = True + + def outer_glow(self, background_image, layer_image, layer_mask, + invert_mask, blend_mode, opacity, + grow, blur, glow_color, + ): + + # 处ç�†mask + if invert_mask: + layer_mask = 1 - layer_mask + _layer = tensor2pil(layer_image) + _mask = mask2image(layer_mask) + 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) + + ret_image = _canvas + ret_mask = glow_mask + # return (pil2tensor(ret_image), ret_mask,) + return (pil2tensor(ret_image),) + +NODE_CLASS_MAPPINGS = { + "LayerStyle_OuterGlow": OuterGlow +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "LayerStyle_OuterGlow": "LayerStyle: OuterGlow" +} \ No newline at end of file diff --git a/workflow/512x512.png b/workflow/512x512.png new file mode 100644 index 0000000..334b79b Binary files /dev/null and b/workflow/512x512.png differ diff --git a/workflow/512x512bkgd.jpg b/workflow/512x512bkgd.jpg new file mode 100644 index 0000000..0ffd73f Binary files /dev/null and b/workflow/512x512bkgd.jpg differ diff --git a/workflow/drop_shadow_example.json b/workflow/drop_shadow_example.json index 33c3a2f..a4241ed 100644 --- a/workflow/drop_shadow_example.json +++ b/workflow/drop_shadow_example.json @@ -1,13 +1,95 @@ { - "last_node_id": 20, - "last_link_id": 49, + "last_node_id": 23, + "last_link_id": 58, "nodes": [ + { + "id": 22, + "type": "LayerStyle_DropShadow", + "pos": [ + 540, + -660 + ], + "size": { + "0": 315, + "1": 266 + }, + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [ + { + "name": "background_image", + "type": "IMAGE", + "link": 54 + }, + { + "name": "layer_image", + "type": "IMAGE", + "link": 55 + }, + { + "name": "layer_mask", + "type": "MASK", + "link": 56 + } + ], + "outputs": [ + { + "name": "image", + "type": "IMAGE", + "links": [ + 58 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "LayerStyle_DropShadow" + }, + "widgets_values": [ + true, + "normal", + 50, + 5, + 5, + 2, + 15, + "#000000" + ] + }, + { + "id": 23, + "type": "SaveImage", + "pos": [ + 890, + -660 + ], + "size": [ + 315, + 270 + ], + "flags": {}, + "order": 3, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 58 + } + ], + "properties": {}, + "widgets_values": [ + "ComfyUI" + ] + }, { "id": 2, "type": "LoadImage", "pos": [ - 237, - -31 + 180, + -680 ], "size": { "0": 315, @@ -21,7 +103,7 @@ "name": "IMAGE", "type": "IMAGE", "links": [ - 45 + 55 ], "shape": 3, "slot_index": 0 @@ -30,7 +112,7 @@ "name": "MASK", "type": "MASK", "links": [ - 46 + 56 ], "shape": 3, "slot_index": 1 @@ -44,157 +126,26 @@ "image" ] }, - { - "id": 17, - "type": "RGB_Picker", - "pos": [ - 332, - -180 - ], - "size": { - "0": 210, - "1": 94 - }, - "flags": {}, - "order": 1, - "mode": 0, - "outputs": [ - { - "name": "value", - "type": "STRING", - "links": [ - 49 - ], - "shape": 3, - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "RGB_Picker" - }, - "widgets_values": [ - "#2b0303", - "HEX" - ] - }, - { - "id": 9, - "type": "PreviewImage", - "pos": [ - 955, - -13 - ], - "size": { - "0": 210, - "1": 246 - }, - "flags": {}, - "order": 4, - "mode": 0, - "inputs": [ - { - "name": "images", - "type": "IMAGE", - "link": 47 - } - ], - "properties": { - "Node name for S&R": "PreviewImage" - } - }, - { - "id": 20, - "type": "LayerStyle_DropShadow", - "pos": [ - 576, - -21 - ], - "size": [ - 355.20001220703125, - 266 - ], - "flags": {}, - "order": 3, - "mode": 0, - "inputs": [ - { - "name": "background_image", - "type": "IMAGE", - "link": 44 - }, - { - "name": "layer_image", - "type": "IMAGE", - "link": 45 - }, - { - "name": "layer_mask", - "type": "MASK", - "link": 46 - }, - { - "name": "shadow_color", - "type": "STRING", - "link": 49, - "widget": { - "name": "shadow_color" - } - } - ], - "outputs": [ - { - "name": "image", - "type": "IMAGE", - "links": [ - 47 - ], - "shape": 3, - "slot_index": 0 - }, - { - "name": "shadow_mask", - "type": "MASK", - "links": [ - 48 - ], - "shape": 3, - "slot_index": 1 - } - ], - "properties": { - "Node name for S&R": "LayerStyle_DropShadow" - }, - "widgets_values": [ - true, - "multply", - 52, - 43, - 45, - 2, - 20, - "#000000" - ] - }, { "id": 3, "type": "LoadImage", "pos": [ - -102, - -33 + -160, + -680 ], "size": { "0": 315, "1": 314 }, "flags": {}, - "order": 2, + "order": 1, "mode": 0, "outputs": [ { "name": "IMAGE", "type": "IMAGE", "links": [ - 44 + 54 ], "shape": 3, "slot_index": 0 @@ -213,125 +164,40 @@ "512x512bkgd.jpg", "image" ] - }, - { - "id": 10, - "type": "MaskToImage", - "pos": [ - 957, - 282 - ], - "size": { - "0": 210, - "1": 26 - }, - "flags": {}, - "order": 5, - "mode": 0, - "inputs": [ - { - "name": "mask", - "type": "MASK", - "link": 48 - } - ], - "outputs": [ - { - "name": "IMAGE", - "type": "IMAGE", - "links": [ - 11 - ], - "shape": 3, - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "MaskToImage" - } - }, - { - "id": 11, - "type": "PreviewImage", - "pos": [ - 1188, - -15 - ], - "size": { - "0": 210, - "1": 246 - }, - "flags": {}, - "order": 6, - "mode": 0, - "inputs": [ - { - "name": "images", - "type": "IMAGE", - "link": 11 - } - ], - "properties": { - "Node name for S&R": "PreviewImage" - } } ], "links": [ [ - 11, - 10, - 0, - 11, - 0, - "IMAGE" - ], - [ - 44, + 54, 3, 0, - 20, + 22, 0, "IMAGE" ], [ - 45, + 55, 2, 0, - 20, + 22, 1, "IMAGE" ], [ - 46, + 56, 2, 1, - 20, + 22, 2, "MASK" ], [ - 47, - 20, + 58, + 22, 0, - 9, + 23, 0, "IMAGE" - ], - [ - 48, - 20, - 1, - 10, - 0, - "MASK" - ], - [ - 49, - 17, - 0, - 20, - 3, - "STRING" ] ], "groups": [], diff --git a/workflow/outer_glow_example.json b/workflow/outer_glow_example.json new file mode 100644 index 0000000..56e788c --- /dev/null +++ b/workflow/outer_glow_example.json @@ -0,0 +1,205 @@ +{ + "last_node_id": 24, + "last_link_id": 62, + "nodes": [ + { + "id": 23, + "type": "SaveImage", + "pos": [ + 890, + -660 + ], + "size": [ + 315, + 270 + ], + "flags": {}, + "order": 3, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 62 + } + ], + "properties": {}, + "widgets_values": [ + "ComfyUI" + ] + }, + { + "id": 3, + "type": "LoadImage", + "pos": [ + -160, + -680 + ], + "size": { + "0": 315, + "1": 314 + }, + "flags": {}, + "order": 0, + "mode": 0, + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 59 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 + } + ], + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "512x512bkgd.jpg", + "image" + ] + }, + { + "id": 2, + "type": "LoadImage", + "pos": [ + 180, + -680 + ], + "size": { + "0": 315, + "1": 314 + }, + "flags": {}, + "order": 1, + "mode": 0, + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 60 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": [ + 61 + ], + "shape": 3, + "slot_index": 1 + } + ], + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "512x512.png", + "image" + ] + }, + { + "id": 24, + "type": "LayerStyle_OuterGlow", + "pos": [ + 536, + -631 + ], + "size": { + "0": 315, + "1": 218 + }, + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [ + { + "name": "background_image", + "type": "IMAGE", + "link": 59 + }, + { + "name": "layer_image", + "type": "IMAGE", + "link": 60 + }, + { + "name": "layer_mask", + "type": "MASK", + "link": 61 + } + ], + "outputs": [ + { + "name": "image", + "type": "IMAGE", + "links": [ + 62 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "LayerStyle_OuterGlow" + }, + "widgets_values": [ + true, + "normal", + 100, + 16, + 20, + "#FFFFFF" + ] + } + ], + "links": [ + [ + 59, + 3, + 0, + 24, + 0, + "IMAGE" + ], + [ + 60, + 2, + 0, + 24, + 1, + "IMAGE" + ], + [ + 61, + 2, + 1, + 24, + 2, + "MASK" + ], + [ + 62, + 24, + 0, + 23, + 0, + "IMAGE" + ] + ], + "groups": [], + "config": {}, + "extra": {}, + "version": 0.4 +} \ No newline at end of file