Nodes/ComfyUI Stash/VR Face Rectify
ComfyUI Node

VR Face Rectify

The node that makes face swapping on VR180 footage actually work

By zyquon·Created about a year ago·Updated 2 months ago· 2
VR Face Rectify
  • image
  • options
  • patches
  • rectify_map
  • count
input_layout
projection
patch_fov80
patch_size768
fisheye_fov135
input_faces_orderlarge-small
input_faces_index0
detect_gender_inputno

Face swappers like ReActor are trained on flat, camera-facing faces. VR180 faces are neither. An equirect or fisheye lens bends a face into a curve, and if you feed that to a swapper you get a smear - the swap technically happens, and then it sits there looking wrong under the lens distortion. VR Face Rectify is the first half of the fix: it detects faces in each eye of a VR frame and pulls each one into a flat, rectilinear patch a swapper can actually handle.

This is one half of a bracketed pair (see VR Face Unrectify for the other). The flow is:

VR frames --> VR Face Rectify --> patches --> ReActor --> swapped patches --> VR Face Unrectify --> VR frames

Note these two nodes shipped in a v2.0 update and the pack README hasn't caught up - they're in the code and in ComfyUI's node list, just not documented upstream.

How it works

For each frame in your batch, the node splits it by input_layout: sbs for the left|right halves of a VR180 side-by-side frame (most VR content), tb for top|bottom, or mono when the whole frame is a single eye (per-eye pipelines). It detects faces in each eye with insightface's buffalo_l model - the same detector ReActor uses - so if ReActor is already in your environment, there's nothing new to install. Face detection is imported lazily, only when a rectify actually runs.

For each detected face it converts the face's pixel position into a yaw/pitch on your source projection, then renders a flat patch. Two numbers matter here: patch_fov (default 80°) and patch_size (default 768px). The author's guidance is that ~75–80° FOV suits ReActor - wide enough to keep context, narrow enough that the face fills a reasonable portion of the patch. The geometry runs on torch if it's importable (GPU), falling back to numpy/cv2 otherwise; the node prints which backend it used, which is genuinely useful to spot-check.

The inputs that matter

  • image (required) - VR180-SBS frame(s); a batch is allowed.
  • input_layout - set this to sbs for VR180 side-by-side content. The default is mono, which assumes one whole frame is a single eye; feed it full SBS footage and every "face" is actually two half-faces squished together.
  • projection - equirect (default) or fisheye. Pick fisheye for actual fisheye lenses and the node auto-detects the lens circle; fisheye_fov (default 135°) only applies then.
  • patch_fov / patch_size - the flat patch geometry. Leave the defaults until you have a reason not to.
  • input_faces_order and input_faces_index - which face(s) get rectified, ReActor-style. Default is large-small order and index 0, meaning "the dominant face only" - the usual one-character-per-job case. 0,1 grabs two, all grabs everyone.
  • detect_gender_input - optional insightface gender filter (no / female / male).
  • options - a wired ReActor-compatible OPTIONS dict overrides those three selection widgets. Leave unwired unless you're feeding it from ReActor.

Selection happens here, at frame level, on purpose: once faces are sliced into single-face patches, ReActor's own order/index options can't distinguish characters anymore - ranking is only meaningful when the whole frame is in view.

Outputs

  • patches - the IMAGE batch of flat face patches. Feed this into ReActor.
  • rectify_map - an opaque VR_RECTIFY_MAP that carries the original frames plus per-patch geometry. This is what Unrectify needs to put everything back.
  • count - number of patches produced.

Keep the map and the patches paired: Unrectify matches patch i to map entry i, so don't reorder or filter the batch between the two nodes.

Installing and troubleshooting

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

or search "Stash" in ComfyUI Manager, then restart. No model download from this pack - the only runtime need beyond the pack's light pillow/numpy/requests deps is insightface + cv2, which ReActor installs for you.

The failure mode to know: no faces detected emits a single black patch and an empty map so the IMAGE wire stays valid - then Unrectify passes the frames through untouched. If that's happening, check input_layout first (the classic "full SBS frame fed as mono" trap), then frame resolution - the detector runs at 640×640, so tiny far-away faces can be missed.

CategoryStash/VR

Inputs (10)

NameTypeDefaultDescription
imageIMAGEVR180-SBS frame(s); a batch is allowed
input_layoutoptCOMBOmono = whole frame is one eye (per-eye pipelines); sbs = left|right halves; tb = top|bottom halves
projectionoptCOMBOSource VR projection of each eye
patch_fovoptINT8020–150Field of view (deg) of the flat patch; ~75-80 suits ReActor
patch_sizeoptINT768128–2048Square patch edge in pixels
fisheye_fovoptFLOAT13590–220Full fisheye FOV (deg); only used for fisheye projection
input_faces_orderoptCOMBOlarge-smallHow to rank faces in each eye before picking by index (matches ReActor). Ignored if an OPTIONS input is wired.
input_faces_indexoptSTRING0Which ranked face(s) to swap: "0" (default = dominant face), "0,1", or "all". Selection is frame-level -> one patch per selected face. Ignored if an OPTIONS input is wired.
detect_gender_inputoptCOMBOnoOnly rectify faces of this gender (insightface). "no" = any. Ignored if an OPTIONS input is wired.
optionsoptOPTIONSOptional ReActor-compatible OPTIONS dict; when wired it overrides the three selection widgets above.

Outputs (3)

NameTypeDescription
patchesIMAGE
rectify_mapVR_RECTIFY_MAP
countINT