diff --git a/src/pyrecest/evaluation/tracking_metrics/_clear_identity.py b/src/pyrecest/evaluation/tracking_metrics/_clear_identity.py index 46f898a67..4f56f3147 100644 --- a/src/pyrecest/evaluation/tracking_metrics/_clear_identity.py +++ b/src/pyrecest/evaluation/tracking_metrics/_clear_identity.py @@ -11,6 +11,7 @@ from ._data import TrackingSequence, unit_interval_scalar _EPS = np.finfo(float).eps +_UNMATCHED_ID = -1 @dataclass(frozen=True) @@ -39,8 +40,8 @@ def evaluate_clear(data: TrackingSequence, *, threshold: float) -> ClearCounts: return ClearCounts(0, data.num_tracker_detections, 0, 0, 0.0) tp = fp = fn = switches = 0 motp_sum = 0.0 - previous_id = np.full(data.num_gt_ids, np.nan) - previous_timestep_id = np.full(data.num_gt_ids, np.nan) + previous_id = np.full(data.num_gt_ids, _UNMATCHED_ID, dtype=int) + previous_timestep_id = np.full(data.num_gt_ids, _UNMATCHED_ID, dtype=int) for gt_ids, tracker_ids, similarity in zip( data.gt_ids, data.tracker_ids, data.similarity_scores, strict=True ): @@ -60,9 +61,11 @@ def evaluate_clear(data: TrackingSequence, *, threshold: float) -> ClearCounts: matched_gt = gt_ids[match_rows] matched_tracker = tracker_ids[match_cols] previous = previous_id[matched_gt] - switches += int(np.sum((~np.isnan(previous)) & (matched_tracker != previous))) + switches += int( + np.sum((previous != _UNMATCHED_ID) & (matched_tracker != previous)) + ) previous_id[matched_gt] = matched_tracker - previous_timestep_id[:] = np.nan + previous_timestep_id[:] = _UNMATCHED_ID previous_timestep_id[matched_gt] = matched_tracker matches = len(match_rows) tp += matches diff --git a/tests/evaluation/test_tracking_metrics.py b/tests/evaluation/test_tracking_metrics.py index c98d47119..b98735415 100644 --- a/tests/evaluation/test_tracking_metrics.py +++ b/tests/evaluation/test_tracking_metrics.py @@ -82,6 +82,21 @@ def test_identity_switch_reduces_association_metrics() -> None: assert finalize_identity(identity_counts)["idf1"] == pytest.approx(0.5) +def test_clear_preserves_large_tracker_identity_precision() -> None: + large_tracker_id = 2**53 + 1 + data = _sequence( + [[0], [0]], + [[large_tracker_id], [large_tracker_id - 1]], + [[[1.0]], [[1.0]]], + num_gt_ids=1, + num_tracker_ids=large_tracker_id + 1, + ) + + counts = evaluate_clear(data, threshold=0.5) + + assert (counts.tp, counts.fp, counts.fn, counts.id_switches) == (2, 0, 0, 1) + + def test_hota_uses_multiple_localization_thresholds() -> None: data = _sequence( [[0]],