Crop by Margins (Mask)
Keep your mask on the same grid as your image
- mask
- MASK
What it's for
The moment you crop an image, every mask you'd already made for it is wrong - off by whatever you trimmed, and silently so. ComfyUI will happily feed a mask to an inpaint or composite node and produce a seam a few dozen pixels from where you meant it, and nothing in the graph will complain.
Crop by Margins (Mask) exists to stop that. It's the mask twin of the image node: same four margins, same snapping rule, one output of type MASK. Apply identical values on both and the two stay pixel-aligned.
How it works
Required inputs are mask and left_px, top_px, right_px, bottom_px (0–16384), plus snap_multiple (1–512). Output: MASK. That's the whole node.
The engine is shared with the image version - one geometry rule, two interfaces - so the crops match by construction, not by you re-typing numbers and hoping. snap_multiple rounds the remaining height and width down to a multiple, fixed at the top-left, taking the extra off the right and bottom. Same behaviour on both nodes, which is the point.
Masks here are ComfyUI's normal floating-point convention: white is the region you're acting on, black is everything else. Standard batched BHW masks are supported, and the node preserves dtype and device rather than re-quantising or moving the tensor. With snap_multiple at 1 it's a pure slice of the mask data.
When you actually need it
Two common shapes.
You cropped manually and the mask came along for the ride. You trimmed 120px of scanner bed off the bottom and a mask you made earlier still covers the full original frame.
Load Image ─> Crop by Margins (Image) [bottom_px 120] ─> inpaint / composite
│
└─> mask ─> Crop by Margins (Mask) [bottom_px 120] ─> same consumer
You used Intelligent AutoCrop. That node crops without touching masks - it only reports what it trimmed, through trim_left / trim_top / trim_right / trim_bottom. Feed those four integers into the mask node (and the image node, if you have a mask but not the cropped image) and the grids line up again. That's the documented pattern, and it's cleaner than rebuilding the mask from the cropped image, which costs you a segmenter pass.
Worth knowing: if you later need to pad a mask up to a target size, pad black. ComfyUI's mask semantics treat the added region as unprotected, and PortraitUtils' own preparation engine does exactly that - mask padding stays black even when you've set padding_fill to White for the image.
Where people get burned
The classic is using Intelligent AutoCrop's trim values on the mask but the cropped image's dimensions in a downstream node, or vice versa. Both nodes crop inward from the top-left; there's no centring and no auto-alignment between two wires.
The second trap is asking for more margin than the mask has. Like the image version, this raises a clear error instead of clamping, so a batch where one mask is smaller keeps going rather than corrupting a silent subset. And if you're feeding this from a segmenter that outputs a mask already in a different coordinate space (some paste-back nodes do), crop the source mask, not the pasted one.
Finally: this does not resize. A mask cropped to 1081 wide still needs a resize stage if your consumer wants multiples of 8 or 32 - do the snapping here and the resampling in Smart Photo Prepare or your own resize node, in that order.
Install
ComfyUI Manager → search PortraitUtils (publisher heyburns, GPLv3), or manually:
cd ComfyUI/custom_nodes
git clone https://github.com/heyburns/PortraitUtils
/path/to/ComfyUI/venv/bin/python -m pip install -r PortraitUtils/requirements.txt
The pack's dependencies (numpy, Pillow, opencv-python-headless, tifffile) go into ComfyUI's Python environment, and only one OpenCV wheel variant should be present. Restart the ComfyUI server - a browser refresh alone doesn't reload Python node definitions. This node needs no models.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| mask | MASK | — | |
| left_px | INT | 00–16384 | — |
| top_px | INT | 00–16384 | — |
| right_px | INT | 00–16384 | — |
| bottom_px | INT | 00–16384 | — |
| snap_multiple | INT | 11–512 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| MASK | MASK | — |