ComfyUI Node

DWPose Estimator

Whole-Body Pose Without the ONNX Stack

By silveroxides·Created 3 months ago·Updated about 12 hours ago· 35
DWPose Estimator
  • image
  • image
  • pose_keypoint
  • Pose Overlay Mask
detect_handtrue
detect_bodytrue
detect_facetrue
resolution0
batch_size5
scale_stick_for_xinsr_cnfalse
detection_threshold0.45
keypoint_threshold0.45
temporal_filterfalse
temporal_radius2
temporal_min_support2
temporal_max_distance0.10
temporal_match_iou0.30
nms_threshold0.45

DWPose is the pose detector people switched to because OpenPose's hands are bad - better finger detection, whole-body output, and it has been the default in comfyui_controlnet_aux for years. It's also slow, and "slow" is the number one complaint you'll find about it in the wild, usually from someone trying to pose a whole video.

UC_DWPoseEstimator is silveroxides' reimplementation: YOLOX-L for person detection, RTMPose for the 133 whole-body keypoints, run eagerly in PyTorch, batched, with an optional temporal filter for video. Detection and pose crops go through separate batches, and partial batches aren't padded, so the last chunk of a clip doesn't pay for a full batch of empty frames.

How it works

The detector (a converted yolox_l TorchScript checkpoint) runs on each frame at a fixed 640 square, which is why resolution doesn't make it "see more detail" - it only changes the image you get back. Detected person boxes are NMS'd, expanded to crops, and each crop goes through the RTMPose model at 288x384 (the dwpose_ucoco_384 weights), which returns SimCC coordinate distributions that get decoded to 133 keypoints.

The two thresholds do different jobs. detection_threshold (0.45) decides whether a person exists at all; keypoint_threshold (0.45) decides whether an individual body, hand or face point survives. nms_threshold (0.45) is the duplicate-box cleanup - lower it and you can lose people standing close together, raise it and you'll get the same person twice.

One important difference from the OpenPose node in the same pack: detect_hand, detect_body and detect_face are drawing toggles only here. Turning off hands makes the map prettier, not the run faster. Hand and face keypoints still come out on pose_keypoint. Same for hands in the OpenPose node, where the toggle does skip the model - don't assume the two behave alike.

Inputs, outputs, wiring

Beyond the above: image (a batch, or consecutive frames for the temporal options), resolution (0 keeps your dimensions, otherwise the shortest edge), batch_size (5, your VRAM dial), and scale_stick_for_xinsr_cn, which thickens the drawn body lines at larger sizes for Xinsir-style maps. Drawing only; joint coordinates don't move.

The six temporal options run the same as the pack's other pose nodes: temporal_filter off by default, temporal_radius 2, temporal_min_support 2, temporal_max_distance 0.1 of the person's box diagonal, temporal_match_iou 0.3. It deletes joints that neighbouring frames can't vouch for and keeps the person entries. It's flicker removal, not interpolation - nothing gets invented, and pruning spans your batch chunks. Turn it off for unrelated stills.

Outputs: image (the pose map, in order - into your pose ControlNet), pose_keypoint (OpenPose-format, thresholded and filtered), and Pose Overlay Mask, which is white on drawn lines and points so you can composite the map over the original footage.

Install

Manager → search "ComfyUI-UtilsCollection", or:

cd ComfyUI/custom_nodes
git clone https://github.com/silveroxides/ComfyUI-UtilsCollection
# restart ComfyUI

Two models, downloaded on first execution into ComfyUI/models/controlnet/preprocessors (flat filenames, even though the HF repo nests them): dwpose_yolox_l.safetensors and dwpose_ucoco_384.safetensors, from silveroxides/ComfyUI-UtilsCollection-Models. No ONNX Runtime, no MMPose, no TorchScript at runtime - the pack converts the original .pth/TorchScript sources to safetensors and validates keys, shapes, dtypes and values before shipping them. The honest caveat from the README: verified conversion, unverified prediction parity and unmeasured speed. If you're squeezing every percent, benchmark it against the aux preprocessor you're replacing.

Troubleshooting

The pack imports kornia when it loads and kornia is not in its requirements.txt. If another pack hasn't already pulled it in, the entire pack fails to import and every node from it goes missing - pip install kornia in ComfyUI's Python environment fixes it.

If detection is failing on dark clothing, backlit shots or motion blur, detection_threshold at 0.45 is a reasonable default but YOLOX has never loved those frames; drop it to 0.3 first before you go rebuilding the pipeline.

And if your downstream ControlNet isn't a DWPose/OpenPose-trained one, remember you're feeding it a skeleton - pose conditioning is spatial, so the prompt still has to describe the subject.

Categoryimage/pose

Inputs (15)

NameTypeDefaultDescription
imageIMAGEImage or image batch to process. For temporal filtering, supply consecutive video frames in order, not unrelated images.
detect_handBOOLEANtrueDraw hand joints in the pose map. Turning off only hides the drawing; hand inference and keypoint output remain enabled.
detect_bodyBOOLEANtrueDraw body skeletons in the pose map. Turning off only hides the drawing; body inference and keypoint output remain enabled.
detect_faceBOOLEANtrueDraw facial landmarks in the pose map. Turning off only hides the drawing; face inference and keypoint output remain enabled.
resolutionINT00–40960 preserves the input dimensions. A positive value sets the output's shortest edge in pixels, preserving aspect ratio. Larger maps do not increase the model's fixed input size.
batch_sizeINT51–64Maximum images or person crops processed together. Higher values may improve throughput but use more memory; lower if VRAM runs out. Does not change temporal-filter range.
scale_stick_for_xinsr_cnBOOLEANfalseScale body-line thickness with image size for Xinsir-style pose maps. Changes drawing only, not detected coordinates.
detection_thresholdFLOAT0.450–1Minimum person-detection confidence. Raise to reject weak detections such as shadows; may also remove real people.
keypoint_thresholdFLOAT0.450–1Minimum confidence for body, hand, and face keypoints. Raise to hide uncertain joints; this does not track or smooth motion.
temporal_filterBOOLEANfalseVideo frames only: remove joints unsupported by nearby frames from both pose maps and keypoints. Keeps person entries; does not smooth or fill missing joints. Leave off for unrelated images.
temporal_radiusINT21–30Temporal filter only: frames to inspect before and after each frame. 2 checks up to four neighbors, across batch_size chunks. Larger windows cost more and may reject fast motion.
temporal_min_supportINT21–60Temporal filter only: neighboring frames that must confirm a joint. Raise to remove more flicker; lower to retain brief poses. Limited to available neighbors, including at clip edges.
temporal_max_distanceFLOAT0.100.001–1Temporal filter only: allowed joint displacement relative to the person's box diagonal; 0.1 means 10%. Raise for faster motion; lower to reject sudden jumps.
temporal_match_iouFLOAT0.300–1Temporal filter only: required overlap between detection boxes to match a person across frames. Raise to avoid mixing people; lower for faster movement or changing boxes.
nms_thresholdFLOAT0.450–1Overlap limit for removing duplicate person boxes. Lower removes more overlapping detections but can lose nearby people; higher keeps more and may leave duplicates.

Outputs (3)

NameTypeDescription
imageIMAGEPose maps in input-frame order, showing the enabled body, hand and face drawings.
pose_keypointPOSE_KEYPOINTPer-frame OpenPose-format keypoints after thresholding and optional temporal filtering. Drawing toggles do not remove body, hand or face keypoints.
Pose Overlay MaskMASKWhite on rendered pose lines and points; black elsewhere. Use to composite the pose map over an image or video. Matches pose-map size and frame order; not a person silhouette.