Skip to content

fix(io): reject duplicate per-frame track ids - #562

Draft
AlexBodner wants to merge 1 commit into
developfrom
fix/mot-duplicate-id-guard
Draft

fix(io): reject duplicate per-frame track ids#562
AlexBodner wants to merge 1 commit into
developfrom
fix/mot-duplicate-id-guard

Conversation

@AlexBodner

Copy link
Copy Markdown
Collaborator

Problem

CLEAR, HOTA and Identity all accumulate per-id counts with fancy-indexed +=, which NumPy buffers instead of adding:

counts = np.zeros(3)
counts[np.array([1, 1, 2])] += 1  # [0, 1, 1] — index 1 was written once, not twice

A track id repeated inside one frame therefore deflates the very totals the metrics divide by, and every reported number comes back quietly wrong.

Fix

_prepare_mot_sequence rejects a repeated id. TrackEval raises on the same input, so this matches the reference.

The check runs after filtering, so the two legitimate cases still pass:

  • ids on rows dropped as ignored or non-pedestrian
  • unconfirmed detections, which all share id -1 and are expected to repeat

Tests

  • Duplicate ground-truth ids and duplicate tracker ids in one frame both raise.
  • The same id across consecutive frames is normal tracking and still works.
  • Duplicate ids on filtered rows and repeated -1 ids are allowed.

tests/io, tests/eval and tests/tune pass at 138.

Notes

Guarding in sequence preparation covers all three metric families at one chokepoint rather than patching each accumulation site in clear.py, hota.py and identity.py. Switching those to np.add.at would make duplicates count correctly instead of rejecting them, but duplicate ids in a frame are malformed input, and TrackEval treats them as an error too.

Made with Cursor

CLEAR, HOTA and Identity all accumulate per-id counts with fancy-indexed
`+=`, which NumPy buffers instead of adding: `counts[[1, 1, 2]] += 1`
leaves 1 at index 1, not 2. A track id repeated inside one frame
therefore deflates the very totals the metrics divide by, and every
reported number comes back quietly wrong.

`_prepare_mot_sequence` now rejects a repeated id, matching TrackEval,
which raises on the same input. The check runs after filtering, so the
two legitimate cases still pass: ids on rows dropped as ignored or
non-pedestrian, and unconfirmed detections that all share id -1.

Guarding in sequence preparation covers all three metric families at one
chokepoint rather than patching each accumulation site.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Borda
Borda requested a balanced review from Copilot August 12, 2026 18:47

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

Rejects malformed MOT input containing duplicate per-frame track IDs before metric evaluation.

Changes:

  • Adds per-frame uniqueness validation after relevant filtering.
  • Adds coverage for duplicates, repeated cross-frame IDs, filtered IDs, and unconfirmed IDs.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/trackers/io/mot.py Validates unique ground-truth and tracker IDs per frame.
tests/io/test_mot.py Tests duplicate rejection and valid exceptions.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants