mot_metrics
sleap_nn.tracking.mot_metrics
¶
TrackEval-faithful multi-object tracking metrics: CLEAR-MOT, Identity and HOTA.
A pure numpy/scipy core (no sleap-io): it scores sequences of per-timestep
ground-truth ids, predicted ids and a ground-truth x prediction similarity
matrix. Turning sio.Labels into such sequences -- matching, don't-care ground
truth, frame alignment -- is the job of
:func:sleap_nn.evaluation.compute_mot_metrics.
The numbers reproduce TrackEval 1.3.0 (Luiten & Hoffhues; the reference
implementation of HOTA, and the MOTChallenge / KITTI evaluation code) exactly,
including its documented quirks, so they can be quoted next to published MOT
results. Output keys use TrackEval's own field names (MOTA, IDF1,
HOTA, CLR_TP, HOTA(0), ...).
Three metric families are computed:
- CLEAR-MOT (Bernardin & Stiefelhagen 2008): per-timestep Hungarian
matching at
similarity >= thresholdthat first keeps the previous timestep's matches (the "continuity bonus"), then maximizes similarity. Gives TP / FN / FP, ID switches, MOTA / MOTP / MODA, mostly-tracked / partly-tracked / mostly-lost and fragmentations. - Identity (Ristani et al. 2016): one global assignment between
ground-truth and predicted identities, maximizing the number of timesteps on
which the pair could match (
similarity >= threshold; every such pair counts, not only a one-to-one per-timestep matching). Gives IDTP / IDFN / IDFP and IDF1 / IDP / IDR. - HOTA (Luiten et al. 2021): detection, association and localization
accuracy over 19 localization thresholds
alpha = 0.05, 0.10, ..., 0.95(:data:HOTA_ALPHAS). Like TrackEval -- and unlike the paper's Eq. 15 -- one alpha-independent Hungarian matching per timestep maximizesglobal alignment score x similarity, and that single matching is then thresholded at each alpha. Array fields hold one value per alpha; the headline number quoted in papers is their mean.
TrackEval behaviors reproduced on purpose (each one changes a number):
- Continuity bonus looks one timestep back; ID switches look any distance back. Matching prefers the prediction matched to the same ground truth at the immediately previous timestep, while a switch is scored against the last prediction a ground truth was matched to, however long ago.
- Fragmentation (
Frag) is, per ground-truth id, the number of tracked segments minus one. A timestep with no prediction at all does NOT end a segment (TrackEval skips such timesteps without resetting the continuity state), while a timestep with only unrelated predictions does: a one-frame gap with nothing predicted givesFrag = 0, the same gap holding a single false positive givesFrag = 1. - Mostly tracked is strictly above 80 % of a trajectory's timesteps; mostly lost is below 20 %.
LocAis 1 where there are no true positives (TrackEval dividesmax(1e-10, sum) / max(1e-10, TP)).HOTA(0)/LocA(0)are the values at alpha = 0.05, the first threshold -- not at alpha = 0.- Empty sequences short-circuit. A sequence with no predictions (or no
ground truth) returns only the counts -- all ground truth missed (or all
predictions false) -- with every derived field left at 0 except
MLR = 1andLocA = 1, andCLR_Frames = 0. :func:combine_sequencesrecomputes the derived fields from the summed counts, as TrackEval'sCOMBINED_SEQrow does.
Ids must be unique within a timestep. They need not be contiguous: every
metric relabels them to 0..n-1 first (:func:relabel_contiguous).
Classes:
| Name | Description |
|---|---|
MOTSequence |
One scored sequence: per-timestep ids and similarities. |
Functions:
| Name | Description |
|---|---|
clear_metrics |
Compute the CLEAR-MOT metrics of one sequence, as TrackEval does. |
combine_sequences |
Combine per-sequence results into one, as TrackEval's |
hota_metrics |
Compute the HOTA metrics of one sequence, as TrackEval does. |
id_metrics |
Compute the identity metrics (IDF1, Ristani et al.) of one sequence. |
mot_metrics |
Compute CLEAR-MOT, Identity and HOTA for one sequence. |
relabel_contiguous |
Relabel ground-truth and predicted ids to contiguous |
MOTSequence
¶
One scored sequence: per-timestep ids and similarities.
A sequence is normally one video. Under global identities several videos
are concatenated into one sequence, with resets marking where each new
video starts.
Construction normalizes and validates every timestep: ids become 1-D int64
arrays, similarities (n_gt, n_pred) float arrays (an empty similarity
may be given in any shape), and resets a sorted tuple of unique ints.
Attributes:
| Name | Type | Description |
|---|---|---|
gt_ids |
List[ndarray]
|
One integer array of ground-truth ids per timestep. Ids are arbitrary integers, unique within a timestep. |
pred_ids |
List[ndarray]
|
One integer array of predicted ids per timestep, same rules. |
similarity |
List[ndarray]
|
One |
resets |
Tuple[int, ...]
|
Timestep indices at which CLEAR-MOT's frame-to-frame state is reset -- video boundaries when several videos are scored as one sequence. At a reset the continuity bonus and the ID-switch memory are cleared (a ground truth's first match in the new video is never a switch), and fragmentation is counted per side of the boundary (a trajectory's first segment after a reset is not a fragment). Identity and HOTA do not depend on timestep order and ignore it. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the three lists differ in length, a similarity matrix has the wrong shape or a non-finite value, ids are not integers, an id occurs twice in one timestep, or a reset is out of range. |
Methods:
| Name | Description |
|---|---|
__attrs_post_init__ |
Normalize and validate every timestep. |
Source code in sleap_nn/tracking/mot_metrics.py
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | |
__attrs_post_init__()
¶
Normalize and validate every timestep.
Source code in sleap_nn/tracking/mot_metrics.py
clear_metrics(seq, threshold=0.5)
¶
Compute the CLEAR-MOT metrics of one sequence, as TrackEval does.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seq
|
MOTSequence
|
The sequence to score. Ids are relabeled contiguously first. |
required |
threshold
|
float
|
Minimum similarity for a ground-truth/predicted pair to
match (TrackEval's |
0.5
|
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dict keyed by TrackEval's field names: the integer counts
|
Source code in sleap_nn/tracking/mot_metrics.py
combine_sequences(per_seq)
¶
Combine per-sequence results into one, as TrackEval's COMBINED_SEQ.
Counts are summed and every derived field recomputed from the sums -- so
the combined MOTA / IDF1 / DetA are detection-weighted, not means of the
per-sequence values. HOTA's association and localization scores (AssA,
AssRe, AssPr, LocA) are averages weighted by each sequence's
HOTA_TP. Combining one sequence recomputes its derived fields, which
differs from the per-sequence result only for an empty sequence (e.g. its
MOTA becomes -FP instead of 0).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
per_seq
|
Sequence[Dict[str, Any]]
|
Results of :func: |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
One dict with the same keys. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in sleap_nn/tracking/mot_metrics.py
hota_metrics(seq, alphas=HOTA_ALPHAS)
¶
Compute the HOTA metrics of one sequence, as TrackEval does.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seq
|
MOTSequence
|
The sequence to score. Ids are relabeled contiguously first. |
required |
alphas
|
Any
|
Localization thresholds. Default :data: |
HOTA_ALPHAS
|
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dict with one value per alpha (float arrays) for |
Source code in sleap_nn/tracking/mot_metrics.py
id_metrics(seq, threshold=0.5)
¶
Compute the identity metrics (IDF1, Ristani et al.) of one sequence.
Named to avoid clashing with :func:sleap_nn.evaluation.identity_metrics,
the older sleap-nn definitions (which differ from TrackEval, see
:class:sleap_nn.evaluation.IdentityMetrics).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seq
|
MOTSequence
|
The sequence to score. Ids are relabeled contiguously first. |
required |
threshold
|
float
|
Minimum similarity for a pair to count as a potential match. |
0.5
|
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dict with the integer counts |
Source code in sleap_nn/tracking/mot_metrics.py
mot_metrics(seq, threshold=0.5)
¶
Compute CLEAR-MOT, Identity and HOTA for one sequence.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seq
|
MOTSequence
|
The sequence to score. |
required |
threshold
|
float
|
Match threshold for CLEAR-MOT and Identity. HOTA has its
own thresholds (:data: |
0.5
|
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
The union of :func: |
Source code in sleap_nn/tracking/mot_metrics.py
relabel_contiguous(seq)
¶
Relabel ground-truth and predicted ids to contiguous 0..n-1.
Each side is relabeled independently, in sorted order of its original ids. Every metric needs this (TrackEval's datasets do it while loading): the per-id tables are indexed by id, so a gap in the ids would create a phantom trajectory -- counted as mostly lost, and a 0/0 in HOTA's alignment score.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seq
|
MOTSequence
|
The sequence to relabel. |
required |
Returns:
| Type | Description |
|---|---|
MOTSequence
|
A new :class: |