From e13bba12ab21fef229f087af3495d3753df9f5bb Mon Sep 17 00:00:00 2001 From: kk8bit Date: Thu, 20 Mar 2025 22:34:05 +0800 Subject: [PATCH] v0.30.5 --- nodes/image_composer.py | 33 +++++++++++---------------------- nodes/image_cropper.py | 30 ++++++++++++++---------------- pyproject.toml | 2 +- 3 files changed, 26 insertions(+), 39 deletions(-) diff --git a/nodes/image_composer.py b/nodes/image_composer.py index 5c18e17..074cb97 100644 --- a/nodes/image_composer.py +++ b/nodes/image_composer.py @@ -9,52 +9,47 @@ class ImageComposer: "required": { "image_a": ("IMAGE",), "mask_a": ("MASK",), - "image_b": ("IMAGE",), + "image_b": ("IMAGE",), "position": (["top", "bottom", "left", "right"], {"default": "top"}), }, "optional": { - "mask_b": ("MASK",), + "mask_b": ("MASK",), } } - RETURN_TYPES = ("IMAGE", "MASK", "DATA") - RETURN_NAMES = ("IMAGE", "MASK", "DATA") + RETURN_TYPES = ("DATA", "IMAGE", "MASK") + RETURN_NAMES = ("DATA", "IMAGE", "MASK") FUNCTION = "compose_images" CATEGORY = "KayTool/Image" def compose_images(self, image_a, mask_a=None, image_b=None, position=None, mask_b=None): - - image_a = image_a[0].cpu().numpy() + image_a = image_a[0].cpu().numpy() image_a_pil = Image.fromarray((image_a * 255).astype(np.uint8)) a_width, a_height = image_a_pil.size - image_b = image_b[0].cpu().numpy() image_b_pil = Image.fromarray((image_b * 255).astype(np.uint8)) b_width, b_height = image_b_pil.size - if mask_a is None: mask_a_np = np.zeros((a_height, a_width), dtype=np.uint8) else: mask_a_np = (mask_a.cpu().numpy() * 255).astype(np.uint8) - if mask_a_np.ndim == 3: + if mask_a_np.ndim == 3: mask_a_np = mask_a_np.squeeze() mask_a_pil = Image.fromarray(mask_a_np) - if position in ["top", "bottom"]: scale = a_width / b_width new_b_width = a_width new_b_height = int(b_height * scale) - else: + else: scale = a_height / b_height new_b_width = int(b_width * scale) new_b_height = a_height image_b_pil = image_b_pil.resize((new_b_width, new_b_height), Image.Resampling.LANCZOS) - if position in ["top", "bottom"]: new_width = a_width new_height = a_height + new_b_height @@ -62,7 +57,6 @@ class ImageComposer: new_width = a_width + new_b_width new_height = a_height - composed_image = Image.new("RGB", (new_width, new_height), (0, 0, 0)) if position == "top": composed_image.paste(image_a_pil, (0, 0)) @@ -79,22 +73,20 @@ class ImageComposer: composed_image.paste(image_b_pil, (a_width, 0)) a_pos = (0, 0, a_width, a_height) b_pos = (a_width, 0, new_width, new_b_height) - else: + else: composed_image.paste(image_b_pil, (0, 0)) composed_image.paste(image_a_pil, (new_b_width, 0)) a_pos = (new_b_width, 0, new_width, a_height) b_pos = (0, 0, new_b_width, new_b_height) - - mask_out = Image.new("L", (new_width, new_height), 0) + mask_out = Image.new("L", (new_width, new_height), 0) mask_out.paste(mask_a_pil, (a_pos[0], a_pos[1])) - if mask_b is None: mask_b_np = np.zeros((b_height, b_width), dtype=np.uint8) else: mask_b_np = (mask_b.cpu().numpy() * 255).astype(np.uint8) - if mask_b_np.ndim == 3: + if mask_b_np.ndim == 3: mask_b_np = mask_b_np.squeeze() mask_b_pil = Image.fromarray(mask_b_np).resize((new_b_width, new_b_height), Image.Resampling.LANCZOS) mask_out.paste(mask_b_pil, (b_pos[0], b_pos[1])) @@ -102,14 +94,11 @@ class ImageComposer: composed_image_tensor = torch.from_numpy(np.array(composed_image).astype(np.float32) / 255.0).unsqueeze(0) mask_out_tensor = torch.from_numpy(np.array(mask_out).astype(np.float32) / 255.0) - data = { "a_position": a_pos, "b_position": b_pos, "a_size": (a_width, a_height), "b_size": (new_b_width, new_b_height), - "composed_image": composed_image_tensor, - "mask_out": mask_out_tensor, } - return (composed_image_tensor, mask_out_tensor, data) \ No newline at end of file + return (data, composed_image_tensor, mask_out_tensor) \ No newline at end of file diff --git a/nodes/image_cropper.py b/nodes/image_cropper.py index ff5c656..3e7332e 100644 --- a/nodes/image_cropper.py +++ b/nodes/image_cropper.py @@ -7,8 +7,12 @@ class ImageCropper: def INPUT_TYPES(cls): return { "required": { - "image_composer_data": ("DATA",), - "target": (["a", "b"], {"default": "a"}), + "image_composer_data": ("DATA",), + "image": ("IMAGE",), + "target": (["a", "b"], {"default": "a"}), + }, + "optional": { + "mask": ("MASK",), } } @@ -17,32 +21,26 @@ class ImageCropper: FUNCTION = "crop_image" CATEGORY = "KayTool/Image" - def crop_image(self, image_composer_data, target): - - composed_image_tensor = image_composer_data.get("composed_image") - mask_out_tensor = image_composer_data.get("mask_out") - - - image = composed_image_tensor[0].cpu().numpy() + def crop_image(self, image_composer_data, image, target, mask=None): + image = image[0].cpu().numpy() image_pil = Image.fromarray((image * 255).astype(np.uint8)) + orig_height, orig_width = image_pil.size[1], image_pil.size[0] - if target == "a": x1, y1, x2, y2 = image_composer_data["a_position"] - else: # "b" + else: x1, y1, x2, y2 = image_composer_data["b_position"] - cropped_image = image_pil.crop((x1, y1, x2, y2)) - - mask_np = (mask_out_tensor.cpu().numpy() * 255).astype(np.uint8) - if mask_np.ndim == 3: + if mask is None: + mask = torch.zeros((orig_height, orig_width), dtype=torch.float32) + mask_np = (mask.cpu().numpy() * 255).astype(np.uint8) + if mask_np.ndim == 3: mask_np = mask_np.squeeze() mask_pil = Image.fromarray(mask_np) cropped_mask = mask_pil.crop((x1, y1, x2, y2)) - cropped_image_tensor = torch.from_numpy(np.array(cropped_image).astype(np.float32) / 255.0).unsqueeze(0) cropped_mask_tensor = torch.from_numpy(np.array(cropped_mask).astype(np.float32) / 255.0) diff --git a/pyproject.toml b/pyproject.toml index af99a92..98226af 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "kaytool" description ="KayTool boosts ComfyUI workflow efficiency with ultra-practical tools: BiRefNet/RemBG for background/mask tasks, wireless data transfer (Get & Out), AI translation (Tencent/Baidu), dynamic math operations, image processing tailored for the most popular workflows (size, color, blur, expand, merge, crop), flexible text handling, precision sliders, metadata and color profile-supported image saving, quick Run options via right-click (Run Group/Node), loading image folders to create batch processing workflows, and more." -version = "0.30.2" +version = "0.30.5" license = {file = "LICENSE"} dependencies = ["rembg, timm"]