Skip to content

feat(metrics): add COCO keypoint mAP (OKS) for sv.KeyPoints - #2687

Merged
Borda merged 21 commits into
roboflow:developfrom
8rulerstar:feat/keypoint-map
Oct 9, 2026
Merged

Borda merged 21 commits into
roboflow:developfrom
8rulerstar:feat/keypoint-map

Conversation

@8rulerstar

@8rulerstar 8rulerstar commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Description

Closes #2686.

Thank you for maintaining supervision! This follows up on the feature proposal in #2686.

Adds sv.metrics.KeypointMeanAveragePrecision, the COCO keypoint mAP. It reuses COCOEvaluator and replaces only the IoU step with Object Keypoint Similarity, ported from pycocotools' computeOks with the keypoint parameters (maxDets=20, medium/large areas, COCO sigmas). Ignore regions for targets without visible keypoints and crowd targets follow COCO.

import supervision as sv
from supervision.metrics import KeypointMeanAveragePrecision

metric = KeypointMeanAveragePrecision()  # COCO sigmas for 17-point skeletons
for predictions, targets in zip(predictions_list, targets_list):
    metric.update(predictions, targets)  # one sv.KeyPoints per image

print(metric.compute())

The fix(metrics) commit is a small related fix: the shared precision guard used np.finfo(np.float32).eps, while pycocotools uses np.spacing(1), so a perfect box prediction scored 0.99999988 instead of 1.0. It is its own commit so it can be split into a separate PR if you prefer.

Motivation and Context

There is no pose metric in sv.metrics yet; evaluating sv.KeyPoints means converting to COCO JSON and running pycocotools. Details in #2686. I had no answers to the open questions there yet, so I went with the defaults proposed in the issue and am happy to change any of them:

  • Only the metric and its result are public. The OKS helper, COCO sigmas and max detections stay private (_keypoint_oks_batch, _COCO_KEYPOINT_SIGMAS, _KEYPOINT_MAX_DETECTIONS).
  • One PR, with the epsilon fix kept as its own commit.
  • Targets read data["area"], data["xyxy"] and data["iscrowd"]; predictions read data["xyxy"] for the size bucket.
  • The pycocotools comparison is a script in this description, not a test dependency.

Changes Made

  • metrics/keypoint_mean_average_precision.py: KeypointMeanAveragePrecision and KeypointMeanAveragePrecisionResult (plot(), to_pandas(), printable COCO-style summary).

  • detection/utils/iou_and_nms.py: private _keypoint_oks_batch, including the COCO expanded-box branch for targets without visible keypoints.

  • metrics/mean_average_precision.py:

    • The accumulator reads the largest max_dets instead of the literal 100. Box, mask and OBB results are unchanged with the default settings; a regression test covers it.
    • Plot, pandas and mean helpers are shared between the box and keypoint results instead of duplicated.
    • Separate commit: EPS = np.spacing(1).
  • Docs page, mkdocs entry, changelog entries.

  • Review follow-up: keypoints given as (N, K, 3) use only the planar xy[..., :2]; sigmas must be positive and finite; docs describe NaN keypoints and zero target areas precisely; more tests for result helpers and edge cases.

Testing

Compared with pycocotools 2.0.11 COCOeval(..., "keypoints"), all five summary values (AP, AP50, AP75, APm, APl):

Data Max abs difference
200 COCO val2017 images, yolo11n-pose predictions, 853 targets incl. 19 crowd and 267 without visible keypoints 2.8e-9
Synthetic, 60 seeds: multiple classes, 10% crowd, targets without visible keypoints, more than 20 predictions per image 1.4e-8
Synthetic, 40 seeds incl. a 5-point skeleton with custom sigmas 1.1e-8

Without target boxes, targets with no visible keypoints are skipped and the COCO subset differs by up to 8.6e-3; this is documented.

  • uv run pytest tests/metrics tests/detection: 2673 passed, 1 skipped. Doctests of the changed modules pass. pre-commit on changed files passes (incl. mypy).

  • The epsilon test fails on develop (0.9999998807907104 == 1.0) and passes with the fix. On random data, box, mask and OBB scores move up by at most 5e-8.

  • Added/updated tests, and the full suite passes locally

  • Updated docs (docstrings / mkdocs entry) for new or changed public API

  • Added a changelog entry in docs/changelog.md under Unreleased (skip for lint/type/format-only or pure doc changes)

