Robbie De LiseandClaude Opus 4.8 fe2f12fe43 Generic YOLO-Pose keypoints node for ComfyUI
A single product-agnostic OUTPUT_NODE: IMAGE -> keypoints JSON, also surfaced to
/history so an external orchestrator can read it with no file sidecar. Runs any
ultralytics pose .pt from models/yolo_pose/; pose semantics live downstream.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 01:11:35 +02:00

ComfyUI — YOLO-Pose Keypoints

A single, product-agnostic ComfyUI node that runs an ultralytics YOLO-Pose model on an image and returns the detected keypoints as JSON. It is an OUTPUT_NODE, so the JSON is also surfaced to ComfyUI's /history (outputs[<node_id>].text[0]) — letting an external orchestrator read the keypoints over the HTTP API with no file sidecar (the same pattern ComfyUI graphs already use for previewing arbitrary values).

It deliberately knows nothing about what the keypoints mean. Pose semantics (what each keypoint index means, any 3D model, solvePnP, UV baking) belong in your downstream code, not in a render node.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/solanex/comfyui-yolo-pose
pip install -r comfyui-yolo-pose/requirements.txt   # ultralytics

Put your trained pose weights in ComfyUI/models/yolo_pose/ (created on first load). The node lists every .pt there in a dropdown. Weights are yours — they are not shipped with this node.

The node — "YOLO-Pose Keypoints" (category keypoints)

Input Type Notes
image IMAGE single still (batch frame 0 is used)
model_name dropdown a .pt in models/yolo_pose/
confidence FLOAT detection threshold (default 0.25)

Output: keypoints_json (STRING) — and the same string in /history.

{
  "image_size": [1920, 1080],
  "model": "pose_model.pt",
  "instances": [
    {
      "box": [812.0, 396.0, 1958.0, 1820.0],
      "box_conf": 0.974,
      "keypoints": [[840.0, 412.0, 0.99], [1102.0, 508.0, 0.98], "..."]
    }
  ]
}

Keypoints are [x, y, confidence] in the input image's pixel coordinates, one list per detected instance. Instances are sorted best-first by box_conf.

Reading it from an orchestrator

hist = requests.get(f"{COMFYUI_URL}/history/{prompt_id}").json()
text = hist[prompt_id]["outputs"][NODE_ID]["text"][0]
kp = json.loads(text)["instances"][0]["keypoints"]   # pick/validate the right one

Then feed those 2D keypoints + your canonical 3D model to solvePnP and bake the UV map. (Multiple instances? Pick the one that matches your object — e.g. best overlap with a segmentation mask — rather than blindly taking index 0.)

License

MIT.

S
Description
No description provided
Readme
28 KiB
Languages
Python 100%