add crop node

This commit is contained in:
thalismind
2025-07-20 22:11:43 -05:00
parent 7ee5ed1f72
commit 33d2d2a230
2 changed files with 81 additions and 1 deletions
+3 -1
View File
@@ -1,13 +1,15 @@
from .nodes import LoadImageWithFilename, LoadImageFolder, SaveImageWithFilename
from .nodes import LoadImageWithFilename, LoadImageFolder, SaveImageWithFilename, CropImageByMask
NODE_CLASS_MAPPINGS = {
"LoadImageWithFilename": LoadImageWithFilename,
"LoadImageFolder": LoadImageFolder,
"SaveImageWithFilename": SaveImageWithFilename,
"CropImageByMask": CropImageByMask,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LoadImageWithFilename": "Load Image With Filename",
"LoadImageFolder": "Load Image Folder",
"SaveImageWithFilename": "Save Image With Filename",
"CropImageByMask": "Crop Image By Mask",
}
+78
View File
@@ -350,3 +350,81 @@ class SaveImageWithFilename:
@classmethod
def IS_CHANGED(s, images, filenames, filename_prefix, **kwargs):
return hashlib.sha256(str(images).encode()).hexdigest()
class CropImageByMask:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"image": ("IMAGE", {"tooltip": "The image to crop"}),
"mask": ("MASK", {"tooltip": "The mask to use for cropping. White pixels (1.0) will be excluded, black pixels (0.0) will be included."})
}
}
CATEGORY = "image"
RETURN_TYPES = ("IMAGE", "MASK")
RETURN_NAMES = ("image", "mask")
FUNCTION = "crop_by_mask"
DESCRIPTION = "Crops an image based on its mask, keeping only areas with black pixels (0.0) in the mask and excluding white pixels (1.0)."
def crop_by_mask(self, image, mask):
# Ensure mask is binary (0.0 or 1.0)
if mask.dim() == 2:
# Single mask
binary_mask = (mask > 0.5).float()
return self._crop_single_image(image, binary_mask)
else:
# Batch of masks
cropped_images = []
cropped_masks = []
for i in range(mask.shape[0]):
single_mask = mask[i]
single_image = image[i] if image.shape[0] > 1 else image[0]
binary_mask = (single_mask > 0.5).float()
cropped_img, cropped_mask = self._crop_single_image(single_image, binary_mask)
cropped_images.append(cropped_img)
cropped_masks.append(cropped_mask)
# Stack results
if len(cropped_images) > 1:
return (torch.stack(cropped_images, dim=0), torch.stack(cropped_masks, dim=0))
else:
return (cropped_images[0].unsqueeze(0), cropped_masks[0].unsqueeze(0))
def _crop_single_image(self, image, mask):
# Convert to numpy for easier manipulation
img_np = image.cpu().numpy()
mask_np = mask.cpu().numpy()
# Find the bounding box of black pixels (0.0) in the mask
# We want to exclude white pixels (1.0), so we look for where mask == 0
black_pixels = (mask_np == 0)
if not np.any(black_pixels):
# If no black pixels, return the original image
return image, mask
# Find the bounding box
rows = np.any(black_pixels, axis=1)
cols = np.any(black_pixels, axis=0)
y_min, y_max = np.where(rows)[0][[0, -1]]
x_min, x_max = np.where(cols)[0][[0, -1]]
# Crop the image and mask
cropped_img = img_np[y_min:y_max+1, x_min:x_max+1]
cropped_mask = mask_np[y_min:y_max+1, x_min:x_max+1]
# Convert back to torch tensors
cropped_img_tensor = torch.from_numpy(cropped_img).float()
cropped_mask_tensor = torch.from_numpy(cropped_mask).float()
return cropped_img_tensor, cropped_mask_tensor
@classmethod
def IS_CHANGED(s, image, mask, **kwargs):
return hashlib.sha256(str(image).encode() + str(mask).encode()).hexdigest()