ComfyUI Node

Face Wrapper

The landmark warping at the heart of Face Processor

By SykkoAtHome·Created 2 years ago·Updated 9 months ago· 20
Face Wrapper
  • image
  • fp_pipe
  • mask
  • image
  • fp_pipe
  • mask
modeDebug
deviceCUDA
show_detectionfalse
show_targetfalse
refinerNone
landmark_size4
show_labelsfalse
x_scale1.00
y_transform0.00

The name undersells this one. FaceWrapper doesn't wrap anything in the API sense - it's the node that actually warps a face, geometrically, using 468 MediaPipe landmarks and a triangle mesh. If you've only ever "fixed" faces by masking and inpainting, this is a different branch of the same problem: instead of asking the model to redraw the face, you map every pixel to where it belongs. Same destination, zero diffusion calls.

What it does

Feed it an image and it runs MediaPipe Face Mesh, giving you 468 landmarks (eyes, nose, mouth, jaw - the works). Then one of three modes decides what happens:

  • Debug - draws the detected landmarks (green) and the "target" base landmarks (red) over your image. This is your alignment preview. Turn on show_labels and landmark_size up if you want to nerd out on indices.
  • Un-Wrap - warps the face from its detected landmarks onto a normalized base face (centered, eyes level). The output is a straightened, front-facing-ish crop you can process however you like.
  • Wrap - the reverse: takes a processed face and warps it back onto the original landmark positions, using state stored in fp_pipe from an earlier Un-Wrap or Fit pass.

The Un-Wrap → do stuff → Wrap loop is the whole point of the pack: it gives you a deterministic, fully manual "face fit" you can repeat identically across a sequence. Compare that with ADetailer-style detect-and-inpaint, which is automatic but slightly different every pass.

How it works

Warping is triangle-based, using the canonical face mesh topology that ships with MediaPipe. Source and target landmarks triangulate the face; each pixel inside a triangle gets mapped through it, and the image is rebuilt by reverse-mapping. There's a pure NumPy CPU implementation and a CUDA one that pulls in CuPy for parallel processing - same math, faster when it works. x_scale (0.5–1.0) and y_transform (−0.5 to 0.5, fraction of image height) shape the normalized base face the Un-Wrap targets.

The fp_pipe DICT is the glue: it carries target_lm (the base landmarks) and per-frame detected_lm under frames, which is exactly what Wrap needs to put the face back. Wire the pipe out of one node and into the next - that's how the pack's nodes talk to each other.

Inputs and outputs that matter

You mostly set mode, device (CPU or CUDA), and x_scale/y_transform. The refiner input is worth a glance: None is pure MediaPipe, Dlib adds a 68-landmark refinement that gets interpolated back to 468 - slightly more robust on tough angles, but it downloads a ~100 MB dlib model on first use. fp_pipe and mask are optional; a supplied mask gets warped right along with the image, which is handy if you're keeping a face region for later.

Outputs: image, fp_pipe (updated), and mask.

Installing it

FaceProcessor has no packaged release - it's a clone-and-hope affair, same as most of the ecosystem. In ComfyUI Manager, search "Face Processor" and hit install, or:

cd ComfyUI/custom_nodes
git clone https://github.com/SykkoAtHome/ComfyUI_FaceProcessor.git

Then restart ComfyUI. The repo's requirements.txt wants mediapipe>=0.10.0, dlib>=19.24.0, pandas>=2.0.0, and cupy-cuda12x (swap for cupy-cuda11x on CUDA 11). The README also lists opencv/numpy/pillow/torch, which you already have if ComfyUI runs. Models aren't manual: the first run downloads the face_landmarker.task, the canonical face mesh, and (if you use it) the dlib predictor into core/resources/models/.

Where people get burned

The big one: pick CUDA without CuPy installed and the node quietly returns your image unwarped - the code catches the missing-import and bails instead of failing loudly. If a Wrap/Un-Wrap seems to do nothing, check your console. Wrap with no valid fp_pipe (no prior Un-Wrap or Fit) is a silent no-op too. And no face detected just passes the original through with an empty mask. Also note the GPU path only actually runs when torch.cuda.is_available() - if you're on a Mac or AMD, you're on the CPU deformer regardless of what the dropdown says.

CategoryFace Processor

Inputs (12)

NameTypeDefaultDescription
imageIMAGE
modeCOMBODebug3 options: Debug, Un-Wrap, Wrap
deviceCOMBOCUDA2 options: CPU, CUDA
show_detectionBOOLEANfalse
show_targetBOOLEANfalse
refinerCOMBONone2 options: None, Dlib
landmark_sizeINT41–10
show_labelsBOOLEANfalse
x_scaleFLOAT1.000.5–1
y_transformFLOAT0.00-0.5–0.5
fp_pipeoptDICT
maskoptMASK

Outputs (3)

NameTypeDescription
imageIMAGE
fp_pipeDICT
maskMASK