LayerUtility: CropByMask¶
Documentation¶
- Class name:
LayerUtility: CropByMask
- Category:
😺dzNodes/LayerUtility
- Output node:
False
This node is designed to crop images based on a specified mask, adjusting the crop area dynamically to fit the mask's shape. It provides functionality to visually preview the crop area on the original image, allowing for precise adjustments based on the mask's boundaries.
Input types¶
Required¶
image
- The image to be cropped, serving as the primary input for the cropping operation.
- Comfy dtype:
IMAGE
- Python dtype:
torch.Tensor
mask_for_crop
- The mask based on which the image will be cropped. It determines the area of the image that will be retained after cropping.
- Comfy dtype:
MASK
- Python dtype:
torch.Tensor
invert_mask
- A boolean flag to invert the mask before cropping, allowing for flexibility in selecting the area to be cropped.
- Comfy dtype:
BOOLEAN
- Python dtype:
bool
detect
- The method used to determine the cropping area based on the mask. Options include 'min_bounding_rect', 'max_inscribed_rect', and 'mask_area'.
- Comfy dtype:
COMBO[STRING]
- Python dtype:
str
top_reserve
- The amount of padding to add to the top edge of the crop area, allowing for adjustments beyond the mask's boundaries.
- Comfy dtype:
INT
- Python dtype:
int
bottom_reserve
- The amount of padding to add to the bottom edge of the crop area, enabling adjustments beyond the mask's boundaries.
- Comfy dtype:
INT
- Python dtype:
int
left_reserve
- The amount of padding to add to the left edge of the crop area, facilitating adjustments beyond the mask's boundaries.
- Comfy dtype:
INT
- Python dtype:
int
right_reserve
- The amount of padding to add to the right edge of the crop area, allowing for adjustments beyond the mask's boundaries.
- Comfy dtype:
INT
- Python dtype:
int
Optional¶
Output types¶
croped_image
- Comfy dtype:
IMAGE
- unknown
- Python dtype:
unknown
- Comfy dtype:
croped_mask
- Comfy dtype:
MASK
- unknown
- Python dtype:
unknown
- Comfy dtype:
crop_box
- Comfy dtype:
BOX
- The coordinates of the crop box used in the cropping operation, providing details on the area selected for cropping.
- Python dtype:
list
- Comfy dtype:
box_preview
- Comfy dtype:
IMAGE
- A preview image showing the crop box overlaid on the original image, aiding in visualizing the crop area.
- Python dtype:
torch.Tensor
- Comfy dtype:
Usage tips¶
- Infra type:
CPU
- Common nodes: unknown
Source code¶
class CropByMask:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(self):
detect_mode = ['min_bounding_rect', 'max_inscribed_rect', 'mask_area']
return {
"required": {
"image": ("IMAGE", ), #
"mask_for_crop": ("MASK",),
"invert_mask": ("BOOLEAN", {"default": False}), # 反转mask#
"detect": (detect_mode,),
"top_reserve": ("INT", {"default": 20, "min": -9999, "max": 9999, "step": 1}),
"bottom_reserve": ("INT", {"default": 20, "min": -9999, "max": 9999, "step": 1}),
"left_reserve": ("INT", {"default": 20, "min": -9999, "max": 9999, "step": 1}),
"right_reserve": ("INT", {"default": 20, "min": -9999, "max": 9999, "step": 1}),
},
"optional": {
}
}
RETURN_TYPES = ("IMAGE", "MASK", "BOX", "IMAGE",)
RETURN_NAMES = ("croped_image", "croped_mask", "crop_box", "box_preview")
FUNCTION = 'crop_by_mask'
CATEGORY = '😺dzNodes/LayerUtility'
def crop_by_mask(self, image, mask_for_crop, invert_mask, detect,
top_reserve, bottom_reserve, left_reserve, right_reserve
):
ret_images = []
ret_masks = []
l_images = []
l_masks = []
for l in image:
l_images.append(torch.unsqueeze(l, 0))
if mask_for_crop.dim() == 2:
mask_for_crop = torch.unsqueeze(mask_for_crop, 0)
# 如果有多张mask输入,使用第一张
if mask_for_crop.shape[0] > 1:
log(f"Warning: Multiple mask inputs, using the first.", message_type='warning')
mask_for_crop = torch.unsqueeze(mask_for_crop[0], 0)
if invert_mask:
mask_for_crop = 1 - mask_for_crop
l_masks.append(tensor2pil(torch.unsqueeze(mask_for_crop, 0)).convert('L'))
_mask = mask2image(mask_for_crop)
bluredmask = gaussian_blur(_mask, 20).convert('L')
x = 0
y = 0
width = 0
height = 0
if detect == "min_bounding_rect":
(x, y, width, height) = min_bounding_rect(bluredmask)
elif detect == "max_inscribed_rect":
(x, y, width, height) = max_inscribed_rect(bluredmask)
else:
(x, y, width, height) = mask_area(_mask)
width = num_round_to_multiple(width, 8)
height = num_round_to_multiple(height, 8)
log(f"{NODE_NAME}: Box detected. x={x},y={y},width={width},height={height}")
canvas_width, canvas_height = tensor2pil(torch.unsqueeze(image[0], 0)).convert('RGB').size
x1 = x - left_reserve if x - left_reserve > 0 else 0
y1 = y - top_reserve if y - top_reserve > 0 else 0
x2 = x + width + right_reserve if x + width + right_reserve < canvas_width else canvas_width
y2 = y + height + bottom_reserve if y + height + bottom_reserve < canvas_height else canvas_height
preview_image = tensor2pil(mask_for_crop).convert('RGB')
preview_image = draw_rect(preview_image, x, y, width, height, line_color="#F00000", line_width=(width+height)//100)
preview_image = draw_rect(preview_image, x1, y1, x2 - x1, y2 - y1,
line_color="#00F000", line_width=(width+height)//200)
crop_box = (x1, y1, x2, y2)
for i in range(len(l_images)):
_canvas = tensor2pil(l_images[i]).convert('RGB')
_mask = l_masks[0]
ret_images.append(pil2tensor(_canvas.crop(crop_box)))
ret_masks.append(image2mask(_mask.crop(crop_box)))
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).", message_type='finish')
return (torch.cat(ret_images, dim=0), torch.cat(ret_masks, dim=0), list(crop_box), pil2tensor(preview_image),)