Nodes/ComfyUI Stash/VR Face Unrectify
ComfyUI Node

VR Face Unrectify

Put the swapped VR faces back — distortion and all

By zyquon·Created about a year ago·Updated 2 months ago· 2
VR Face Unrectify
  • patches
  • rectify_map
  • image

VR Face Rectify gave you flat patches to swap. This node is what makes the result look like it was never flat. VR Face Unrectify takes the swapped patch batch from your face swapper plus the VR_RECTIFY_MAP that Rectify produced, projects each swapped patch back into its exact original frame, eye, and position, and composites it onto the untouched source frames.

It's the second half of the pair, and it has exactly one job: make the swap disappear back into the lens distortion instead of floating on top of it. If you've ever seen a face swap that looks "stuck on," that's what skipping this step looks like in VR.

How it works

Every patch Rectify cut has a corresponding entry in the map - which frame it came from, which eye (L/R/T/B/M), which projection, the yaw/pitch it was viewed from, and the patch geometry. Unrectify walks those entries in order and does the inverse projection (p2e, or fisheye_p2e for fisheye sources) to bend each swapped patch back into the shape that lens originally imposed, then alpha-composites it into place.

Two implementation details are worth knowing because they explain the behavior you'll see:

  • The reprojection is windowed - it only processes the eye sub-region the patch actually covers, so cost tracks the face footprint rather than the whole frame. Big 4K SBS batches stay reasonable.
  • If no faces were found at rectify time, the map is empty and the frames pass through byte-for-byte unchanged. The node degrades gracefully instead of crashing.

Like its partner, the geometry runs on torch (GPU) when available and falls back to numpy/cv2 otherwise; the node logs which backend it used.

Inputs and outputs

Both inputs are required, and both come from the same run of the pipeline:

  • patches - the swapped patch batch, straight from ReActor (or whatever swapper you're running).
  • rectify_map - the VR_RECTIFY_MAP from VR Face Rectify. Same wire, same run.

Output is a single image: the composited frames, same batch shape as the originals, ready to be saved or fed back into a video-encode path.

The gotcha: batch order is sacred

Unrectify matches patches[i] to map entry i, so the swapper must preserve the batch order and count it received. If it doesn't - if a swapper drops frames, re-sorts them, or is set to process one image per run - you'll get a hard error:

VR Face Unrectify: N patches expected from the map but only M arrived; the swapper must preserve batch order/count

That error is the friendly version. The unfriendly version is subtle: if the swapper reorders instead of dropping, you get a face composited into the wrong spot with no complaint at all. Practical rules:

  • Run the whole batch through Rectify → swapper → Unrectify in one shot; don't split or shuffle in between.
  • Use a swapper with batch support (ReActor handles batches) and keep its batch settings aligned with what Rectify emitted.
  • Both the map and the patches come from the same Rectify run - rewire the pair together if you change any rectify settings, or the geometry won't match the patches.

Installing and troubleshooting

cd ComfyUI/custom_nodes && git clone https://github.com/zyquon/ComfyUI-Stash

or search "Stash" in ComfyUI Manager and restart. No extra model downloads - the pack's deps are just pillow/numpy/requests, and the face detection side already ran upstream in Rectify. If you're hitting the count mismatch error, the fix is almost always on the swapper side: batch order in, batch order out. And if everything "works" but the faces look wrong, check that input_layout and projection on the Rectify node match the actual source footage - the unrectify can only faithfully undo what rectify actually did.

CategoryStash/VR

Inputs (2)

NameTypeDefaultDescription
patchesIMAGESwapped patch batch from ReActor
rectify_mapVR_RECTIFY_MAPFrom VR Face Rectify

Outputs (1)

NameTypeDescription
imageIMAGE