Nodes/ComfyUI-Majoor-ImageOps/〽️ Image Ops Transform
ComfyUI Node

〽️ Image Ops Transform

Pan, rotate, scale, flip — your whole frame batch in one GPU pass

By MajoorWaldi·Created 8 months ago·Updated 8 days ago· 11
〽️ Image Ops Transform
  • fill_color
  • image
  • mask
  • image
  • mask
bypassfalse
translate_x0
translate_y0
rotate_deg0.0
scale1.00
flipnone
filter
expandfalse
fill_modetransparent
invert_maskfalse

You'd think "move and rotate an image" would be covered by core ComfyUI. It sort of is, in the most awkward way possible - geometry nodes that are fiddly to wire, resize nodes that can't rotate, nothing that treats a batch of video frames as one thing. ImageOps Transform is the version you actually want: translate, rotate, scale, and flip in a single GPU pass, applied to the whole frame batch at once, with a mask so you can move a subject without touching the background.

It's the geometric workhorse of the MajoorWaldi ComfyUI-Majoor-ImageOps pack, the one you reach for when you need a punch-in, a camera pan on generated footage, a mirror flip, or just to deskew a tilted scan before it feeds the sampler. The batch-first design is the point - the same pack philosophy ("stop treating frames like mysterious ghosts in the graph") means one node call warps every frame identically, which is exactly what you want from a camera move.

How it works

The implementation is refreshingly clean: it builds a batch of inverse affine matrices and does a single affine_grid + grid_sample pass over the whole tensor. No intermediate canvases, no image-sized temporary buffers - translate, rotate, and scale all resolve in one sampling operation, so quality stays high even when you're doing a heavy punch-in (scale 2.0 + a pan at the same time only ever resamples once).

The mask handling is where a compositor's instincts show up in the code. If you supply a mask, the node premultiplies the RGB by the mask before warping and unpremultiplies after. That sounds like implementation trivia, but it's the difference between a moving subject with a smeared colored halo at its edges and a clean one. Cheap implementations skip it; this one doesn't.

Inputs that matter

  • translate_x / translate_y - pixel offsets, ±4096. Positive x moves right, positive y moves down (screen space).
  • rotate_deg - degrees, clockwise positive, ±180.
  • scale - 0.01 to 8.0; 1.0 is identity. Values above 1 magnify (crop in), below 1 shrink (reveal edges).
  • flip - none / horizontal / vertical. Cheaper than encoding a flip as a 180° rotation of the canvas.
  • fill_mode - what happens to the holes a rotate or pan leaves behind: transparent (the default - you get an RGBA image with clear corners), mirror, stretch, expand (edge border), or color (solid fill_color).
  • filter - nearest / bilinear / bicubic resampling. Bicubic for final-quality work, nearest if you're downscaling pixel art.
  • expand - here's the trap: it's a compatibility option that does nothing. The README and the code agree - the GPU affine path always keeps the output size fixed. Don't tick it expecting the canvas to grow; it won't.

Outputs

image and mask, both warped consistently. The mask tracks the transform, so a rotating subject's alpha stays glued to it - which is how you composite the transformed result back over an untouched background.

Installing it

cd ComfyUI/custom_nodes
git clone https://github.com/MajoorWaldi/ComfyUI-Majoor-ImageOps

Or search "ComfyUI-Majoor-ImageOps" in ComfyUI Manager, restart, and hard-refresh the browser (Ctrl+F5 / Cmd+Shift+R on macOS). No extra pip packages, no model downloads - just torch's affine_grid/grid_sample, which every ComfyUI already has. ComfyUI ≥ 0.13.0.

Where people get burned

Two recurring ones. First: rotate 45° with the default transparent fill and you get an RGBA image with see-through corners - and if your downstream node expects RGB, that alpha channel travels with it. Set fill_mode to mirror or color unless you actually want the transparency. Second: because the output size never grows (no expand), a 45° rotation crops the corners rather than letterboxing them - the corners are genuinely gone, so plan the composition or pad first with ImageOps PadOut.

Categoryimage/imageops

Inputs (13)

NameTypeDefaultDescription
bypassBOOLEANfalse
translate_xINT0-4096–4096
translate_yINT0-4096–4096
rotate_degFLOAT0.0-180–180
scaleFLOAT1.000.01–8
flipCOMBOnone4 options: none, horizontal, vertical, both
filterCOMBO3 options: nearest, bilinear, bicubic
expandBOOLEANfalseReserved. Currently inactive — the GPU affine path uses a fixed-size canvas. Kept for workflow compatibility.
fill_modeCOMBOtransparentHow to fill uncovered areas when scale, rotate, or translate leaves holes.
fill_colorCOLOR#000000
invert_maskBOOLEANfalse
imageoptIMAGE,VIDEOImages/Video input. Accepts IMAGE batches and VIDEO frame sources.
maskoptMASK

Outputs (2)

NameTypeDescription
imageIMAGE
maskMASK