OKS Score
Does the generated pose actually match the reference?
- image
- reference_image
- oks
- pass_mask
- info
- reasons
Character similarity alone doesn't get you a usable batch. Your LoRA can nail the face while the character is bent into a pose your reference never makes. OKS Score is the middle stage of the CCIP Judge pack: it compares the skeleton of each generated image against your reference pose and scores how well the joints line up.
OKS stands for Object Keypoint Similarity - the same metric COCO pose evaluation has used for years, so you're not learning some bespoke scoring system. Higher is better, and the default pass line is 0.5.
How it works
Each image goes through two models in sequence:
- An anime-trained person detector (
person_detect_v1.1_mfromdghs-imgutils) finds the largest person in the frame - "largest," not "the main character," which matters for group shots. - DWPose (
dw-ll_ucoco_384.onnx, pulled from Hugging Face on first run) extracts the 17 BODY keypoints of that person.
Your reference pose gets the same treatment. Then the node normalizes both skeletons over their common visible joints - so it's crop- and frame-size invariant - converts per-joint distance into similarity using COCO's sigma table, and averages. Two design details worth knowing, because they'll explain confusing failures:
- If the generated image is missing a joint that the reference has, that joint scores 0 and still counts against you. It doesn't shrink the denominator, so hiding wrong joints never inflates the score.
- Both shoulders are required. No shoulders, no pose measurement - the node refuses rather than scoring garbage.
Detection or pose-extraction failure produces NaN, which fails against every threshold. A broken image can never accidentally pass.
The inputs that matter
image- your generated batch.threshold- pass line, default0.5.reference_folderorreference_image- the reference pool (reference_imagewins if both are set).reference_pose_json- an OpenPose BODY-18 JSON file. If you always compare against the same reference pose, supply this and the node skips re-estimating the reference every run. Faster, and it stabilizes your baseline.keypoint_set-"portrait"(face through wrists, joints 0–10),"full_body"(all 17), or empty (also all 17). Useportraitfor head-and-shoulders shots.
One input is a trap: fail_score looks like a safety net, but it's a leftover from an older version and is ignored. Don't set it expecting a graceful fallback for failed images - there isn't one, by design.
Outputs: oks (per-image score), pass_mask (per-image boolean), info (a text summary), and reasons - per-image strings like missing_required_joints(left_shoulder) that tell you why something failed instead of a bare score.
Installing
Same as the rest of the pack - one clone and one pip install:
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
Or search "CCIP Judge" in ComfyUI Manager. Python 3.10–3.12 only; 3.13 breaks the dependency install (dghs-imgutils pins numpy<2, no 3.13 wheels). First run downloads the DWPose ONNX model from Hugging Face.
Where people get burned
- Group shots. It scores the biggest bounding box, not the protagonist. If two characters are in frame, that's a coin flip.
- Extreme close-ups of a face: no shoulders detected, so you get
NaN, and the image fails regardless of how good it is. full_bodyon an image where the character is cropped at the edge - missing joints count as 0-point hits, so off-screen bodies fail hard.
Wire oks into Three-Stage Filter (or Image Router) alongside the CCIP and Angle scores. OKS alone catches pose drift; combined with CCIP it separates "right person, wrong pose" from "wrong person entirely."
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| threshold | FLOAT | 0.500–1 | — |
| reference_folder | STRING | — | |
| fail_score | FLOAT | 0.00-1–1 | — |
| reference_imageopt | IMAGE | — | |
| reference_pose_jsonopt | STRING | — | |
| keypoint_setopt | STRING | — |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| oks | FLOAT | — |
| pass_mask | BOOLEAN | — |
| info | STRING | — |
| reasons | STRING | — |