Nodes/ComfyUI-Sapiens2/Sapiens2 Normals
ComfyUI Node

Sapiens2 Normals

Surface normals for people that don't drift off-plane

By kijai·Created 4 months ago·Updated 4 months ago· 65
Sapiens2 Normals
  • image
  • sapiens2_model
  • mask
  • normal
frames_per_batch1

A normal map tells you which way a surface is pointing - not how far away it is (that's depth), but its 3D orientation, encoded as per-pixel color. Sapiens2's normal head is trained specifically on humans, which is the whole selling point. Generic normal estimators get skin and clothing right-ish; Sapiens2 was trained on a billion human images and the difference shows in the small stuff - fingers, folds of cloth, the plane of a face - staying coherent and stable instead of smearing into a blur. If you're generating people and you want relighting or geometry-aware conditioning, this is the node.

The two things people actually do with it: ControlNet conditioning (SDXL's normal control type eats these directly - redraw a person with the same surface geometry but a totally different look), and relighting workflows where the normal map lets a light pass re-shade the subject realistically. Both are the kind of "invisible unlock" that feels niche until you've seen a flat skin pass turn into something with actual dimensionality.

How it works

You wire in an image, the SAPIENS2_MODEL from a Sapiens2 Loader that loaded a -normal checkpoint, and it runs the frames through the network. Inputs worth knowing:

  • frames_per_batch (default 1) - frames per forward pass. Bump it up for video, drop it if you OOM. The node chunks long batches automatically, so this is purely a speed/VRAM trade.
  • mask (optional) - an output that behaves like upstream Sapiens2's vis_normal.py --seg_dir: where mask = 0, the output is black. So a foreground mask from a background-removal or SAM node gives you a clean subject-on-black normal map instead of the background's normals bleeding in. Good for feeding into ControlNet, where stray background geometry can steer the generation.

Output is a single normal (IMAGE): normalized vectors remapped from [-1,1] to [0,1], so it looks like a plausible "RGB depth" image rather than a raw vector field. It runs at the pack's standard 1024×768 working resolution and returns your original size.

Installing and wiring

Manager search "ComfyUI-Sapiens2", or git clone https://github.com/kijai/ComfyUI-Sapiens2 into custom_nodes, restart - no Python deps to babysit, though it does need a recent ComfyUI (the pack uses the newer io.ComfyNode API). Then a normal checkpoint from facebook/sapiens2-normal-1b into ComfyUI/models/sapiens2. It only matters that the checkpoint is a normal one; the loader detects it and this node refuses anything else with a clear "expected 'normal'" error - which also means you can't mix up the model folder and waste an afternoon.

Where people get burned: forgetting the mask and sending a full-scene normal map into ControlNet, then wondering why the background re-imposes itself on the new generation. A quick BiRefNet/SAM pass costs a few seconds and removes the whole class of problem. And if your normals look washed out or wrong-colored, remember they're designed to be interpreted as RGB - preview them as an image, not as a depth map.

CategorySapiens2

Inputs (4)

NameTypeDefaultDescription
imageIMAGE
sapiens2_modelSAPIENS2_MODEL
frames_per_batchINT11–256Frames per forward pass. Lower if you OOM.
maskoptMASKOptional foreground mask. Where mask=0 the output is black — matches upstream vis_normal.py with --seg_dir.

Outputs (1)

NameTypeDescription
normalIMAGE