Reach Score (gate + D)
Is Your Character Actually Doing the Pose? Reach Score (gate + D)
- image
- reach_d
- gate_status
- gate_reason
- info
If you batch-generate anime characters and want to auto-keep only the frames where the figure is actually doing the pose - arm extended, wrist above the hip, standing full-body - the generic pose scorers will quietly lie to you. That's the gap this node exists for. Reach Score (gate + D) is the newest node in the CCIP Judge pack, and it's the one the pack's own authors use for action-pose checks: it scores how well a generated figure reproduces a target reach, and refuses to grade anything that isn't a verifiable standing full body.
It's part of the pack's bigger idea: score a batch against a reference character (CCIP), a pose (OKS/Angle), then route only the winners to liked. Reach Score slots in where the generic pose metric isn't trustworthy.
Why OKS isn't good enough
The pack's other pose node, OKS Score, uses COCO-17 OKS. The problem, straight from the source: OKS caps the four arm joints at 4/17 of the score and renormalizes over whichever joints both poses share. On a reach task, a figure standing there with arms completely in the wrong place can outscore one that actually reproduces the reach. Reach Score was built to close exactly that normalization exploit.
How it works: a gate, then a score
Two stages, always in this order, never blended:
- Full-body validity gate - a fail-closed constraint. Seated, kneeling, cropped, or undetectable figures are rejected outright and get no score:
NaNplus a status and reason. That's deliberate. A low-but-real number is something an evolutionary search can average away, and a seated figure with perfect arms would otherwise score near 1.0. Rejects cost you nothing downstream becauseNaNnever passes any threshold. - Reach composite (D) - only for gated-in images. D = 0.25·arm position + 0.625·arm angle + 0.125·reach extent, all in a torso-anchored frame so it's translation- and scale-invariant. In plain terms: where the arms are, which way the arm segments point, and how high the wrists sit relative to the pelvis. Angle carries most of the signal; position and reach are anti-degeneracy guards. Output lands in [0, 1], higher = closer to the target.
The reference pose comes from authored keypoints, not from re-estimating a rendered image - that's the failure this project already paid for once, so there's no image fallback. Pose extraction on the generated side runs the anime person detector plus DWPose (dw-ll_ucoco_384.onnx, pulled from Hugging Face on first run), through a shared content-keyed cache, so images already scored by OKS Score cost no second DWPose inference.
The inputs that matter
Only two inputs, both required:
image(IMAGE) - your batch of generated frames, usually straight off VAEDecode.reference_pose_json(STRING) - a path to an OpenPose BODY-18 JSON (the same authored JSON that drives OpenPoseStudio in the generation workflow). Leave it empty and the node raises; a malformed file raises too. It's a setup error, not a soft failure.
Four outputs: reach_d (FLOAT per image, NaN for rejected), gate_status (ok / invalid_pose / invalid_frame / invalid_detection), gate_reason (the specific reason string), and info (one-line summary: count, gated-in count, mean D, rejection breakdown). Wire reach_d into a threshold or the pack's Image Router; the status/reason outputs are what you read when everything comes back rejected.
Installing it
In ComfyUI Manager, search CCIP Judge; or clone and install by hand:
cd <ComfyUI>/custom_nodes
git clone https://github.com/o-ankomochi-o/comfyui-ccip-judge.git
cd comfyui-ccip-judge
python -m pip install -r requirements.txt
Dependencies are dghs-imgutils, onnxruntime, huggingface_hub, opencv-contrib-python, numpy, and Pillow. Two gotchas from the README: stick to Python 3.10–3.12 (3.13 breaks on dghs-imgutils pinning numpy<2), and don't install onnxruntime and onnxruntime-gpu into the same environment. First run downloads the DWPose model from Hugging Face.
Common issues
- Everything comes back NaN. Check
gate_reason. The usual suspects: seated figures, legs cropped off the bottom edge (invalid_frame:lower_body_out_of_frame), or extreme close-ups where the torso/shoulders aren't detectable. - "reference_pose_json is required" - this node has no image fallback by design. Feed it the authored pose.
- It doesn't measure the character. Reach D is about the action. If you're also filtering on likeness, that's CCIP Score's job.
- Multi-person images score the largest detected person's bbox, not the semantic hero. The README calls this out for the pack as a whole.
Bottom line: if you're building an automated LoRA-eval or pose-filtering pipeline and the arms matter, this is the node to reach for. If you just want a quick pass/fail on "is a person standing here," OKS Score is simpler and fine.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| reference_pose_json | STRING | — |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| reach_d | FLOAT | — |
| gate_status | STRING | — |
| gate_reason | STRING | — |
| info | STRING | — |