Nodes/ComfyUI-Majoor-ImageOps/〽️ Image Ops Corner Pin
ComfyUI Node

〽️ Image Ops Corner Pin

Pin a screen onto anything — this is ComfyUI's Nuke-style CornerPin

By MajoorWaldi·Created 8 months ago·Updated 8 days ago· 11
〽️ Image Ops Corner Pin
  • fill_color
  • image
  • image
  • mask
bypassfalse
tl_x0.000
tl_y0.000
tr_x1.000
tr_y0.000
bl_x0.000
bl_y1.000
br_x1.000
br_y1.000
filterbilinear
supersample1
fill_modetransparent
invert_maskfalse

If you've ever tried to paste a generated image onto a TV, a phone screen, or a billboard in a photo, you know the pain: free transform in some editor, eyeballing the perspective, doing it again after every render. ImageOps Corner Pin is the node that ends that. Drag the four corners of a quad directly on the preview, and it warps your image so it sits flat on whatever surface you pinned. It's the ComfyUI equivalent of the CornerPin2D node every compositor knows from Nuke, and it's one of the standout nodes in the ComfyUI-Majoor-ImageOps pack.

What it actually does

You feed it an image (or a batch of frames) and give it eight numbers - the destination X/Y of the top-left, top-right, bottom-left, and bottom-right corners, normalized so 0–1 spans the frame. Under the hood it solves a per-frame homography, runs the warp through PyTorch's grid_sample, and - the detail that separates this from a lazy paste job - premultiplies the alpha before warping and un-premultiplies after. That's why transparent edges stay clean instead of getting that dark fringed halo you see from naive warps. RGBA in means RGBA out, edges intact.

The real magic is the UI. You don't type the eight numbers. The node's live preview shows your image with four draggable corner handles - click and drag, no queueing, and the warp updates in real time. Handles snap to the frame edges (0 and 1) when you get close, which makes "exactly cover the screen" trivial; hold Alt while dragging to disable the snap and land anywhere, including slightly outside the frame.

The inputs that matter

  • tl_x / tl_y, tr_x / tr_y, bl_x / bl_y, br_x / br_y - the four destination corners in normalized coordinates. Defaults map the full frame (0,0 → 1,1). The allowed range goes to ±2 so you can push corners off-frame if the target surface is bigger than your source.
  • supersample (1–4) - renders the warp at 2–4x and downsamples. This is your anti-aliasing knob for perspective edges; if diagonal lines come out jaggier than you'd like, bump it.
  • fill_mode - what to do with the area outside your pinned quad. transparent (default) leaves it open so you can comp the result over anything; mirror, stretch, expand, and color (with fill_color) cover the edges instead.
  • filter - nearest, bilinear (default), or bicubic resampling. Bilinear is fine; bicubic for the final pass.
  • invert_mask flips the warped transparency mask, and bypass returns the input untouched for quick A/B.

Wiring it up

Outputs are image and mask. The mask is the warped alpha - the transparent quad in image form - so you can feed it into a merge or comp node to place the result. A classic flow: load a photo of a room, pin your generated "screen content" onto the TV, then use ImageOps Merge (with the mask) to composite it in. Because it's batch-first, a video clip pins onto a moving TV as a per-frame homography - genuinely useful for animation work.

Install and gotchas

Install via ComfyUI Manager (search "ComfyUI-Majoor-ImageOps") or:

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

Then restart ComfyUI and hard-refresh your browser (Ctrl+F5 / Cmd+Shift+R). No model downloads, no extra Python deps - it's pure torch. If the corner handles don't appear on the preview, the refresh is almost certainly the fix; the preview UI is frontend JavaScript and stale cache is the classic culprit.

One tip from real use: pin with the destination quad slightly larger than the surface and let fill_mode handle the overlap - a hair of overhang hides misalignment. And if your pinned content looks soft, it's the supersample, not your perspective. Crank it for the final render.

Categoryimage/imageops

Inputs (15)

NameTypeDefaultDescription
bypassBOOLEANfalse
tl_xFLOAT0.000-2–2
tl_yFLOAT0.000-2–2
tr_xFLOAT1.000-2–2
tr_yFLOAT0.000-2–2
bl_xFLOAT0.000-2–2
bl_yFLOAT1.000-2–2
br_xFLOAT1.000-2–2
br_yFLOAT1.000-2–2
filterCOMBObilinear3 options: nearest, bilinear, bicubic
supersampleINT1Render at 2x-4x and downsample to reduce perspective aliasing.
fill_modeCOMBOtransparentHow to fill uncovered areas outside the pinned quad.
fill_colorCOLOR#000000
invert_maskBOOLEANfalse
imageoptIMAGE,VIDEOImages/Video input. Accepts IMAGE batches and VIDEO frame sources.

Outputs (2)

NameTypeDescription
imageIMAGE
maskMASK