Bounded Image Crop With Mask (Swwan)
Find the mask, crop around it, keep the box
- image
- mask
- IMAGE
- IMAGE_BOUNDS
You've got a mask that highlights the subject - a face, a person, an object - and you want to crop the image tightly around it. You could find the bounding box by hand, but that's exactly the kind of math that should be a node. BoundedImageCropWithMask computes the box from the mask, adds padding around it, crops the image, and hands you back both the crop and the box it used.
The key difference from the plain BoundedImageCrop in the same pack: this one does the detection for you. It scans the mask to find where the non-zero pixels are, expands that region by your padding values, and crops. The IMAGE_BOUNDS it outputs is the reusable bit - you can store it, inspect it, or feed it back into a restore/uncrop node later to put the crop back where it came from.
Inputs and outputs
Required: image (IMAGE), mask (MASK), and four padding values - padding_left/right/top/bottom, each defaulting to 64 pixels. The padding is what keeps the crop from hugging the mask edge: for a face close to the image border you might drop padding to 0 or 20; for a subject you want context around, bump it up.
Optional: return_list (default false) - if you're cropping a batch and want the results as a Python list of images rather than a stacked tensor, flip this.
Outputs: the cropped IMAGE, and the computed IMAGE_BOUNDS.
The batch rules mirror the plain crop: if your image count and mask count match, each image is cropped by its own mask; if they don't, the first mask is applied to every image. That's convenient for cropping every frame of a video with one detected region, and a footgun if you forget you did it.
How it works
Mechanically it's simple: torch.any across the mask's rows and columns finds the top/bottom and left/right extents of non-zero pixels, then padding is applied and clamped to the image edges. No blurring, no feathering - the box is a hard rectangle (for soft mask edges you'd add the pack's CropByMask V4 or feather in restore). Because it's tensor-level, it's fast enough to run per-frame on video.
Installing it
It's part of ComfyUI_Swwan:
cd ComfyUI/custom_nodes
git clone https://github.com/aining2022/ComfyUI_Swwan
pip install -r ComfyUI_Swwan/requirements.txt
Or via ComfyUI Manager, search "ComfyUI_Swwan".
Where people get burned
The ordering of the bounds tuple matters - (rmin, rmax, cmin, cmax), row-then-column - so if you're hand-building bounds to feed another node, row values come first. And a fully-empty mask (all zeros) has no pixels to find, so torch.where returns nothing and the node throws. Guard against empty masks before you hit a long batch, and remember the first-mask-for-all fallback when your counts mismatch.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| mask | MASK | — | |
| padding_left | INT | 640–18446744073709550000 | — |
| padding_right | INT | 640–18446744073709550000 | — |
| padding_top | INT | 640–18446744073709550000 | — |
| padding_bottom | INT | 640–18446744073709550000 | — |
| return_listopt | BOOLEAN | false | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | — |
| IMAGE_BOUNDS | IMAGE_BOUNDS | — |