jz Segmented Figure Control
When Your Pose Skeleton Renders the Character Too Thin
- pose_keypoint
- control
- drawn
The skeleton isn't the shape you want
An OpenPose or DWPose skeleton is honest about joints and dishonest about everything else. It says where the wrist is and nothing about how many segments make up that arm - and the limb thickness it implies belongs to whoever was in the reference photo. Point that at a character built from stacked rounded segments and you get a figure rendered too thin, with the wrong number of lumps.
jz Segmented Figure Control (jz_SegmentedFigureControl, category jz/image) takes that same POSE_KEYPOINT and redraws it as a schematic figure with the structure already in it - segment counts drawn in explicitly, every proportion derived from the figure's own torso height rather than the person's build. It's plain geometry, so the counts are exact rather than inferred. And it's aimed at edit models, which take a hand-made control map as a reference and follow it: no ControlNet checkpoint, just a map and a sentence.
How it draws
The node walks the COCO-18 body keypoints - neck, shoulders, elbows, wrists, hips, knees, ankles - and lays overlapping ellipses along each limb, tapering toward the far end. Overlap is deliberately heavy: at less, an outstretched arm renders as a string of separate beads. Torso scale comes from neck-to-hip distance, so a short character doesn't come out stumpy, and the drawing order (legs, torso, arms, head, feet) is what makes it read as a body.
Three styles: outline (default - white shapes with a black outline on a dark blue backdrop), regions (one flat colour per segment, no outline), and silhouette (outer boundary only, internal lines stripped, which is the domain a silhouette LoRA is trained in).
The author expected regions to win and defaulted to it. Head to head on twelve poses it was worse than line art and worse than passing no control at all - the figure came out as a stack of separate blobs. The reasoning holds up: published flat-colour comparisons transfer a layout into a photograph, while the target here is itself flat white line art. A control map in the output's own visual language reads as "refine this"; blobs read as "render these shapes".
Inputs and outputs
You'll touch three of the seven inputs. pose_keypoint is the only required wire, and it's a POSE_KEYPOINT, not an image - it comes off a DWPose or OpenPose preprocessor. width and height default to 1024×1536 (multiples of 8) and define the canvas the figure is drawn on. Then the counts: leg_segments (default 5), arm_segments (default 4), torso_bands (default 4), all 1–12 - those defaults suit the one character the author built this for.
The one worth thinking about is min_leg_straightness (0–1, default 0 = off). Set it and the node measures leg extension - hip-to-ankle distance over the hip-knee-ankle path, worst leg - refusing poses below the threshold, on the theory that ellipses laid along a squashed crouched skeleton don't read as a body. The calibration is refreshingly blunt: the two poses that failed scored 0.660 and 0.927, the ten that worked 0.942 to 1.000, so 0.93 separates them by 0.015. Hint, not classifier - it will refuse poses that would have worked.
Two outputs. control is the IMAGE you wire into Apply ControlNet, a pose LoRA path, or a reference slot on an edit model. drawn is a BOOLEAN saying whether a usable figure came out - wire it into jz Switch or jz Fallback from the same pack so a crouched pose falls through to the plain skeleton instead of failing the job.
Installing it
The pack publishes to the Comfy Registry from CI, so try ComfyUI Manager first - search comfyui-jz, or the display name jz Segmented Figure Control.
cd ComfyUI/custom_nodes
git clone https://github.com/j-zhang19/comfyui-jz
# restart ComfyUI
No model downloads, no API key, no pip dance. The pack's pyproject.toml asks for requests, pillow and numpy, which ComfyUI already ships; this node uses only Pillow, numpy and torch. Most of jz is API nodes for Gemini, OpenRouter, BytePlus and fal - this one touches no network.
Fair warning: the README doesn't document this node. Its jz/image contents list stops at twelve and this isn't one of them. The source is nodes/image/segmented_figure_control.py, and its docstring beats most wikis.
Where people get burned
A black frame and no explanation. No neck keypoint, no hips, or no people at all, and the node returns a blank black image with drawn false rather than raising. Sensible - a graph shouldn't die mid-run - but ignore drawn and you'll wonder why your control map is empty.
Wiring an image in. POSE_KEYPOINT is a keypoint structure, not pixels; no socket takes a skeleton PNG. You need a preprocessor - comfyui_controlnet_aux supplies DWPose and OpenPose Preprocessor and their pose_keypoint output - or a JSON pose editor node.
Aspect mismatch stretches the figure. Coordinates scale from the pose's own canvas to your width/height, and that scaling is anisotropic whenever the two aspects differ. A wide source photo onto a 1024×1536 portrait canvas gives you a long, thin character, and you'll blame the segment counts.
Only the first person, only the body. The node reads people[0] and nothing else, so multi-person poses lose everyone but one. COCO-18 carries no hands or face, so a hand-heavy pose is as vague here as a plain skeleton.
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 | — |