Image Crop by Mask
Crop to exactly what a mask marks — and get told how to paste it back
- image
- mask
- cropped_image
- crop_data
- bounds
- cropped_mask
The automated-fix loop that runs ComfyUI - detect a broken region, crop it, re-render it, paste it back - lives or dies on one detail: the crop and the paste have to agree about where the rectangle was. Image Crop by Mask is the node that makes that agreement explicit. It crops each image to the tightest box around whatever its mask marks, and hands you not just the crop but the window it was cut from, so a downstream Image Paste Crop can drop the fixed result back into the exact same rectangle.
It's from WAS Node Suite, WASasquatch's MIT pack (457 nodes in v3). If you've built an inpaint-and-paste region fix by hand - cropping with a math chain, praying the coordinates line up - this is the version where the bookkeeping is built in.
The inputs that matter
mask- decides where the crop sits. The box is measured on the mask, so the mask needs to be the same size as its image or the crop lands in the wrong place. Everything abovethreshold(default 0.5) counts as marked; 0.5 boxes the mask's solid core, 0.0 pulls in its whole feathered edge.padding- extra pixels kept on all four sides, trimmed where the box would run off the picture. This is the one to raise for inpainting: 0 crops tight against the mask, but 32–64px of surroundings gives the re-render pass context to match against and hides the seam. Inpainting KB lore says exactly this - the padding is what makes "only masked" look seamless.bbox_mode- how a batch of masks is treated.unionuses one rectangle covering every mask;per_framegives each image its own box, which is what follows a subject that moves across a sequence.divisible_by- rounds both crop sides down to a multiple of this (8 suits most samplers) so the sampler doesn't silently round for you. The crop window is measured before this rounding, so Image Paste Crop still lands on the rectangle actually cut.
What comes out
Four outputs, and each one exists because someone needed it:
cropped_image- one crop per input image, all the same size so they travel as a single batch. That uniform size is the trick that lets one crop window describe a whole batch.crop_data- the window, for Image Paste Crop to put the finished region back at its original position and size.bounds- the same rectangle as rows of top/bottom/left/right, for wiring into the pack's Draw Image Bounds node to visualize what got cut.cropped_mask- the mask cut to the crop's rectangle at the crop's size, so an inpainting pass knows exactly which pixels were marked.
The workflow it powers
Wire the mask (however you got it - an auto-detector, a SAM pass, a hand-paint), crop, run the region through a re-render at its native resolution, then paste via crop_data. The padding is where quality lives: too tight and the fix has nothing to blend against; too loose and you're re-rendering half the frame. Threshold trips people up too - crank it to 1.0 and no pixel counts as marked, so every frame gets cropped whole (the node names the offender in the console). A genuinely blank mask does the same.
Install and gotchas
WAS Node Suite installs via ComfyUI Manager ("WAS Node Suite v3") or git clone https://github.com/WASasquatch/was-node-suite-comfyui into ComfyUI/custom_nodes, then restart. Needs ComfyUI 0.14.0+ and Python 3.10+; v3 installs no extra Python packages, unlike the old v2 whose pinned dependencies caused famous import failures.
The two real traps: a mask whose size doesn't match its image (everything mis-crops silently), and per_frame mode's paste behavior - the paste window there is the union rectangle, so pasting stretches every crop across the whole shared box and lands exactly only where every mask shared a box. If you're per-frame cropping a moving subject and pasting each back, check whether you actually want the union window or want to paste crop-by-crop.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | The images to cut down. The box is measured on the mask and then sliced straight out of the image, so a mask that is not the same size as its image crops in the wrong place. A batch comes back the same length. | |
| mask | MASK | Marks the area to keep. The tightest box around everything above threshold decides where the crop sits. A blank mask crops that frame whole. | |
| bbox_mode | COMBO | Which box a batch of masks is cropped to. 'union' uses one rectangle covering every mask; 'per_frame' gives each image its own, following a subject that moves. | |
| padding | INT | 00–16384 | Extra pixels kept on all four sides of the marked area, trimmed where the box would run off the picture. 0 crops tight against the mask; 64 leaves an inpainting pass some of the surroundings to match against. |
| threshold | FLOAT | 0.500–1 | How bright a mask pixel must be, from 0.0 to 1.0, to count as marked. 0.5 boxes a mask's solid core; 0.0 takes its whole feathered edge in. |
| divisible_by | INT | 81–64 | Rounds both sides of the crop down to a multiple of this and resizes to match, which saves a sampler rounding it. 1 leaves the crop as cut. |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| cropped_image | IMAGE | The cropped regions, one per input image and all one size, so they travel on as a single batch. That size is a multiple of divisible_by. |
| crop_data | CROP_DATA | The crop window, for Image Paste Crop to put the finished region back in the right place at its original size. In per_frame mode it holds the union rectangle, so a paste stretches every crop across that whole rectangle and lands exactly only where every mask shared a box. |
| bounds | IMAGE_BOUNDS | One row per image giving the rectangle it was cropped from, as top, bottom, left and right pixel positions, to wire into Draw Image Bounds. |
| cropped_mask | MASK | The mask cut to the same rectangle as the crop and carried at the crop's own size, so an inpainting pass knows which pixels were marked. |