v0.30.5
This commit is contained in:
+11
-22
@@ -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
@@ -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
@@ -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"]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user