From 1e0ca3f09e5f77ec6f9200b9e05cb573837f8fdd Mon Sep 17 00:00:00 2001 From: pjpollot Date: Fri, 27 Sep 2024 11:54:48 +0900 Subject: [PATCH 1/3] implement merge by node mask --- MaskNodes.py | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/MaskNodes.py b/MaskNodes.py index 6952489..8caca9b 100644 --- a/MaskNodes.py +++ b/MaskNodes.py @@ -969,7 +969,63 @@ def paste(self, image_base, image_to_paste, mask, resize_behavior, mask_mapping_ else: paste_mask = torch.min(pasting_alpha, mask[i]).unsqueeze(2).repeat(1, 1, 4) result[image_index] = pasting * paste_mask + result[image_index] * (1. - paste_mask) + return (result,) + + +class MergeByMask: + """ + Merge `image_to_paste` onto `image_base` using `mask` to determine the location. + """ + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "image_base": ("IMAGE",), + "image_to_paste": ("IMAGE",), + "mask": ("IMAGE",), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "merge" + + CATEGORY = "Masquerade Nodes" + + def merge(self, image_base, image_to_paste, mask): + image_to_paste = tensor2rgba(image_to_paste) + B, H, W, C = image_to_paste.shape + + image_base = tensor2rgba(image_base) + # batchify the image_base tensor + if image_base.shape[0] == 1: + image_base = image_base.repeat(B, 1, 1, 1) + else: + raise ValueError( + "image_base must be a single image.\n" + f"Here: #(image_base)={image_base.shape[0]}." + ) + if image_base.shape[1:] != torch.Size([H,W,C]): + raise ValueError( + "image_base must have the same format as image_to_paste.\n" + f"Here: image_base -> {tuple(image_base.shape[1:])}; image_to_paste -> {(H,W,C)}." + ) + + mask = tensor2mask(mask) + if mask.shape[1:] != torch.Size([H, W]): + raise ValueError( + "mask must have the same size as image_base (and image_to_paste).\n" + f"Here: mask -> {tuple(mask.shape[1:])}; base_image -> {(H, W)}." + ) + mask = mask.unsqueeze(-1).repeat(B, 1, 1, 4) + + result = mask * image_to_paste + (1-mask) * image_base + + return (result,) + class GetImageSize: def __init__(self): @@ -1333,6 +1389,7 @@ def increment(self, seed, max_value): "Mask To Region": MaskToRegion, "Cut By Mask": CutByMask, "Paste By Mask": PasteByMask, + "Merge By Mask": MergeByMask, "Get Image Size": GetImageSize, "Change Channel Count": ChangeChannelCount, "Constant Mask": ConstantMask, @@ -1358,6 +1415,7 @@ def increment(self, seed, max_value): "Mask To Region": "Mask To Region", "Cut By Mask": "Cut By Mask", "Paste By Mask": "Paste By Mask", + "Merge By Mask": "Merge By Mask", "Get Image Size": "Get Image Size", "Change Channel Count": "Change Channel Count", "Constant Mask": "Constant Mask", From 609a308479b33210e6f313f62e55da0b7a4f298e Mon Sep 17 00:00:00 2001 From: pjpollot Date: Fri, 27 Sep 2024 16:28:31 +0900 Subject: [PATCH 2/3] merge-by-mask example workflow --- examples/merge-with-mask.json | 553 ++++++++++++++++++++++++++++++++++ 1 file changed, 553 insertions(+) create mode 100644 examples/merge-with-mask.json diff --git a/examples/merge-with-mask.json b/examples/merge-with-mask.json new file mode 100644 index 0000000..1abb373 --- /dev/null +++ b/examples/merge-with-mask.json @@ -0,0 +1,553 @@ +{ + "last_node_id": 28, + "last_link_id": 30, + "nodes": [ + { + "id": 18, + "type": "MaskToImage", + "pos": { + "0": 2950, + "1": 303 + }, + "size": { + "0": 264.5999755859375, + "1": 26 + }, + "flags": {}, + "order": 0, + "mode": 0, + "inputs": [ + { + "name": "mask", + "type": "MASK", + "link": null + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 18 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "MaskToImage" + } + }, + { + "id": 17, + "type": "PreviewImage", + "pos": { + "0": 2984, + "1": 381 + }, + "size": { + "0": 210, + "1": 246 + }, + "flags": {}, + "order": 5, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 18 + } + ], + "outputs": [], + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 28, + "type": "Get Image Size", + "pos": { + "0": 875, + "1": -118 + }, + "size": { + "0": 210, + "1": 46 + }, + "flags": {}, + "order": 6, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 28 + } + ], + "outputs": [ + { + "name": "width", + "type": "INT", + "links": [ + 29 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "height", + "type": "INT", + "links": [ + 30 + ], + "shape": 3, + "slot_index": 1 + } + ], + "properties": { + "Node name for S&R": "Get Image Size" + } + }, + { + "id": 24, + "type": "Merge By Mask", + "pos": { + "0": 1613, + "1": -94 + }, + "size": { + "0": 254.40000915527344, + "1": 66 + }, + "flags": {}, + "order": 9, + "mode": 0, + "inputs": [ + { + "name": "image_base", + "type": "IMAGE", + "link": 22 + }, + { + "name": "image_to_paste", + "type": "IMAGE", + "link": 26 + }, + { + "name": "mask", + "type": "IMAGE", + "link": 23 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 24 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "Merge By Mask" + } + }, + { + "id": 20, + "type": "LoadImage", + "pos": { + "0": 427, + "1": -425 + }, + "size": { + "0": 315, + "1": 314 + }, + "flags": {}, + "order": 1, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 22, + 28 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 + } + ], + "title": "base_image", + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "base_image.png", + "image" + ] + }, + { + "id": 21, + "type": "LoadImage", + "pos": { + "0": 425, + "1": -807 + }, + "size": { + "0": 315, + "1": 314 + }, + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 23 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 + } + ], + "title": "base_image_mask", + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "mask.png", + "image" + ] + }, + { + "id": 25, + "type": "PreviewImage", + "pos": { + "0": 1617, + "1": 25 + }, + "size": [ + 599.4408521578694, + 304.06028651513566 + ], + "flags": {}, + "order": 10, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 24 + } + ], + "outputs": [], + "properties": { + "Node name for S&R": "PreviewImage" + }, + "color": "#322", + "bgcolor": "#533" + }, + { + "id": 19, + "type": "LoadImage", + "pos": { + "0": -32, + "1": 56 + }, + "size": [ + 315, + 314 + ], + "flags": {}, + "order": 3, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 19 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 + } + ], + "title": "input_1", + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "image_to_paste_1.png", + "image" + ] + }, + { + "id": 22, + "type": "LoadImage", + "pos": { + "0": 333, + "1": 64 + }, + "size": { + "0": 315, + "1": 314 + }, + "flags": {}, + "order": 4, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 20 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 + } + ], + "title": "input_2", + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "image_to_paste_2.jpg", + "image" + ] + }, + { + "id": 23, + "type": "ImageBatch", + "pos": { + "0": 874, + "1": -25 + }, + "size": { + "0": 210, + "1": 46 + }, + "flags": {}, + "order": 7, + "mode": 0, + "inputs": [ + { + "name": "image1", + "type": "IMAGE", + "link": 19 + }, + { + "name": "image2", + "type": "IMAGE", + "link": 20 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 25 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "ImageBatch" + } + }, + { + "id": 26, + "type": "ImageScale", + "pos": { + "0": 1135, + "1": -112 + }, + "size": [ + 315, + 130 + ], + "flags": {}, + "order": 8, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 25 + }, + { + "name": "width", + "type": "INT", + "link": 29, + "widget": { + "name": "width" + } + }, + { + "name": "height", + "type": "INT", + "link": 30, + "widget": { + "name": "height" + } + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 26 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "ImageScale" + }, + "widgets_values": [ + "nearest-exact", + 512, + 512, + "disabled" + ] + } + ], + "links": [ + [ + 18, + 18, + 0, + 17, + 0, + "IMAGE" + ], + [ + 19, + 19, + 0, + 23, + 0, + "IMAGE" + ], + [ + 20, + 22, + 0, + 23, + 1, + "IMAGE" + ], + [ + 22, + 20, + 0, + 24, + 0, + "IMAGE" + ], + [ + 23, + 21, + 0, + 24, + 2, + "IMAGE" + ], + [ + 24, + 24, + 0, + 25, + 0, + "IMAGE" + ], + [ + 25, + 23, + 0, + 26, + 0, + "IMAGE" + ], + [ + 26, + 26, + 0, + 24, + 1, + "IMAGE" + ], + [ + 28, + 20, + 0, + 28, + 0, + "IMAGE" + ], + [ + 29, + 28, + 0, + 26, + 1, + "INT" + ], + [ + 30, + 28, + 1, + 26, + 2, + "INT" + ] + ], + "groups": [], + "config": {}, + "extra": { + "ds": { + "scale": 0.6115909044841471, + "offset": [ + -145.80489427594418, + 929.3213888819508 + ] + } + }, + "version": 0.4 +} \ No newline at end of file From afa406a82e007e293cdb3f3c46fd9bcee8bc379c Mon Sep 17 00:00:00 2001 From: pjpollot Date: Fri, 27 Sep 2024 16:57:20 +0900 Subject: [PATCH 3/3] fix example workflow --- examples/merge-with-mask.json | 379 ++++++++++++++-------------------- 1 file changed, 157 insertions(+), 222 deletions(-) diff --git a/examples/merge-with-mask.json b/examples/merge-with-mask.json index 1abb373..5913ed3 100644 --- a/examples/merge-with-mask.json +++ b/examples/merge-with-mask.json @@ -2,68 +2,6 @@ "last_node_id": 28, "last_link_id": 30, "nodes": [ - { - "id": 18, - "type": "MaskToImage", - "pos": { - "0": 2950, - "1": 303 - }, - "size": { - "0": 264.5999755859375, - "1": 26 - }, - "flags": {}, - "order": 0, - "mode": 0, - "inputs": [ - { - "name": "mask", - "type": "MASK", - "link": null - } - ], - "outputs": [ - { - "name": "IMAGE", - "type": "IMAGE", - "links": [ - 18 - ], - "shape": 3, - "slot_index": 0 - } - ], - "properties": { - "Node name for S&R": "MaskToImage" - } - }, - { - "id": 17, - "type": "PreviewImage", - "pos": { - "0": 2984, - "1": 381 - }, - "size": { - "0": 210, - "1": 246 - }, - "flags": {}, - "order": 5, - "mode": 0, - "inputs": [ - { - "name": "images", - "type": "IMAGE", - "link": 18 - } - ], - "outputs": [], - "properties": { - "Node name for S&R": "PreviewImage" - } - }, { "id": 28, "type": "Get Image Size", @@ -76,7 +14,7 @@ "1": 46 }, "flags": {}, - "order": 6, + "order": 5, "mode": 0, "inputs": [ { @@ -92,8 +30,8 @@ "links": [ 29 ], - "shape": 3, - "slot_index": 0 + "slot_index": 0, + "shape": 3 }, { "name": "height", @@ -101,43 +39,39 @@ "links": [ 30 ], - "shape": 3, - "slot_index": 1 + "slot_index": 1, + "shape": 3 } ], "properties": { "Node name for S&R": "Get Image Size" - } + }, + "widgets_values": [] }, { - "id": 24, - "type": "Merge By Mask", + "id": 23, + "type": "ImageBatch", "pos": { - "0": 1613, - "1": -94 + "0": 874, + "1": -25 }, "size": { - "0": 254.40000915527344, - "1": 66 + "0": 210, + "1": 46 }, "flags": {}, - "order": 9, + "order": 4, "mode": 0, "inputs": [ { - "name": "image_base", - "type": "IMAGE", - "link": 22 - }, - { - "name": "image_to_paste", + "name": "image1", "type": "IMAGE", - "link": 26 + "link": 19 }, { - "name": "mask", + "name": "image2", "type": "IMAGE", - "link": 23 + "link": 20 } ], "outputs": [ @@ -145,97 +79,73 @@ "name": "IMAGE", "type": "IMAGE", "links": [ - 24 + 25 ], - "shape": 3, - "slot_index": 0 + "slot_index": 0, + "shape": 3 } ], "properties": { - "Node name for S&R": "Merge By Mask" - } + "Node name for S&R": "ImageBatch" + }, + "widgets_values": [] }, { - "id": 20, - "type": "LoadImage", + "id": 26, + "type": "ImageScale", "pos": { - "0": 427, - "1": -425 + "0": 1135, + "1": -112 }, "size": { "0": 315, - "1": 314 + "1": 130 }, "flags": {}, - "order": 1, + "order": 6, "mode": 0, - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "IMAGE", + "name": "image", "type": "IMAGE", - "links": [ - 22, - 28 - ], - "shape": 3, - "slot_index": 0 + "link": 25 }, { - "name": "MASK", - "type": "MASK", - "links": null, - "shape": 3 + "name": "width", + "type": "INT", + "link": 29, + "widget": { + "name": "width" + } + }, + { + "name": "height", + "type": "INT", + "link": 30, + "widget": { + "name": "height" + } } ], - "title": "base_image", - "properties": { - "Node name for S&R": "LoadImage" - }, - "widgets_values": [ - "base_image.png", - "image" - ] - }, - { - "id": 21, - "type": "LoadImage", - "pos": { - "0": 425, - "1": -807 - }, - "size": { - "0": 315, - "1": 314 - }, - "flags": {}, - "order": 2, - "mode": 0, - "inputs": [], "outputs": [ { "name": "IMAGE", "type": "IMAGE", "links": [ - 23 + 26 ], - "shape": 3, - "slot_index": 0 - }, - { - "name": "MASK", - "type": "MASK", - "links": null, + "slot_index": 0, "shape": 3 } ], - "title": "base_image_mask", "properties": { - "Node name for S&R": "LoadImage" + "Node name for S&R": "ImageScale" }, "widgets_values": [ - "mask.png", - "image" + "nearest-exact", + 512, + 512, + "disabled" ] }, { @@ -245,12 +155,12 @@ "0": 1617, "1": 25 }, - "size": [ - 599.4408521578694, - 304.06028651513566 - ], + "size": { + "0": 599.4408569335938, + "1": 304.0602722167969 + }, "flags": {}, - "order": 10, + "order": 8, "mode": 0, "inputs": [ { @@ -260,9 +170,11 @@ } ], "outputs": [], + "title": "outputs", "properties": { "Node name for S&R": "PreviewImage" }, + "widgets_values": [], "color": "#322", "bgcolor": "#533" }, @@ -273,12 +185,12 @@ "0": -32, "1": 56 }, - "size": [ - 315, - 314 - ], + "size": { + "0": 315, + "1": 314 + }, "flags": {}, - "order": 3, + "order": 0, "mode": 0, "inputs": [], "outputs": [ @@ -288,8 +200,8 @@ "links": [ 19 ], - "shape": 3, - "slot_index": 0 + "slot_index": 0, + "shape": 3 }, { "name": "MASK", @@ -319,7 +231,7 @@ "1": 314 }, "flags": {}, - "order": 4, + "order": 1, "mode": 0, "inputs": [], "outputs": [ @@ -329,8 +241,8 @@ "links": [ 20 ], - "shape": 3, - "slot_index": 0 + "slot_index": 0, + "shape": 3 }, { "name": "MASK", @@ -349,81 +261,117 @@ ] }, { - "id": 23, - "type": "ImageBatch", + "id": 20, + "type": "LoadImage", "pos": { - "0": 874, - "1": -25 + "0": 427, + "1": -425 }, "size": { - "0": 210, - "1": 46 + "0": 315, + "1": 314 }, "flags": {}, - "order": 7, + "order": 2, "mode": 0, - "inputs": [ + "inputs": [], + "outputs": [ { - "name": "image1", + "name": "IMAGE", "type": "IMAGE", - "link": 19 + "links": [ + 22, + 28 + ], + "slot_index": 0, + "shape": 3 }, { - "name": "image2", - "type": "IMAGE", - "link": 20 + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 } ], + "title": "base_image", + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "base_image.png", + "image" + ] + }, + { + "id": 21, + "type": "LoadImage", + "pos": { + "0": 425, + "1": -807 + }, + "size": { + "0": 315, + "1": 314 + }, + "flags": {}, + "order": 3, + "mode": 0, + "inputs": [], "outputs": [ { "name": "IMAGE", "type": "IMAGE", "links": [ - 25 + 23 ], - "shape": 3, - "slot_index": 0 + "slot_index": 0, + "shape": 3 + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 } ], + "title": "base_image_mask", "properties": { - "Node name for S&R": "ImageBatch" - } + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "mask (1).png", + "image" + ] }, { - "id": 26, - "type": "ImageScale", + "id": 24, + "type": "Merge By Mask", "pos": { - "0": 1135, - "1": -112 + "0": 1610, + "1": -110 + }, + "size": { + "0": 254.40000915527344, + "1": 66 }, - "size": [ - 315, - 130 - ], "flags": {}, - "order": 8, + "order": 7, "mode": 0, "inputs": [ { - "name": "image", + "name": "image_base", "type": "IMAGE", - "link": 25 + "link": 22 }, { - "name": "width", - "type": "INT", - "link": 29, - "widget": { - "name": "width" - } + "name": "image_to_paste", + "type": "IMAGE", + "link": 26 }, { - "name": "height", - "type": "INT", - "link": 30, - "widget": { - "name": "height" - } + "name": "mask", + "type": "IMAGE", + "link": 23 } ], "outputs": [ @@ -431,32 +379,19 @@ "name": "IMAGE", "type": "IMAGE", "links": [ - 26 + 24 ], - "shape": 3, - "slot_index": 0 + "slot_index": 0, + "shape": 3 } ], "properties": { - "Node name for S&R": "ImageScale" + "Node name for S&R": "Merge By Mask" }, - "widgets_values": [ - "nearest-exact", - 512, - 512, - "disabled" - ] + "widgets_values": [] } ], "links": [ - [ - 18, - 18, - 0, - 17, - 0, - "IMAGE" - ], [ 19, 19, @@ -542,12 +477,12 @@ "config": {}, "extra": { "ds": { - "scale": 0.6115909044841471, + "scale": 0.5559917313492245, "offset": [ - -145.80489427594418, - 929.3213888819508 + 265.4389041262049, + 1004.9081206506651 ] } }, "version": 0.4 -} \ No newline at end of file +}