Restore Rotated Image
The boring node that makes rotating-your-image-then-fixing-it actually work
- image
- rotation_info
- mask
- image
- mask
Rotating an image by an arbitrary angle is easy - ComfyUI does it, your phone does it. The hard part is the rest of the job: you rotate a tilted photo so you can inpaint or upscale the subject at the right orientation, and then you have to get it back into the same canvas, same dimensions, same spot, without it looking like you glued it back together wrong. That's what Restore Rotated Image is for. It's the second half of DiamondGo's tiny comfyui-rotate-image pair, and honestly it's the half that makes the whole idea worth having.
What it actually does
The trick here is the same one that made crop-and-stitch inpainting a standard: don't transform the whole workflow, transform the image and undo it. You pipe your image through Rotate Image, which spins it by any angle (positive = counter-clockwise, PIL convention - negative for clockwise), expands the canvas so nothing clips, and fills the new corners with black. You run the rotated image through whatever you like - KSampler, inpainting, an upscaler. Then Restore Rotated Image rotates it back by the exact same angle and crops it to the original dimensions.
The mechanism lives in a small dict called rotation_info, which is a custom ComfyUI type that only ever connects between the two nodes in this pack. Rotate Image emits it alongside the rotated image; Restore Rotated Image reads back the angle, the original width and height, and the interpolation method, rotates by the negated angle, and center-crops. Because the original dimensions are stored in that dict, it even survives size-changing steps in the middle - upscale to 4x between rotate and restore and you still land back at the source resolution. Under the hood everything is PIL, looping over each frame in the batch, so batched images work too.
Inputs and outputs that matter
image(required, IMAGE) - your processed, still-rotated image, straight from whatever you ran in between.rotation_info(required, ROTATION_INFO) - the metadata wire fromRotate Image. This is the whole point: it's what makes the undo exact. Connect it directly, don't try to reconstruct it.mask(optional, MASK) - if you rotated a mask along with the image, feed the processed mask back in here so it gets un-rotated to match. Skip it and you still get an outputmask, just a black one.
Outputs are image and mask, both cropped back to the original dimensions. Wire the restored image straight to Save Image (or ImageCompositeMasked if you're blending it with an untouched original - same discipline the inpainting docs push).
Installing it
Via ComfyUI Manager: search "Rotate Image" and install. Manual route:
cd ComfyUI/custom_nodes
git clone https://github.com/DiamondGo/comfyui-rotate-image
Then restart ComfyUI. That's the whole install - no models to download, and the requirements.txt is just torch, numpy, and Pillow, all of which ComfyUI already ships. The pack is MIT-licensed and tiny; the author's README even has a placeholder clone URL, so use the real repo above.
Where people get burned
- Forgetting
rotation_info. It's required for a reason; the node can't reverse something it doesn't have data for. If you break that wire, you get nothing useful. - The angle convention. Positive = counter-clockwise. If you cranked
angleto +30 expecting a rightward tilt, the whole thing mirrors wrong and the restore will faithfully undo the wrong turn. - LANCZOS isn't LANCZOS. The dropdown offers it, but PIL's
rotate()doesn't support true LANCZOS, so the node silently uses BICUBIC - the best filterrotate()accepts. BICUBIC is the default and it's fine for photos; use NEAREST for pixel art and masks so you don't soften hard edges. - Black corners. They're part of the design - that's what the expanded canvas fills with. If they end up in your inpainted region, that's expected; if they show up in the final image, your middle pipeline didn't cover them.
It's a niche tool, but when you need to fix a tilted subject at native resolution, this pair saves you from the alternative - eyeballing a manual counter-rotation and praying it lines up. For a 0-star, no-frills pack, the restore node is quietly the useful one.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| rotation_info | ROTATION_INFO | — | |
| maskopt | MASK | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| mask | MASK | — |