Resize To Canvas Size (COI)
The portrait-crop node that frames the subject, not the middle
- image
- mask
- IMAGE
- padding_mask
Center-cropping a portrait is a lottery. The subject is rarely dead centre, so you crop to the middle and get a canvas full of shoulder while the face stares at you from the edge. Resize To Canvas Size (COI) - the mask variant in the same pack - fixes that by making the placement target the actual subject. "COI" is Centre of Interest, and it's the whole game here.
It's the same resize engine as the base Resize To Canvas Size node: same five scale methods, same crop/stretch choice, same six padding fills, same padding_mask output. The difference is how the image gets positioned. Instead of a 3×3 anchor grid that says "keep this edge," this node takes an optional mask, computes a Centre of Interest from it, and then uses a 7×7 target grid to pick exactly where that COI lands on the canvas.
How the COI is computed
Three anchor_modes, straight from the source:
manual- the COI is just the image centre. If you connect no mask, or pick this mode, you're effectively using the base node with a finer grid.mask_bbox_centre- the centre of the bounding box of all non-zero mask pixels. Predictable, single-subject framing.mask_weighted_centre- a true weighted centroid (centre of mass). Big contiguous masked areas dominate the pull, and a few stray pixels are negligible. This is the mode for "frame everyone in the group shot."
If the mask size doesn't match the image, it's auto-resized (nearest-neighbour) to fit, so you can feed masks from any source.
The 7×7 grid maps to canvas fractions in steps of 1/6 - so the 49 dots cover every third-boundary, midpoint, and centre. Default 3,3 places the COI at canvas centre. Remember the semantics: the grid means "put the COI here."
The inputs that matter
mask(optional) - what drives everything. A face-detection mask viaUltralyticsDetectorProvider→BboxDetectorSEGS→SAMDetectorCombinedis the classic setup; any subject mask works.anchor_grid- a plain STRING like"3,3". The clickable widget writes it, but because it's a string you can also drive it from any node that outputscol,rowtext. Neat automation trick.anchor_mode- the three options above.width/height,scale_method,fill_method,padding_fill- same meaning as the base node.
Outputs: IMAGE (RGBA) and padding_mask (1 = padding, 0 = original pixels).
The recipe that made this node exist
Face-centred portrait crop: feed in a face-detection mask, set anchor_mode: mask_bbox_centre, grid 3,3, scale_method: Fit to Canvas width. The detected face lands dead centre on the canvas no matter where it sat in the source. With two subjects, switch to mask_weighted_centre and the node finds the centre of gravity across both regions - everyone gets framed instead of whoever was luckiest.
Where it trips people up
- No mask connected, or
manualmode, and the node quietly behaves like a centre-crop. If your framing looks wrong, check the mask first - a black or empty mask gives you image-centre placement with zero warning. mask_weighted_centreis biased by blob size. A big background region will yank the COI toward it. Single clean subject: usemask_bbox_centre. Multiple subjects to balance: weighted. Don't use weighted with a sloppy mask.- Garbage in, garbage out. The node is only as smart as the mask; a coarse detection mask produces coarse framing. This isn't a bug, it's the design.
Install is the same as the rest of the pack - ComfyUI Manager search "ComfyUI-ResizeToCanvasSize", or git clone into custom_nodes/ and restart. No extra Python packages, no model downloads. If you do a lot of portrait work at fixed canvas sizes, this is the node in the pack you'll actually reach for.
Inputs (12)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| width | INT | 5121–8192 | — |
| height | INT | 5121–8192 | — |
| anchor_grid | STRING | 3,3 | — |
| anchor_mode | COMBO | manual | 3 options: manual, mask_bbox_centre, mask_weighted_centre |
| scale_method | COMBO | Fit to Canvas short edge | 5 options: None – use original size, Fit to Canvas short edge, Fit to Canvas long edge, Fit to Canvas height, Fit to Canvas width |
| fill_method | COMBO | crop | 2 options: crop, stretch |
| padding_fill | COMBO | black | 6 options: black, white, gray_50, transparent, custom, noise |
| custom_color_hex | STRING | #000000 | — |
| noise_seed | INT | 00–18446744073709550000 | — |
| maskopt | MASK | — | |
| custom_color_hex_inputopt | STRING | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | — |
| padding_mask | MASK | — |