Additional Notes

  • keypoint visible is boolean in sv.KeyPoints, so COCO v=1 and v=2 are both treated as labelled, as in pycocotools.
  • Out of scope for now: AR metrics and per-keypoint breakdowns.
  • I can add the comparison script to this description or the test suite, whichever you prefer.

Thank you for taking the time to review this. Any feedback is very welcome, and I'm glad to adjust the API, naming or structure to whatever fits supervision best.

@8rulerstar
8rulerstar requested a review from SkalskiP as a code owner October 7, 2026 04:16
8rulerstar added a commit to 8rulerstar/supervision that referenced this pull request Oct 7, 2026
@codecov

codecov Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.41860% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 93%. Comparing base (67c9600) to head (a1e990b).

Additional details and impacted files
@@           Coverage Diff            @@
##           develop   #2687    +/-   ##
========================================
  Coverage       93%     93%            
========================================
  Files           81      82     +1     
  Lines        12238   12526   +288     
========================================
+ Hits         11360   11674   +314     
+ Misses         878     852    -26     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Borda
Borda requested a balanced review from Copilot October 7, 2026 05:50
@Borda Borda added the bug Something isn't working label Oct 7, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Valid three-coordinate KeyPoints can crash or miscompute OKS, while infinite sigmas can make unrelated poses perfect matches.

Review effort: Balanced
Findings: 1 High severity · 1 Medium severity · 5 Low severity

Open (7)
What changed in this PR

Adds COCO-compatible keypoint mAP evaluation for sv.KeyPoints using Object Keypoint Similarity.

Changes:

  • Introduces keypoint mAP metrics, OKS matching, and public exports.
  • Adds extensive parity and regression tests.
  • Adds API documentation, navigation, and changelog entries.
File Description
src/​supervision/​detection/​utils/​iou_and_nms.py Implements OKS calculation and COCO sigmas.
src/​supervision/​metrics/​keypoint_mean_average_precision.py Implements keypoint mAP evaluation and result reporting.
src/​supervision/​metrics/​mean_average_precision.py Shares reporting helpers and updates precision/max-detection handling.
src/​supervision/​metrics/​__init__.py Exports the new metric API.
tests/​metrics/​test_keypoint_mean_average_precision.py Tests OKS, COCO parity, categories, and reporting.
tests/​metrics/​test_mean_average_precision.py Adds epsilon and max-detection regressions.
docs/​metrics/​keypoint_mean_average_precision.md Documents the public API.
mkdocs.yml Adds the documentation page to navigation.
docs/​changelog.md Records the feature and precision fix.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/supervision/metrics/keypoint_mean_average_precision.py Outdated
Comment thread src/supervision/detection/utils/iou_and_nms.py Outdated
Comment thread docs/metrics/keypoint_mean_average_precision.md Outdated
Comment thread src/supervision/metrics/keypoint_mean_average_precision.py Outdated
Comment thread src/supervision/metrics/mean_average_precision.py Outdated
Comment thread tests/metrics/test_keypoint_mean_average_precision.py Outdated
Comment thread tests/metrics/test_mean_average_precision.py Outdated
8rulerstar added a commit to 8rulerstar/supervision that referenced this pull request Oct 7, 2026
Add sv.metrics.KeypointMeanAveragePrecision, the COCO keypoint mAP over
sv.KeyPoints predictions and targets, plus sv.keypoint_oks_batch and
sv.COCO_KEYPOINT_SIGMAS. The metric subclasses the existing COCOEvaluator and
only replaces the IoU computation with OKS, so matching, accumulation and
101-point interpolation are shared with MeanAveragePrecision. The accumulator
now reads the largest max_dets instead of the literal 100, which keeps box,
mask and OBB results unchanged and allows the COCO keypoint maxDets of 20.

Scores match pycocotools 2.0.11 COCOeval(..., "keypoints") on synthetic data
within 1e-7.
…tools

The precision `tp / (fp + tp + EPS)` used the float32 epsilon (~1.2e-7)
where pycocotools uses `np.spacing(1)` (~2.2e-16), so a perfect
precision became 0.99999988 and one perfectly predicted box scored
mAP@50:95 0.9999998807907104 instead of 1.0. Use `np.spacing(1)`.
@Borda Borda added enhancement New feature or request and removed bug Something isn't working labels Oct 8, 2026
Changes:
- Mask non-finite keypoint coordinate deltas before adding similarity, so each contributes zero without contaminating finite points.
- Add a mixed-NaN regression proving visible finite points retain expected contribution.

