diff --git a/image/icmask_example.jpg b/image/icmask_example.jpg index ded5665..19b7f4c 100644 Binary files a/image/icmask_example.jpg and b/image/icmask_example.jpg differ diff --git a/py/ic_mask.py b/py/ic_mask.py index 084a157..b897641 100644 --- a/py/ic_mask.py +++ b/py/ic_mask.py @@ -4,7 +4,7 @@ import torch import numpy as np from PIL import Image import cv2 -from .imagefunc import log +from .imagefunc import log, fit_resize_image, tensor2pil, pil2tensor def resize_img(img, resolution, interpolation=cv2.INTER_CUBIC): @@ -27,7 +27,6 @@ def fit_image(image, mask=None, output_length=1536, patch_mode="auto"): if mask is not None: mask = mask.detach().cpu().numpy() - # print("np.all(mask == 0)",np.all(mask == 0)) base_length = int(output_length / 3 * 2) half_length = int(output_length / 2) image_height, image_width, _ = image.shape @@ -88,14 +87,25 @@ def fit_image(image, mask=None, output_length=1536, patch_mode="auto"): return resized_image, resized_mask, target_width, target_height, patch_mode +def crop_and_scale_as(image:Image, size:tuple): + + target_width, target_height = size + _image = Image.new('RGB', size=size, color='black') + + ret_image = fit_resize_image(image, target_width, target_height, "crop", Image.LANCZOS) + return ret_image + + class ICMask_Data: - def __init__(self, x_offset, y_offset, target_width, target_height, total_width, total_height): + def __init__(self, x_offset, y_offset, target_width, target_height, total_width, total_height, orig_width, orig_height): self.x_offset = x_offset self.y_offset = y_offset self.target_width = target_width self.target_height = target_height self.total_width = total_width self.total_height = total_height + self.orig_width = orig_width + self.orig_height = orig_height class LS_ICMask: @@ -131,7 +141,8 @@ class LS_ICMask: def ic_mask(self, first_image, patch_mode, output_length, patch_color, first_mask=None, second_image=None, second_mask=None): - + orig_width = 0 + orig_height = 0 if output_length % 64 != 0: output_length = output_length - (output_length % 64) @@ -149,6 +160,8 @@ class LS_ICMask: image2_mask = torch.zeros((image2.shape[0], image2.shape[1])) else: image2_mask = second_mask[0] + orig_width = image2.shape[1] + orig_height = image2.shape[0] image2, image2_mask, _, _, _ = fit_image(image2, image2_mask, output_length, patch_mode) else: image2 = create_image_from_color(target_width, target_height, color=patch_color) @@ -157,6 +170,8 @@ class LS_ICMask: image2_mask = torch.zeros((image2.shape[0], image2.shape[1])) else: image2_mask = second_mask[0] + orig_width = image2.shape[1] + orig_height = image2.shape[0] image2, image2_mask, _, _, _ = fit_image(image2, image2_mask, output_length) min_y = 0 @@ -183,7 +198,7 @@ class LS_ICMask: return_images = concatenated_image icmask_data = ICMask_Data(min_x, min_y, target_width, target_height, concatenated_image.shape[1], - concatenated_image.shape[0]) + concatenated_image.shape[0], orig_width, orig_height) return (return_images, return_masks, icmask_data) @@ -199,20 +214,24 @@ class LS_ICMask_CropBack: "icmask_data": ("ICMASK_DATA",), }} RETURN_TYPES = ("IMAGE",) - FUNCTION = "crop" + FUNCTION = "crop_back" CATEGORY = '😺dzNodes/LayerUtility' - def crop(self, image, icmask_data): + def crop_back(self, image, icmask_data): width = icmask_data.target_width height = icmask_data.target_height x = icmask_data.x_offset y = icmask_data.y_offset + orig_width = icmask_data.orig_width + orig_height = icmask_data.orig_height x = min(x, image.shape[2] - 1) y = min(y, image.shape[1] - 1) to_x = width + x to_y = height + y img = image[:,y:to_y, x:to_x, :] - return (img,) + pil_image = tensor2pil(img) + ret_image = crop_and_scale_as(pil_image, (orig_width, orig_height)) + return (pil2tensor(ret_image,),) NODE_CLASS_MAPPINGS = { "LayerUtility: ICMask": LS_ICMask, @@ -221,5 +240,5 @@ NODE_CLASS_MAPPINGS = { NODE_DISPLAY_NAME_MAPPINGS = { "LayerUtility: ICMask": "LayerUtility: IC Mask", - "LayerUtility: ICMaskCropBack": "LayerUtility: IC Mask Crop Back" + "LayerUtility: ICMaskCropBack": "LayerUtility: IC Mask Crop Back", } \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 08acd16..b2c7a65 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui_layerstyle" description = "A set of nodes for ComfyUI it generate image like Adobe Photoshop's Layer Style. the Drop Shadow is first completed node, and follow-up work is in progress." -version = "2.0.7" +version = "2.0.8" license = "MIT" dependencies = ["numpy", "pillow", "torch", "matplotlib", "Scipy", "scikit_image", "scikit_learn", "opencv-contrib-python", "pymatting", "timm", "colour-science", "transformers", "blend_modes", "huggingface_hub", "loguru"] diff --git a/workflow/icmask_example.json b/workflow/icmask_example.json index 7b82d3c..37d9df5 100644 --- a/workflow/icmask_example.json +++ b/workflow/icmask_example.json @@ -1,6 +1,6 @@ { - "last_node_id": 439, - "last_link_id": 634, + "last_node_id": 441, + "last_link_id": 638, "nodes": [ { "id": 11, @@ -682,8 +682,7 @@ "name": "croped_image", "type": "IMAGE", "links": [ - 598, - 616 + 598 ], "slot_index": 0 }, @@ -782,138 +781,6 @@ ], "color": "rgba(27, 80, 119, 0.7)" }, - { - "id": 432, - "type": "LayerUtility: ImageMaskScaleAs", - "pos": [ - 1282.739013671875, - 1840.77587890625 - ], - "size": [ - 378, - 162 - ], - "flags": {}, - "order": 20, - "mode": 0, - "inputs": [ - { - "name": "scale_as", - "type": "*", - "link": 616 - }, - { - "name": "image", - "type": "IMAGE", - "link": 615, - "shape": 7 - }, - { - "name": "mask", - "type": "MASK", - "link": null, - "shape": 7 - } - ], - "outputs": [ - { - "name": "image", - "type": "IMAGE", - "links": [ - 617 - ], - "slot_index": 0 - }, - { - "name": "mask", - "type": "MASK", - "links": null - }, - { - "name": "original_size", - "type": "BOX", - "links": null - }, - { - "name": "widht", - "type": "INT", - "links": null - }, - { - "name": "height", - "type": "INT", - "links": null - } - ], - "properties": { - "Node name for S&R": "LayerUtility: ImageMaskScaleAs" - }, - "widgets_values": [ - "crop", - "lanczos" - ], - "color": "rgba(38, 73, 116, 0.7)" - }, - { - "id": 370, - "type": "LayerUtility: RestoreCropBox", - "pos": [ - 1290.447998046875, - 2105.74853515625 - ], - "size": [ - 365.7912902832031, - 118 - ], - "flags": {}, - "order": 21, - "mode": 0, - "inputs": [ - { - "name": "background_image", - "type": "IMAGE", - "link": 610 - }, - { - "name": "croped_image", - "type": "IMAGE", - "link": 617 - }, - { - "name": "crop_box", - "type": "BOX", - "link": 460 - }, - { - "name": "croped_mask", - "type": "MASK", - "link": null, - "shape": 7 - } - ], - "outputs": [ - { - "name": "image", - "type": "IMAGE", - "links": [ - 603 - ], - "slot_index": 0 - }, - { - "name": "mask", - "type": "MASK", - "links": null - } - ], - "properties": { - "Node name for S&R": "LayerUtility: RestoreCropBox" - }, - "widgets_values": [ - false - ], - "color": "rgba(38, 73, 116, 0.7)" - }, { "id": 252, "type": "PreviewImage", @@ -1060,6 +927,33 @@ ], "color": "rgba(38, 73, 116, 0.7)" }, + { + "id": 424, + "type": "PreviewImage", + "pos": [ + 1759.7010498046875, + 1737.902587890625 + ], + "size": [ + 607.7578735351562, + 616.5079345703125 + ], + "flags": {}, + "order": 21, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 603 + } + ], + "outputs": [], + "properties": { + "Node name for S&R": "PreviewImage" + }, + "widgets_values": [] + }, { "id": 427, "type": "LayerUtility: ICMaskCropBack", @@ -1091,7 +985,7 @@ "name": "IMAGE", "type": "IMAGE", "links": [ - 615 + 637 ], "slot_index": 0 } @@ -1103,31 +997,64 @@ "color": "rgba(38, 73, 116, 0.7)" }, { - "id": 424, - "type": "PreviewImage", + "id": 370, + "type": "LayerUtility: RestoreCropBox", "pos": [ - 1759.7010498046875, - 1737.902587890625 + 1285.8817138671875, + 1942.3494873046875 ], "size": [ - 607.7578735351562, - 616.5079345703125 + 365.7912902832031, + 118 ], "flags": {}, - "order": 22, + "order": 20, "mode": 0, "inputs": [ { - "name": "images", + "name": "background_image", "type": "IMAGE", - "link": 603 + "link": 610 + }, + { + "name": "croped_image", + "type": "IMAGE", + "link": 637 + }, + { + "name": "crop_box", + "type": "BOX", + "link": 460 + }, + { + "name": "croped_mask", + "type": "MASK", + "link": null, + "shape": 7 + } + ], + "outputs": [ + { + "name": "image", + "type": "IMAGE", + "links": [ + 603 + ], + "slot_index": 0 + }, + { + "name": "mask", + "type": "MASK", + "links": null } ], - "outputs": [], "properties": { - "Node name for S&R": "PreviewImage" + "Node name for S&R": "LayerUtility: RestoreCropBox" }, - "widgets_values": [] + "widgets_values": [ + false + ], + "color": "rgba(38, 73, 116, 0.7)" } ], "links": [ @@ -1323,30 +1250,6 @@ 0, "IMAGE" ], - [ - 615, - 427, - 0, - 432, - 1, - "IMAGE" - ], - [ - 616, - 364, - 0, - 432, - 0, - "*" - ], - [ - 617, - 432, - 0, - 370, - 1, - "IMAGE" - ], [ 621, 103, @@ -1402,6 +1305,14 @@ 102, 0, "MODEL" + ], + [ + 637, + 427, + 0, + 370, + 1, + "IMAGE" ] ], "groups": [ @@ -1461,10 +1372,10 @@ "config": {}, "extra": { "ds": { - "scale": 0.5644739300537781, + "scale": 0.6830134553650707, "offset": [ - 1389.037077059614, - -664.2146906381768 + 1070.8293436182964, + -836.2221698058116 ] }, "workspace_info": {