Lip Mask from Sapiens2 Pose
Sapiens2's 32 lip keypoints turned into a mask — only worth it if you already run Sapiens2
- pose_keypoint
- mask
- centroid_x_list
- centroid_y_list
- fallback_active_list
Sapiens2 is Meta's April 2026 family of high-resolution transformers pretrained on a billion human images - pose, body-part segmentation, surface normals, pointmaps. The pose head outputs a monster 308-keypoint set (the "Goliath-308" layout), and the mouth gets special treatment: a 16-point outer lip ring plus a 16-point inner ring, 32 lip keypoints where DWPose only has 20. More points, more faithful contours. That's the entire pitch of LipMaskFromSapiens2: it reads that richer lip ring and turns it into a mask.
Straight up, though: this is a niche-of-a-niche node. You only want it if you're already running a Sapiens2 pose preprocessor - this pack is a consumer of that POSE_KEYPOINT payload, not a producer. If your pipeline is DWPose, the sibling LipMaskFromPose node gives you the same idea with less setup. If you're doing the Sapiens2 thing for the segmentation head anyway, the finer lip contours are a real upgrade for lip-focused inpainting.
How it works
The 308-keypoint layout puts the outer lip ring at indices 188–203 (vermilion border) and the inner ring at 204–219 (the wet line / mouth opening). The node reads each person's keypoints_2d array from the POSE_KEYPOINT payload, needs at least ~220 points to trust the mouth ring, and refuses otherwise.
Where DWPose's node gives you one mask_mode, this gives you four region dropdowns (region_1 through region_4), each selectable from 14 named regions - full_outer_lip, full_inner_lip, full_mouth, lips_only, outer_top_lip, outer_bottom_lip, inner_top_lip, inner_bottom_lip, top_lip_full, bottom_lip_full, plus point-based ones (mouth_corners, cupid_bow, inner_centers) that draw small disks whose size is set by point_radius (default 6). Selected regions are unioned into one mask. So "upper lip flesh plus the cupid's bow, but not the mouth interior" is a few dropdown clicks instead of a geometry problem.
Everything else is the pack's shared defensive machinery: auto_fallback swaps in an anchor-based ellipse when the inner ring collapses (puckers, kisses, occlusion), gated by fallback_area_threshold (inner/outer area ratio, default 0.15) and padded by fallback_pad_h/fallback_pad_v; confidence_threshold (default 0.3) skips people/frames with weak lip-keypoint scores; person_index picks who to mask, -1 unions everyone.
Outputs mirror the DWPose node: mask, centroid_x_list/centroid_y_list (for SAM-2 point prompting), and fallback_active_list telling you which frames ran on the fallback ellipse.
One honest limitation
Sapiens2 pose has no teeth and no tongue keypoints - the teeth were stripped from the original 344-keypoint set, and tongue never existed in the taxonomy. The node says so plainly in its own description. If you need per-pixel teeth or tongue, that's the segmentation head's job, and the same pack's MouthMaskFromSapiens2Seg node is the piece that reads it.
Installing it
Manager (search "lip_mask_from_pose") or git clone https://github.com/Jalen-Brunson/lip_mask_from_pose into custom_nodes/, then restart. This node adds zero dependencies - numpy/cv2/torch only. What you actually need is a Sapiens2 pose preprocessor that emits the 308-keypoint POSE_KEYPOINT (lassiiter's comfyui-sapiens2 or Kijai's are the ones people run). Without one of those, this node has nothing to eat.
Troubleshooting
- Mask never appears: you're feeding it a DWPose
POSE_KEYPOINT, which doesn't carry the 308-keypointkeypoints_2dfield. Check the source preprocessor, not the node. - Everything silently falls back to ellipses: low-quality face crops mean weak keypoints; raise the source resolution or loosen
confidence_threshold.
Inputs (14)
| Name | Type | Default | Description |
|---|---|---|---|
| pose_keypoint | POSE_KEYPOINT | — | |
| region_1 | COMBO | full_inner_lip | 14 options: full_outer_lip, full_inner_lip, full_mouth, lips_only, outer_top_lip, outer_bottom_lip, +8 |
| region_2 | COMBO | none | 14 options: full_outer_lip, full_inner_lip, full_mouth, lips_only, outer_top_lip, outer_bottom_lip, +8 |
| region_3 | COMBO | none | 14 options: full_outer_lip, full_inner_lip, full_mouth, lips_only, outer_top_lip, outer_bottom_lip, +8 |
| region_4 | COMBO | none | 14 options: full_outer_lip, full_inner_lip, full_mouth, lips_only, outer_top_lip, outer_bottom_lip, +8 |
| blur_radius | INT | 50–100 | Gaussian blur radius for feathered edges. 0 = hard edge. |
| grow_pixels | INT | 00–200 | Dilate the unioned mask outward by this many pixels. |
| point_radius | INT | 61–100 | Disk radius used when a selected region is point-based (mouth_corners, cupid_bow, inner_centers). |
| person_index | INT | 0-1–99 | Which detected person to use (0 = first). -1 = union all people. |
| confidence_threshold | FLOAT | 0.300–1 | Skip a person/frame when avg lip-keypoint score is below this. |
| auto_fallback | BOOLEAN | true | Fall back to an anchor-based ellipse when the inner ring collapses (puckered/occluded lips). |
| fallback_area_threshold | FLOAT | 0.150.01–1 | Inner-ring area / outer-ring area below which the ring is considered collapsed. |
| fallback_pad_h | FLOAT | 1.200.5–3 | Horizontal scale for the fallback ellipse. |
| fallback_pad_v | FLOAT | 1.300.5–3 | Vertical scale for the fallback ellipse. |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| mask | MASK | — |
| centroid_x_list | FLOAT | — |
| centroid_y_list | FLOAT | — |
| fallback_active_list | BOOLEAN | — |