Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/trackers/eval/clear.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ def compute_clear_metrics(
"CLR_Frames": num_frames,
}

gt_contiguous = unique_gt_ids.dtype.kind in "iu" and unique_gt_ids[0] == 0 and unique_gt_ids[-1] == num_gt_ids - 1

# Initialize counters
clr_tp = 0
clr_fn = 0
Expand All @@ -176,8 +178,8 @@ def compute_clear_metrics(

# Process each timestep
for t, (gt_ids_t, tracker_ids_t) in enumerate(zip(gt_ids, tracker_ids)):
# Map GT IDs to indices using searchsorted (vectorized)
gt_indices_t = np.atleast_1d(np.searchsorted(unique_gt_ids, gt_ids_t))
# Map GT IDs directly or use the searchsorted fallback.
gt_indices_t = np.atleast_1d(gt_ids_t if gt_contiguous else np.searchsorted(unique_gt_ids, gt_ids_t))

# Handle empty frames
if len(gt_ids_t) == 0:
Expand Down
23 changes: 15 additions & 8 deletions src/trackers/eval/hota.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,17 @@ def compute_hota_metrics(
num_tracker_ids = len(unique_tracker_ids)

# `unique_gt_ids` / `unique_tracker_ids` are sorted (np.unique returns sorted
# output), so an id's row/column index is simply its position found by binary
# search. This replaces per-frame Python dict lookups in the hot loops below.
# output). Prepared zero-based integer IDs can be used as indices directly.
# Other ID layouts keep the binary-search mapping used by public callers.
# The check is done once before both passes.
# Precondition: all per-frame IDs are present in unique_*_ids (guaranteed —
# unique arrays are built from concatenation of all frames).
gt_contiguous = unique_gt_ids.dtype.kind in "iu" and unique_gt_ids[0] == 0 and unique_gt_ids[-1] == num_gt_ids - 1
tracker_contiguous = (
unique_tracker_ids.dtype.kind in "iu"
and unique_tracker_ids[0] == 0
and unique_tracker_ids[-1] == num_tracker_ids - 1
)

# Variables for global association (ref: hota.py:48-50)
potential_matches_count: np.ndarray = np.zeros((num_gt_ids, num_tracker_ids), dtype=np.float64)
Expand All @@ -145,15 +152,15 @@ def compute_hota_metrics(
if len(gt_ids_t) == 0 or len(tracker_ids_t) == 0:
# Still count IDs even if no matches possible
if len(gt_ids_t) > 0:
gt_indices = np.searchsorted(unique_gt_ids, gt_ids_t)
gt_indices = gt_ids_t if gt_contiguous else np.searchsorted(unique_gt_ids, gt_ids_t)
gt_id_count[gt_indices] += 1
if len(tracker_ids_t) > 0:
tr_indices = np.searchsorted(unique_tracker_ids, tracker_ids_t)
tr_indices = tracker_ids_t if tracker_contiguous else np.searchsorted(unique_tracker_ids, tracker_ids_t)
tracker_id_count[0, tr_indices] += 1
continue

gt_indices = np.searchsorted(unique_gt_ids, gt_ids_t)
tr_indices = np.searchsorted(unique_tracker_ids, tracker_ids_t)
gt_indices = gt_ids_t if gt_contiguous else np.searchsorted(unique_gt_ids, gt_ids_t)
tr_indices = tracker_ids_t if tracker_contiguous else np.searchsorted(unique_tracker_ids, tracker_ids_t)

similarity = similarity_scores[t]

Expand Down Expand Up @@ -188,8 +195,8 @@ def compute_hota_metrics(
hota_fn += len(gt_ids_t)
continue

gt_indices = np.searchsorted(unique_gt_ids, gt_ids_t)
tr_indices = np.searchsorted(unique_tracker_ids, tracker_ids_t)
gt_indices = gt_ids_t if gt_contiguous else np.searchsorted(unique_gt_ids, gt_ids_t)
tr_indices = tracker_ids_t if tracker_contiguous else np.searchsorted(unique_tracker_ids, tracker_ids_t)

similarity = similarity_scores[t]

Expand Down
18 changes: 13 additions & 5 deletions src/trackers/eval/identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,15 @@ def compute_identity_metrics(
num_tracker_ids = len(unique_tracker_ids)

# `np.unique` sorts the IDs, and every per-frame ID is included in those
# arrays, so searchsorted maps all IDs directly to their global indices.
# arrays. Prepared zero-based integer IDs can be used as indices directly.
# Other ID layouts retain searchsorted, preserving the public-call fallback.
# The check is performed once before entering the frame loop.
gt_contiguous = unique_gt_ids.dtype.kind in "iu" and unique_gt_ids[0] == 0 and unique_gt_ids[-1] == num_gt_ids - 1
tracker_contiguous = (
unique_tracker_ids.dtype.kind in "iu"
and unique_tracker_ids[0] == 0
and unique_tracker_ids[-1] == num_tracker_ids - 1
)

# Variables for global association (ref: identity.py:48-50)
potential_matches_count = np.zeros((num_gt_ids, num_tracker_ids))
Expand All @@ -121,15 +129,15 @@ def compute_identity_metrics(
if len(gt_ids_t) == 0 or len(tracker_ids_t) == 0:
# Still count IDs even if no matches possible
if len(gt_ids_t) > 0:
gt_indices = np.searchsorted(unique_gt_ids, gt_ids_t)
gt_indices = gt_ids_t if gt_contiguous else np.searchsorted(unique_gt_ids, gt_ids_t)
gt_id_count[gt_indices] += 1
if len(tracker_ids_t) > 0:
tr_indices = np.searchsorted(unique_tracker_ids, tracker_ids_t)
tr_indices = tracker_ids_t if tracker_contiguous else np.searchsorted(unique_tracker_ids, tracker_ids_t)
tracker_id_count[tr_indices] += 1
continue

gt_indices = np.searchsorted(unique_gt_ids, gt_ids_t)
tr_indices = np.searchsorted(unique_tracker_ids, tracker_ids_t)
gt_indices = gt_ids_t if gt_contiguous else np.searchsorted(unique_gt_ids, gt_ids_t)
tr_indices = tracker_ids_t if tracker_contiguous else np.searchsorted(unique_tracker_ids, tracker_ids_t)

similarity = similarity_scores[t]

Expand Down
Loading