Skip to content

fix(metrics): replace np.unique matching with greedy algorithm - #2380

Merged
Borda merged 4 commits into
developfrom
fix/2378
Jul 1, 2026
Merged

fix(metrics): replace np.unique matching with greedy algorithm#2380
Borda merged 4 commits into
developfrom
fix/2378

Conversation

@Borda

@Borda Borda commented Jul 1, 2026

Copy link
Copy Markdown
Member

The two-pass np.unique deduplication in _match_detection_batch dropped valid TP assignments when a prediction's best-IoU target was already claimed by a higher-confidence prediction. The greedy one-pass algorithm (sort by IoU desc, assign if neither target nor pred already matched) was already used in _split_detections_by_outcome and ConfusionMatrix.evaluate_detection_batch but missing from Recall, F1Score, Precision, MeanAverageRecall, and MeanAveragePrecision.


resolves #2378

The two-pass np.unique deduplication in _match_detection_batch dropped
valid TP assignments when a prediction's best-IoU target was already
claimed by a higher-confidence prediction. The greedy one-pass algorithm
(sort by IoU desc, assign if neither target nor pred already matched)
was already used in _split_detections_by_outcome and
ConfusionMatrix.evaluate_detection_batch but missing from Recall,
F1Score, Precision, MeanAverageRecall, and MeanAveragePrecision.

- Fix all five _match_detection_batch implementations
- Add regression tests reproducing the issue #2378 example in each
  affected metric class (IoU matrix [[1.0, 0.667], [0.333, 0.538]])

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
@Borda
Borda requested a review from Copilot July 1, 2026 09:33
@Borda
Borda requested a review from SkalskiP as a code owner July 1, 2026 09:33
@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83%. Comparing base (97f5c0c) to head (cdc228a).

Additional details and impacted files
@@           Coverage Diff           @@
##           develop   #2380   +/-   ##
=======================================
+ Coverage       83%     83%   +1%     
=======================================
  Files           69      70    +1     
  Lines         9628    9613   -15     
=======================================
+ Hits          7946    7988   +42     
+ Misses        1682    1625   -57     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

Pull request overview

This PR fixes inconsistent TP/FP matching across detection metrics by replacing the np.unique-based deduplication in _match_detection_batch with a greedy one-pass matcher (aligning behavior with the existing greedy matcher used elsewhere in the metrics module) to prevent valid TP assignments from being dropped (issue #2378).

Changes:

  • Replace np.unique two-pass deduplication with greedy IoU-desc matching in _match_detection_batch across Recall, Precision, F1Score, MeanAverageRecall, and (deprecated) metrics.detection.MeanAveragePrecision.
  • Add regression tests covering the reported “two valid pairs” IoU-matrix case for Recall, Precision, F1Score, and MeanAverageRecall.

Review scores (n/5):

  • Code quality: 4/5
  • Testing: 3/5
  • Docs: 4/5

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/supervision/metrics/recall.py Switch _match_detection_batch to greedy matching to avoid dropping valid TP pairs.
src/supervision/metrics/precision.py Same greedy matching fix applied to Precision.
src/supervision/metrics/f1_score.py Same greedy matching fix applied to F1Score.
src/supervision/metrics/mean_average_recall.py Same greedy matching fix applied to MeanAverageRecall.
src/supervision/metrics/detection.py Same greedy matching fix applied to deprecated MeanAveragePrecision._match_detection_batch.
tests/metrics/test_recall.py Adds regression test reproducing #2378 matching failure for Recall.
tests/metrics/test_precision.py Adds regression test reproducing #2378 matching failure for Precision.
tests/metrics/test_f1_score.py Adds regression test reproducing #2378 matching failure for F1Score.
tests/metrics/test_mean_average_recall.py Adds regression test reproducing #2378 matching failure for MeanAverageRecall.

Comment thread src/supervision/metrics/recall.py Outdated
Comment thread src/supervision/metrics/precision.py Outdated
Comment thread src/supervision/metrics/f1_score.py Outdated
Comment thread src/supervision/metrics/mean_average_recall.py Outdated
Comment thread src/supervision/metrics/detection.py Outdated
Comment thread src/supervision/metrics/detection.py Outdated
… test

- Add kind='stable' to np.argsort in _match_detection_batch across all 5 metric
  implementations (Recall, Precision, F1Score, MeanAverageRecall, deprecated
  MeanAveragePrecision) to ensure deterministic TP assignment when IoU values tie
- Add test_greedy_matching_two_valid_pairs to TestDetectionMetrics covering the
  deprecated MeanAveragePrecision._match_detection_batch with the issue #2378 IoU
  matrix, closing the one missing regression test flagged by /review

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
@Borda Borda added the bug Something isn't working label Jul 1, 2026
Borda and others added 2 commits July 1, 2026 12:24
…cation

Move the repeated 11-line greedy IoU matching block from all five
_match_detection_batch implementations into a single shared private helper
at supervision/metrics/utils/matching.py. Each call site reduces to two
lines; empty-indices fast-path is handled by the generator yielding nothing.

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
@Borda
Borda merged commit 4624dde into develop Jul 1, 2026
26 checks passed
@Borda
Borda deleted the fix/2378 branch July 1, 2026 10:30
@Borda Borda mentioned this pull request Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: why do Recall / F1Score use a different matching algorithm than ConfusionMatrix

2 participants