jz Bib Control
A pose skeleton is the wrong control image for a character made of stacked blobs
- pose_keypoint
- control
- drawn
The problem this thing solves
You've got a character who isn't people-shaped - a mascot or toy figure built out of stacked rounded segments, five blobs per leg, four per arm, four chunky bands for the torso. You pose them with a ControlNet, feed in a DWPose skeleton, and the render comes back as a thin human wearing the character like a costume.
That's not a tuning problem. A skeleton carries joint positions and nothing else: no segment counts, and limb thickness borrowed from whoever was in the reference photo. The control image says "human proportions", so you get human proportions. jz Bib Control takes that same pose and redraws it as a figure that already has the right internal structure - segment counts drawn in, every proportion derived from the figure's own torso height. Plain geometry, and the counts are exact by construction.
How it works
It reads the pose_keypoint socket, pulls the COCO-18 body joints out of it (nose, neck, shoulders, elbows, wrists, hips, knees, ankles - the layout DWPose emits), and computes torso_h as the neck-to-hip distance. That one number sets the figure's scale: unit = torso_h / 2.6, and everything else - limb widths, torso band width, head radius, foot size - is a multiple of unit. Then it walks each limb as a polyline, drops evenly spaced ellipses along it, tapers them toward the far end, overlaps them hard so an outstretched arm reads as one continuous limb rather than a string of beads, and draws legs, torso, arms, head, feet in that order - the stacking order is what makes the drawing legible to the model.
The source comments are worth reading; this was calibrated, not guessed. Head width is pinned at 0.36 of torso width, measured off reference art - an earlier version drew it at 0.93 and the model put a ghost disc behind the figure's head in a third of frames.
The inputs that matter
pose_keypoint is the only input you truly have to feed, and it wants a POSE_KEYPOINT from a pose estimator - in practice DWPose Estimator out of comfyui_controlnet_aux, the pack that makes pose control workable in ComfyUI at all. width and height (1024×1536 default, step of 8) are your output canvas: match them to the aspect you're generating at, because the pose gets scaled into the canvas and a mismatched aspect stretches the figure anisotropically.
style has three values and the default is the surprising one. outline is white shapes with a black outline. regions is one flat colour per segment, no outline. silhouette is the outer boundary only, internal lines removed. The author expected regions to win - published comparisons rank a flat-colour region map above line art for control fidelity - and defaulted to it. Head to head across twelve poses it was far worse than line art, and worse than no control at all. Here the control already drawn in the output's visual language wins. Start on outline.
leg_segments, arm_segments and torso_bands are exactly what they say. min_leg_straightness at 0 disables the check; set it near 0.93 and clearly crouched poses get refused instead of producing a squashed pile of blobs. Treat it as a hint - it'll reject some poses that would have worked.
The outputs
control is an IMAGE - wire it to the image input of ControlNet Apply (Advanced) for a pose model, or into an edit model's reference input. drawn is a BOOLEAN and it's the clever part: when the pose can't carry a figure (no people in the keypoint data, no neck, no hips, or the straightness check failed) you get a blank frame and drawn = false instead of an exception, so nothing dies mid-graph. Pair it with jz Switch or jz Fallback from the same pack and fall back to the plain skeleton for that frame.
Installing it
Through ComfyUI Manager, search comfyui-jz and install, or:
cd ComfyUI/custom_nodes
git clone https://github.com/j-zhang19/comfyui-jz
Restart and look under jz/image. Nothing to download, no API key - the node is Pillow and NumPy. The pack's API nodes with their key rules and cost reporting don't touch this one.
Where people get burned
- Only the first person gets drawn. The node reads
people[0]out of the keypoint data, and only the first frame if you hand it a list. Two people in the reference photo means one figure in the control image. - Keypoint layout matters. The index table is COCO-18. Feed it pose JSON with a different joint ordering and limbs attach to the wrong places - it looks like a broken drawing, not an error.
- The README hasn't caught up. The 27-node list predates this one, and the file on disk is still
nodes/image/segmented_figure_control.py, so if you go digging through the source that's what you're looking for. - It's geometry, not magic. Wrong segment counts in the drawing get reproduced faithfully. Skim the
controloutput before blaming the sampler.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| pose_keypoint | POSE_KEYPOINT | — | |
| width | INT | 102464–4096 | — |
| height | INT | 153664–4096 | — |
| style | COMBO | outline | 3 options: outline, regions, silhouette |
| leg_segments | INT | 51–12 | — |
| arm_segments | INT | 41–12 | — |
| torso_bands | INT | 41–12 | — |
| min_leg_straightness | FLOAT | 0.000–1 | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| control | IMAGE | — |
| drawn | BOOLEAN | — |