Impact:
- Prevents NaN coordinates in predictions from turning a valid OKS result into NaN.
- Keeps visible-keypoint denominator behavior unchanged while preserving finite keypoint scores.

Verification:
- Full suite via .venv/bin/pytest --cov=supervision: 4,811 passed, 61 skipped, 39 warnings.
- pre-commit run --all-files: passed.

Residual limits:
- Codex Rig workflow remediation remains outside this repository commit because its worktree mixes those changes with unrelated uncommitted work.
- OpenCV is unavailable; tests used the package’s NumPy fallback.

---

Co-authored-by: Codex <codex@openai.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/supervision/detection/utils/iou_and_nms.py
Comment thread docs/metrics/keypoint_mean_average_precision.md Outdated
Comment thread src/supervision/metrics/keypoint_mean_average_precision.py Outdated
8rulerstar and others added 4 commits October 9, 2026 13:20
- fix(metrics): treat non-finite target keypoints as unlabelled
- fix(metrics): validate keypoint mAP area and sigmas upfront
- docs(metrics): document _compute_iou as the COCO evaluator hook
- refactor(metrics): move mAP result helpers to metrics core
- fix(metrics): keep class 0 for unlabelled agnostic keypoint mAP
- fix(metrics): reject keypoint skeletons with zero keypoints

[resolve group] PR roboflow#2687 — items 12 13 14 15 16 32

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Codex <codex@openai.com>
- test(metrics): add pycocotools parity generator and state the ~1e-7 tolerance
- test(metrics): pin keypoint mAP fallback target area semantics
- test(metrics): cover ignore-region and crowd semantics in keypoint mAP
- test(metrics): pin keypoint mAP maxDets cap and missing-confidence scoring

[resolve group] PR roboflow#2687 — items 17 18 19 20

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Codex <codex@openai.com>
Borda and others added 11 commits October 9, 2026 13:40
- perf(metrics): skip small area range in keypoint mAP evaluation
- perf(metrics): vectorize per-image keypoint mAP prep
- refactor(metrics): add public xyxy and iscrowd data keys to config

[resolve group] PR roboflow#2687 — items 22 23 27

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Codex <codex@openai.com>
- docs(metrics): state COCO v=1 and v=2 map to visible keypoints
- docs(metrics): clarify per-class prediction cap and bbox wording in keypoint mAP

[resolve group] PR roboflow#2687 — items 24 25

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Codex <codex@openai.com>
[resolve group] PR roboflow#2687 — items 21

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Codex <codex@openai.com>
[resolve group] PR roboflow#2687 — items 28

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Codex <codex@openai.com>
- `_pycocotools_summarize` looks up the size index by area range via `_OBJECT_SIZE_AREA_RANGES`, not `ObjectSize` enum position
- drop private `_XYXY_DATA_FIELD`/`_ISCROWD_DATA_FIELD` aliases; use `XYXY_DATA_FIELD`/`ISCROWD_DATA_FIELD` from `config.py` directly
- docs: a class whose only targets are skipped (no keypoints, no `xyxy`) scores `-1`

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
- `_KEYPOINT_MAX_DETECTIONS` and `_COCO_KEYPOINT_SIGMAS`: replace the floating string literal after the assignment with a `#:` comment above it

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
- rename `tests/metrics/generate_keypoint_map_parity.py` to `_generate_keypoint_map_parity.py`
- update the `python -m tests.metrics._generate_keypoint_map_parity` command in its docstring and in the parity test comment

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
- `mkdocs.yml`: `Keypoint mAP` nav label becomes `mAP (KeyPoints)`

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
- `mkdocs.yml`: bare `mAP` nav label becomes `mAP (Detections)`, pairing with `mAP (KeyPoints)`

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
- `docs/metrics/mean_average_precision.md`: intro covering both `MeanAveragePrecision` (Detections, IoU) and `KeyPointMeanAveragePrecision` (KeyPoints, OKS); new `## Pose estimation` section with the keypoint usage notes and example; `KeyPointMeanAveragePrecision` and `KeyPointMeanAveragePrecisionResult` API blocks appended
- remove `docs/metrics/keypoint_mean_average_precision.md`
- `mkdocs.yml`: single `mAP` nav entry replaces `mAP (Detections)` and `mAP (KeyPoints)`

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
@Borda
Borda merged commit 2ccb0e6 into roboflow:develop Oct 9, 2026
36 checks passed
@8rulerstar
8rulerstar deleted the feat/keypoint-map branch October 9, 2026 13:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: COCO keypoint mAP (OKS) for sv.KeyPoints

3 participants