From 3ebf7452c32398ffed53d838cfdc35bf5257f0b8 Mon Sep 17 00:00:00 2001 From: "Guo Y.K" Date: Thu, 23 Mar 2023 23:33:35 +0800 Subject: [PATCH 1/4] nodes: add ImagePadForOutpaint --- nodes.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/nodes.py b/nodes.py index 0b8be76..1f49590 100644 --- a/nodes.py +++ b/nodes.py @@ -908,6 +908,45 @@ class ImageInvert: return (s,) +class ImagePadForOutpaint: + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "image": ("IMAGE",), + "left": ("INT", {"default": 0, "min": 0, "max": 4096, "step": 64}), + "top": ("INT", {"default": 0, "min": 0, "max": 4096, "step": 64}), + "right": ("INT", {"default": 0, "min": 0, "max": 4096, "step": 64}), + "bottom": ("INT", {"default": 0, "min": 0, "max": 4096, "step": 64}), + } + } + + RETURN_TYPES = ("IMAGE", "MASK") + FUNCTION = "expand_image" + + CATEGORY = "image" + + def expand_image(self, image, left, top, right, bottom): + d1, d2, d3, d4 = image.size() + + new_image = torch.zeros( + (d1, d2 + top + bottom, d3 + left + right, d4), + dtype=torch.float32, + ) + new_image[:, top:top + d2, left:left + d3, :] = image + + mask = torch.ones( + (d2 + top + bottom, d3 + left + right), + dtype=torch.float32, + ) + mask[top:top + d2, left:left + d3] = torch.zeros( + (d2, d3), + dtype=torch.float32, + ) + return (new_image, mask) + + NODE_CLASS_MAPPINGS = { "KSampler": KSampler, "CheckpointLoader": CheckpointLoader, @@ -926,6 +965,7 @@ NODE_CLASS_MAPPINGS = { "LoadImageMask": LoadImageMask, "ImageScale": ImageScale, "ImageInvert": ImageInvert, + "ImagePadForOutpaint": ImagePadForOutpaint, "ConditioningCombine": ConditioningCombine, "ConditioningSetArea": ConditioningSetArea, "KSamplerAdvanced": KSamplerAdvanced, From 4580f3e64420835aa73b89ec4e8c9bda48c5ee2a Mon Sep 17 00:00:00 2001 From: "Guo Y.K" Date: Fri, 24 Mar 2023 22:39:33 +0800 Subject: [PATCH 2/4] nodes: add feathering to to ImagePadForOutpaint --- nodes.py | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/nodes.py b/nodes.py index 1f49590..40e7558 100644 --- a/nodes.py +++ b/nodes.py @@ -919,6 +919,7 @@ class ImagePadForOutpaint: "top": ("INT", {"default": 0, "min": 0, "max": 4096, "step": 64}), "right": ("INT", {"default": 0, "min": 0, "max": 4096, "step": 64}), "bottom": ("INT", {"default": 0, "min": 0, "max": 4096, "step": 64}), + "feathering": ("INT", {"default": 0, "min": 0, "max": 4096, "step": 1}), } } @@ -927,7 +928,7 @@ class ImagePadForOutpaint: CATEGORY = "image" - def expand_image(self, image, left, top, right, bottom): + def expand_image(self, image, left, top, right, bottom, feathering): d1, d2, d3, d4 = image.size() new_image = torch.zeros( @@ -940,10 +941,30 @@ class ImagePadForOutpaint: (d2 + top + bottom, d3 + left + right), dtype=torch.float32, ) - mask[top:top + d2, left:left + d3] = torch.zeros( - (d2, d3), - dtype=torch.float32, - ) + + if feathering > 0 and feathering * 2 < d2 and feathering * 2 < d3: + # distances to border + mi, mj = torch.meshgrid( + torch.arange(d2, dtype=torch.float32), + torch.arange(d3, dtype=torch.float32), + indexing='ij', + ) + distances = torch.minimum( + torch.minimum(mi, mj), + torch.minimum(d2 - 1 - mi, d3 - 1 - mj), + ) + # convert distances to square falloff from 1 to 0 + t = (feathering - distances) / feathering + t.clamp_(min=0) + t.square_() + + mask[top:top + d2, left:left + d3] = t + else: + mask[top:top + d2, left:left + d3] = torch.zeros( + (d2, d3), + dtype=torch.float32, + ) + return (new_image, mask) From 4c01386c19e2dd5be3ba234124575a80283995df Mon Sep 17 00:00:00 2001 From: "Guo Y.K" Date: Sat, 25 Mar 2023 16:27:47 +0800 Subject: [PATCH 3/4] nodes: ImagePadForOutpaint: fix feathering, ignore edges not expanded --- nodes.py | 47 ++++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/nodes.py b/nodes.py index 40e7558..a3c0335 100644 --- a/nodes.py +++ b/nodes.py @@ -1,3 +1,5 @@ +import math + import torch import os @@ -942,28 +944,31 @@ class ImagePadForOutpaint: dtype=torch.float32, ) + t = torch.zeros( + (d2, d3), + dtype=torch.float32 + ) + if feathering > 0 and feathering * 2 < d2 and feathering * 2 < d3: - # distances to border - mi, mj = torch.meshgrid( - torch.arange(d2, dtype=torch.float32), - torch.arange(d3, dtype=torch.float32), - indexing='ij', - ) - distances = torch.minimum( - torch.minimum(mi, mj), - torch.minimum(d2 - 1 - mi, d3 - 1 - mj), - ) - # convert distances to square falloff from 1 to 0 - t = (feathering - distances) / feathering - t.clamp_(min=0) - t.square_() - - mask[top:top + d2, left:left + d3] = t - else: - mask[top:top + d2, left:left + d3] = torch.zeros( - (d2, d3), - dtype=torch.float32, - ) + + for i in range(d2): + for j in range(d3): + dt = i if top != 0 else d2 + db = d2 - i if bottom != 0 else d2 + + dl = j if left != 0 else d3 + dr = d3 - j if right != 0 else d3 + + d = min(dt, db, dl, dr) + + if d >= feathering: + continue + + v = (feathering - d) / feathering + + t[i, j] = v * v + + mask[top:top + d2, left:left + d3] = t return (new_image, mask) From 3b873029581329fd943a56955550f0b02c548c62 Mon Sep 17 00:00:00 2001 From: "Guo Y.K" Date: Sat, 25 Mar 2023 16:32:48 +0800 Subject: [PATCH 4/4] nodes: ImagePadForOutpaint: clean imports --- nodes.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/nodes.py b/nodes.py index a3c0335..84f8b01 100644 --- a/nodes.py +++ b/nodes.py @@ -1,5 +1,3 @@ -import math - import torch import os