This commit is contained in:
kk8bit
2025-03-20 22:34:05 +08:00
parent 42a33a32b0
commit e13bba12ab
3 changed files with 26 additions and 39 deletions
+11 -22
View File
@@ -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)
return (data, composed_image_tensor, mask_out_tensor)
+14 -16
View File
@@ -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)
+1 -1
View File
@@ -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"]