Nodes/NH-Nodes/NH Restore Image Geometry
ComfyUI Node

NH Restore Image Geometry

Get back the edges your model cropped away

By jetthuangai·Created 6 months ago·Updated about 12 hours ago· 7
NH Restore Image Geometry
  • image
  • original_image
  • resize_data
  • image
  • mask
◄resize_methodbicubic►

Here's a thing that quietly ruins edits: to send a photo through a model you resize it to a size the model likes, and the usual way to do that without squashing the picture is to crop it. 3:2 becomes 1:1, the sides go away, you edit or upscale the square, and now you want the original photo back. Paste the result into the middle and you get two dead-straight seams - worse if you upscaled, because the new content is sharper than the frame around it.

NH Restore Image Geometry closes that loop properly. It takes the processed image, the original, and the resize_data that the resize recorded, and puts the processed content back into the original frame at the right place and scale, keeping the original pixels outside the crop.

What it's for, in a real workflow

It only earns its slot if you resize with one of the pack's own nodes, because resize_data is the trick. The canonical chain, straight from the docs:

Load Image → NH Smart Ratio Image Resize (cover_crop) → … your processing … → NH Restore Image Geometry → Save Image

The wiring is the part people get wrong:

| Connect | To | |---|---| | Load Image IMAGE | Smart Ratio Image Resize image and Restore Image Geometry original_image | | resize node image | your processing nodes | | the processed IMAGE | Restore Image Geometry image | | resize node resize_data | Restore Image Geometry resize_data |

original_image comes from the loader, not from downstream - it's the untouched frame you're restoring into.

Where I'd actually reach for it: face and skin cleanups on portrait-orientation photos, upscaling a region-sized crop, any img2img pass where the model wants a fixed canvas but the client wants the framing they sent. The usual upscaling answer for big images is tiled upscalers that never leave the original geometry; this is the other case, where you deliberately changed geometry to keep the model comfortable and now need to undo it.

How it works

resize_data is a version-1 record of what the resize did: source size, target size, resized size, crop offsets, padding offsets and mode. From those numbers the node builds an inverse map.

Mechanically it's a warp, not a paste. It samples the content region (trimming padding bars for contain_pad), builds a sampling grid in the original image's coordinate space and pulls pixels through it with a bilinear-family sampler. The processed content lands on the original's pixel grid at the original's resolution, which is why it doesn't look like a sticker slapped on a photo.

Alongside the image it returns mask: white where restored content sits, black where the original is retained. Wire that into an inpaint, a colour match or a second pass and you're editing only what changed.

resize_method is the filter - bicubic (default), bilinear, nearest. Note the short list, versus the eight filters the resize nodes themselves offer. bicubic is right for photographic content; nearest is for pixel art and masks where you want no smoothing.

All three resize modes work. cover_crop is the main event; contain_pad and stretch are supported; and for the plain "give me my original size back" job on padded input, the pack's NH Remove Resize Padding does it from the same resize_data.

Installing it

ComfyUI Manager, search NH-Nodes, install, restart. Or:

cd ComfyUI/custom_nodes
git clone https://github.com/jetthuangai/NH-Nodes.git
cd NH-Nodes
python -m pip install -r requirements.txt

That's it - no models, no downloads. It's tensor math on numpy/Pillow/scipy/torch, so it loads instantly and costs no VRAM. The pack targets a recent ComfyUI (tested on 0.41) and Python 3.10+.

Common issues

The processing must preserve the framing. This is the trap, and the node says so plainly. resize_data describes one resize and nothing else. Rotate, outpaint, crop further, or let a model hallucinate a wider frame, and there's no information in resize_data to undo it - the restore will be wrong in a way that looks like a scale bug. Same-frame upscales, relights, deblurs and img2img passes are fine. Geometry changes are not.

Size mismatches error rather than guess. original_image must match the source size in resize_data, and both images need the same channel count - you get a clear ValueError, not a squashed output. Batches may differ only if one holds a single image, which is deliberate: restore a whole batch against one original, or one result across a batch.

Old resize_data won't load. It checks version == 1 and names the keys it needs; hand-built or pre-1.0 data gets refused. Re-run the resize node.

Nothing renders on the canvas. It's a pass-through node with no preview. Put a Save Image at the end - you can't see a seam problem until you look at the composite at 100%.

And check the mask before you trust the composite. If it isn't where you expected the edited region, you wired the wrong image into original_image - a much faster diagnosis than squinting at a seam.

CategoryNH-Nodes/Resolution

Inputs (4)

NameTypeDefaultDescription
imageIMAGE—
original_imageIMAGE—
resize_dataNH_RESIZE_DATA—
resize_methodCOMBObicubic3 options: bicubic, bilinear, nearest

Outputs (2)

NameTypeDescription
imageIMAGE—
maskMASK—