Nodes/Melite Poser Nodes/🦴 Poser Render OpenPose (joints → perfect skeleton, no DWPose)
ComfyUI Node

🦴 Poser Render OpenPose (joints → perfect skeleton, no DWPose)

A perfect skeleton, because DWPose shouldn't be estimating a rig you already have

By JayDataEngineer·Created 2 months ago·Updated 4 days ago· 1
🦴 Poser Render OpenPose (joints → perfect skeleton, no DWPose)
  • joints
  • mesh
  • image
â—„width768â–º
â—„height1024â–º
â—„framing1.05â–º
â—„azimuth0.00â–º
â—„elevation5.00â–º
â—„fov30.00â–º
â—„target_y0.00â–º

Here's the silliest step in most 3D-conditioning workflows: you render a posed body, then run a neural network over the pixels to guess where the joints are. DWPose does a good job on photographs and a mediocre one on smooth CG renders, where the estimator has no texture to latch onto and starts drifting a few pixels per joint. Then you feed that estimated skeleton to a pose ControlNet and wonder why the finishing character's hands are slightly wrong.

Poser Render OpenPose asks the obvious question: you already know the joint positions, why estimate them? It takes the 77 SOMA joints, pushes them through the exact camera pipeline that PoserRender uses, and draws a proper OpenPose skeleton at mathematically exact 2D coordinates. No estimator, no drift.

How it works

Two things make this more than a stick-figure plotter.

First, the camera is shared, by construction. Both PoserRender and this node call the same bounding-sphere helper and the same look-at / perspective maths - one in the rasteriser, one in pure numpy to dodge a device mismatch. Same centre, same radius, same framing, azimuth, elevation, fov, target_y. That's what keeps the skeleton sub-pixel aligned with the depth pass, and it's why the tooltip on framing yells at you to match PoserRender's value.

Second, the drawing is delegated to the authoritative renderer. Rather than hand-rolling bones, the node imports draw_bodypose and draw_handpose from custom_controlnet_aux.open_pose.util - the exact annotator code that generated the OpenPose ControlNet's training images. So you get the COCO-18 limb topology, the fixed rainbow colours, the Neck→Nose→Eye head wiring, and the HSV hands with red dots, pixel-compatible with what the ControlNet expects rather than "close enough". The comment in the source is worth the read: eyes branch off the nose, never the neck, and there is no mid-hip joint.

Inputs and outputs

Camera first: width, height, framing (defaults to 1.05 here vs 1.12 in PoserRender - set them to the same number yourself), azimuth, elevation, fov, target_y.

Then two optional typed inputs, and these are the fun bit:

  • joints - a JOINTS tuple from PoserTemplateBind or PoserBindPose. Omit it and the node loads the template bind pose straight out of skin_standard.npz: the ~51° arm drop, ~25° elbow bend canonical SOMAX rest pose. That's genuinely useful - a neutral skeleton render with zero upstream nodes.
  • mesh - used only for bounding-sphere normalisation. Omit it and the sphere is computed from the 77 joints with a 1.15× margin, which is the escape hatch for the soma identity model that can't be deformed. Fingertips and toes aren't joints, hence the fudge factor.

One output: IMAGE, 3-channel, ready to wire into a pose ControlNet (or into a ControlNetApply with an OpenPose model). The skeleton is drawn on a black canvas exactly as the annotator does it for training data.

Install

cd /path/to/ComfyUI/custom_nodes
git clone https://github.com/JayDataEngineer/melite-poser-nodes.git

Restart, or install melite-poser-nodes from ComfyUI Manager. Two extra requirements this node has that the pack's requirements.txt does not cover:

  • custom_controlnet_aux - the module path belongs to Fannovel16's ComfyUI's ControlNet Auxiliary Preprocessors. If that pack isn't installed, this node dies at execution on the import, and the error message points at a module name rather than at the pack.
  • /opt/kimodo/kimodo/assets/skeletons/somaskel77/skin_standard.npz - the template bind pose is a hard-coded absolute path. If you're passing joints in, you never touch it. If you're not, and that file isn't at that exact location, the node has nothing to load.

Where people get burned

Coordinate mismatch is silent. Feed a joints from a shaped identity and a mesh from an unshaped one and you get a skeleton floating beside the body with no error. Same shape model on both sides, every time.

Hands are all-or-nothing. The hand renderer dereferences every keypoint without null checks, so if any single hand joint projects behind the camera the whole hand is dropped - which is also what DWPose does, deliberately. That means extreme poses lose hands instead of producing garbage. Body joints handle None gracefully; the ears (COCO indices 16 and 17) are always None because SOMA-77 has no ear joints, and the renderer just skips them.

The 1.05 vs 1.12 default will catch you. The two nodes ship with different framing defaults. Copy the value across explicitly.

A note on why this node is interesting beyond the pack: people have been reverse-engineering the OpenPose skeleton spec from preprocessor output for years. There's a 2024 thread where the only reliable answer to "where exactly does the neck joint sit" was render it from a 3D character's skeleton - which is precisely this. If your pipeline has ground-truth joints, estimating them from pixels is a step you can just delete.

CategoryTechNoir/Poser

Inputs (9)

NameTypeDefaultDescription
widthINT76832–4096—
heightINT102432–4096—
framingFLOAT1.050.1–10Camera framing — MUST match PoserRender's value for the skeleton to align with the mesh render.
azimuthFLOAT0.00-360–360—
elevationFLOAT5.00-90–90—
fovFLOAT30.001–170—
target_yFLOAT0.00-1–1—
jointsoptJOINTSOPTIONAL. When provided, uses those joint positions (from PoserTemplateBind or PoserBindPose). When OMITTED, loads the 1:1 template bind pose from skin_standard.npz (51° arm drop, 25° elbow bend) — the ACTUAL canonical SOMAX rest pose, not a synthetic A-pose.
meshoptMESHOPTIONAL. When provided, bounding-sphere normalization uses the mesh vertices (same as PoserRender). When omitted, the bounding sphere is computed from the 77 joints directly — needed for the 'soma' identity_model which can't deform (no PoserDeformSoma).

Outputs (1)

NameTypeDescription
imageIMAGE—