TAI BUI
Back to curriculum
BuildPython

Multi-Object Tracking & Video Memory

Tracking is detection plus association: detect every frame, then match this frame's detections to last frame's tracks by ID.

TIME: ~60 minutesPREREQUISITES: Phase 4 Lesson 06 (YOLO Detection), Phase 4 Lesson 08 (Mask R-CNN), Phase 4 Lesson 24 (SAM 3)

Learning Objectives

  • Distinguish tracking-by-detection from query-based tracking and name the algorithm families (SORT, DeepSORT, ByteTrack, BoT-SORT, SAM 2 memory tracker, SAM 3.1 Object Multiplex)
  • Implement IoU + Hungarian assignment from scratch for classic tracking-by-detection
  • Explain SAM 2's memory bank and why it handles occlusion better than IoU-based association
  • Read the three tracking metrics (MOTA, IDF1, HOTA) and pick which one matters for a given use case

The Problem

A detector finds objects in a single frame. A tracker figures out which detection in frame t is the same object as a detection in frame t-1. Skip that step and you cannot count objects crossing a line, follow a ball through an occlusion, or say "car #4 has been in the lane for 8 seconds" — all you have is a stack of unrelated snapshots.

Tracking sits underneath almost every video-facing product: sports analytics, surveillance, autonomous driving, medical video analysis, wildlife monitoring, footfall counting. The building blocks repeat across all of them: a per-frame detector, a motion model (a Kalman filter or something richer), an association step (Hungarian assignment on IoU, cosine similarity, or learned features), and a track lifecycle that governs how tracks get born, updated, and killed.

2026 added two new patterns on top of that classical stack. SAM 2 tracks by feature memory instead of a motion model. SAM 3.1 Object Multiplex shares one memory bank across many instances of the same concept. This lesson works through the classical stack first, then the memory-based approach.

Warm-up0/3
1. A security camera shows someone walk behind a pillar for two seconds, then reappear on the other side. If a system only runs an object detector on each frame independently, with no tracking logic at all, what is the obvious gap?Warm-up
A camera view split by a pillar in the middle, with a person detected in frame 10 on the left side and a person detected in frame 40 on the right side, and a dashed question mark box between the two asking whether they are the same person.
Illustrative pair of frames; the detector runs on each frame alone.
2. In object detection, IoU (Intersection over Union) measures how much two bounding boxes overlap, from 0 (no overlap) to 1 (identical boxes). If a box detected in frame 2 has high IoU with a box from frame 1, what does that most likely suggest?Warm-up
A dashed rectangle from frame 1 and a solid rectangle from frame 2 drawn on the same blank background, overlapping almost completely, with no IoU number printed.
Illustrative pair of boxes; no ratio is computed on screen.
3. A classifier has precision 0.5 and recall 1.0. What is its F1 score?Warm-up
A card showing one classifier's precision as 0.50 and recall as 1.00, next to a dashed box asking for its F1 score.
Values given directly by the question.

The Concept

Tracking-by-detection

Mermaid diagram

Every tracker you'll run into in 2026 is a variation on this loop. What changes between them:

  • SORT (2016): Kalman filter plus IoU Hungarian matching. Simple, fast, no appearance model.
  • DeepSORT (2017): SORT plus a CNN-based appearance feature per track, a ReID embedding, which handles crossings better.
  • ByteTrack (2021): matches low-confidence detections as a second stage. No appearance features needed, and still the top performer on MOT17.
  • BoT-SORT (2022): ByteTrack plus camera motion compensation plus ReID.
  • StrongSORT and OC-SORT: ByteTrack descendants with stronger motion and appearance modeling.

Query-based tracking: the other paradigm

Tracking-by-detection always keeps detection and association as two separate, bolted-together steps. Query-based tracking collapses them into one model. MOTR and its successors (MOTRv2, MOTRv3) represent each tracked object as a learned query token that gets refined frame to frame inside a transformer, so the same forward pass that detects new objects also carries forward the identity of ones already being tracked. No external Hungarian matching, no hand-tuned IoU threshold. The network learns the association implicitly, in the same spirit as SAM 2's memory bank, but trained end to end for detection and tracking jointly rather than added on top of a segmenter.

Kalman filter in one paragraph

A Kalman filter maintains a per-track state, (x, y, w, h, dx, dy, dw, dh), plus a covariance that tracks how much to trust it. Each frame it predicts the new state from a constant-velocity model, then updates that prediction with the matched detection, weighting the detection more heavily when the prediction's uncertainty is high. The result is smooth trajectories and tracks that can survive a short occlusion, one to five frames, without losing their identity.

Every classical tracker leans on a Kalman filter for this motion-prediction step.

The Hungarian algorithm

Given an M x N cost matrix (tracks by detections), the Hungarian algorithm finds the one-to-one assignment that minimises total cost. Cost is usually 1 - IoU(track_bbox, detection_bbox), or negative cosine similarity between appearance features. Runtime is O((M+N)^3), fast enough in Python via scipy.optimize.linear_sum_assignment for M and N up to roughly 1000.

ByteTrack's key idea

Standard trackers throw away low-confidence detections (below roughly 0.5). ByteTrack keeps them as second-stage candidates: after matching tracks against high-confidence detections, whatever's left unmatched gets a second pass against the low-confidence pool, with a slightly looser IoU threshold. That second pass is what recovers short occlusions and prevents ID switches near crowds.

SAM 2 memory-based tracking

SAM 2 handles video by keeping a memory bank of per-instance spatio-temporal features. Give it a prompt, a click, a box, or text, on one frame, and it encodes that instance into memory. On every later frame, the memory cross-attends against the new frame's features, and the decoder produces a mask for the same instance right there.

No Kalman filter. No Hungarian assignment. The association happens implicitly, inside the memory-attention operation itself.

Pros:

  • Survives large occlusions, since the memory carries instance identity across many frames.
  • Open-vocabulary once combined with SAM 3's text prompts.
  • No separate motion model required.

Cons:

  • Slower than ByteTrack once you're tracking many objects.
  • The memory bank grows over time and eats into the context window.

SAM 3.1 Object Multiplex

Earlier SAM 2 and SAM 3 tracking kept one memory bank per instance: track 50 objects, maintain 50 memory banks. Object Multiplex (March 2026) collapses all of them into a single shared memory with per-instance query tokens, so cost scales sub-linearly with the number of instances instead of linearly.

That's what makes Multiplex the default for 2026 crowd tracking: concert crowds, warehouse workers, traffic intersections, anywhere the object count used to be the bottleneck.

Three metrics to know

  • MOTA (Multi-Object Tracking Accuracy): 1 - (FN + FP + ID switches) / GT, weighted by error type. A single number that conflates detection and association failures, so it can hide exactly the problem you care about.
  • IDF1 (ID F1): the harmonic mean of ID precision and recall. It answers "did each ground-truth track keep its ID over time," which makes it the better read than MOTA whenever ID switches are what worries you.
  • HOTA (Higher Order Tracking Accuracy): splits into detection accuracy (DetA) and association accuracy (AssA) instead of collapsing everything into one score. The community standard since 2020, and the most complete of the three.

Report IDF1 for surveillance, where the question is who is who. Reach for HOTA everywhere else: sports analytics (counting passes) and general academic comparison both call for it.

Check: The Concept0/22
1. What is tracking-by-detection?Define
Last frame's tracks on the left and this frame's fresh detections on the right, with an unresolved question mark box between them and a note asking what happens in that middle box.
Illustrative single frame-to-frame step.
2. What does the Hungarian algorithm do in tracking-by-detection?Define
A three by three table of costs between track A, B and C and detections 1, 2 and 3, noted as 1 minus IoU for each pair, next to a dashed box asking what the algorithm's output is.
Costs computed in the figure script from three track boxes and three detection boxes.
3. What does a Kalman filter do at each frame in a classical tracker?Define
One track's state vector of position, size and velocity plus a covariance, above a single unresolved question-mark box standing for the filter's work on that state this frame.
State fields are quoted from the lesson; no numeric instance is shown.
4. What is ByteTrack's distinguishing contribution?Define
Five detection boxes from one frame, each drawn around a person icon and labelled with its confidence score, from 0.93 down to 0.21, next to a dashed box asking what ByteTrack contributes.
Illustrative confidence scores, typed not captured; the boxes stand for one frame's raw detections.
5. What does DeepSORT add to SORT?Define
One track card holding only a bounding box and velocity, above a dashed box asking what else might be added to it.
Illustrative single track; no appearance data shown yet.
6. In SAM 2, what is the memory bank?Define
A click prompt on one instance in frame 1 flowing into an unresolved memory box, then on to frame 50 with a question of whether it is still the same instance, and a note that the prompt is given only once.
Illustrative single-instance prompt.
7. What does the MOTA (Multi-Object Tracking Accuracy) formula compute?Define
Five frames side by side, each listing three ground-truth objects on the left and the track ids matched to them on the right, with one chip missing and one extra, next to a dashed box asking for MOTA.
Illustrative five-frame run; the chips are the figure's own worked example.
8. How does HOTA (Higher Order Tracking Accuracy) differ structurally from MOTA?Define
A card saying MOTA reports one score for the whole video, next to an unresolved dashed box for what HOTA reports, with a note that both read the same tracker output against the same ground truth.
Both metrics score the same run; HOTA's own shape is left unresolved.
9. What is Object Multiplex in SAM 3.1?Define
Six small person icons standing for many tracked instances of one concept, next to a dashed box asking what Object Multiplex is.
Illustrative small crowd; a real crowd is much larger.
10. Which lineage correctly traces the tracker algorithm family as the lesson lays it out?Define
Four tracker name boxes, BoT-SORT, SORT, ByteTrack and DeepSORT, shown side by side in a shuffled order with no years attached, next to a note asking for the correct release order.
Names only; no chronological order is implied by this layout.
11. Why can a track with a Kalman filter survive a short occlusion?Explain
A track's centre moving steadily along a line across frames 0 to 2, next to a dashed region marked frames 3 and 4 with no detection matched, holding a dashed box that asks what the track's state is through that gap.
Illustrative steady motion; positions are drawn by hand.
12. Why does ByteTrack keep low-confidence detections instead of discarding them?Explain
Three detection boxes in one crowded frame scored 0.92, 0.88 and 0.31, where the 0.31 box is a person standing half behind another, next to a dashed box asking why ByteTrack keeps that low-scoring box.
Illustrative crowd scene and scores; the people are drawn by hand.
13. Two people cross paths and their boxes overlap heavily. Why does DeepSORT cope better than SORT?Explain
Two people close together with their dashed bounding boxes, labelled track 1 and track 2, covering much of the same area while they cross.
Illustrative crossing scene; the overlap is drawn by hand.
14. How does a query-based tracker like MOTR differ from tracking-by-detection pipelines such as SORT, ByteTrack and BoT-SORT?Explain
A card listing tracking-by-detection as two separate steps, detect then associate, next to a dashed box asking how a query-based tracker works instead.
Quoted stage names from the lesson's own tracking-by-detection loop.
15. How does SAM 2's memory-based tracker avoid explicit Hungarian-style association?Explain
A memory bank card above a new frame's features card, with an unresolved question mark box between them and a note that no Hungarian step runs in that gap.
Illustrative single frame against an already-populated memory bank.
16. Why does a single shared memory with per-instance query tokens (Object Multiplex) help with crowds?Explain
A row of twelve person icons standing for many instances of one class, above a card restating the design as one shared memory with one query token per instance, next to a dashed box asking what that buys.
Illustrative crowd; the design named on the card is the one the question gives.
17. Why can MOTA hide the problem you care about when ID switches are the worry?Explain
Two cards side by side, a MOTA score of 0.81 described as looking solid and a separate count of 15 ID switches over the same video, next to a dashed box asking whether both numbers can be true at once.
Illustrative pair of numbers, typed not captured.
18. For surveillance video where keeping each person's ID consistent is the primary requirement, which metric should you report?Analyze
A requirements card for a surveillance camera stating the goal as keeping each person's own ID correct over time, next to a dashed box asking for the best metric, with no metric names listed.
Scenario quoted from the question's own wording.
19. Tracking 50 people with SAM 2 gets slower roughly in proportion to the count. What causes this, and what addresses it?Analyze
A bar chart of SAM 2's relative tracking time at 10, 50 and 100 people, rising roughly in proportion to the count, with no cause labeled yet.
Illustrative relative timings, typed not captured; shape matches the question's own roughly proportional claim.
20. A tracker scores well on MOTA, yet reviewers see two people swap IDs every time they cross. What explains this, and what should you check?Analyze
Two people before and after crossing paths, with their id colors swapped after the crossing, next to a card reporting a MOTA score of 0.84 described as looking good.
Illustrative crossing scenario and score, typed not captured.
21. SAM 2 follows a person through a long occlusion well, but slows down as a long video runs. What is the most likely cause?Analyze
A line chart of time per frame against frame number for one tracked person over a long video, climbing steadily as more frames pass, with no cause labelled.
Illustrative relative timings, typed not captured; shape matches the question's own claim that it slows down as the video runs.
22. You are evaluating trackers for sports analytics, where counting passes depends on both finding players and keeping them distinct. Which metric fits best?Design
A requirements card for sports analytics stating the need to find every player and keep each one distinct, next to a dashed box asking for the best metric, with no metric names listed.
Scenario quoted from the question's own wording.

Build It

Step 1: IoU-based cost matrix

python
import numpy as np
 
 
def bbox_iou(a, b):
    """
    a, b: (N, 4) arrays of [x1, y1, x2, y2].
    Returns (N_a, N_b) IoU matrix.
    """
    ax1, ay1, ax2, ay2 = a[:, 0], a[:, 1], a[:, 2], a[:, 3]
    bx1, by1, bx2, by2 = b[:, 0], b[:, 1], b[:, 2], b[:, 3]
    inter_x1 = np.maximum(ax1[:, None], bx1[None, :])
    inter_y1 = np.maximum(ay1[:, None], by1[None, :])
    inter_x2 = np.minimum(ax2[:, None], bx2[None, :])
    inter_y2 = np.minimum(ay2[:, None], by2[None, :])
    inter = np.clip(inter_x2 - inter_x1, 0, None) * np.clip(inter_y2 - inter_y1, 0, None)
    area_a = (ax2 - ax1) * (ay2 - ay1)
    area_b = (bx2 - bx1) * (by2 - by1)
    union = area_a[:, None] + area_b[None, :] - inter
    return inter / np.clip(union, 1e-8, None)

Step 2: Minimal SORT-style tracker

This version skips the constant-velocity Kalman step and matches on IoU alone, which keeps the code short enough to read in one sitting. Production trackers need the Kalman predict; grab the full version from the sort Python package when you do.

python
from scipy.optimize import linear_sum_assignment
 
 
class Track:
    def __init__(self, tid, bbox, frame):
        self.id = tid
        self.bbox = bbox
        self.last_frame = frame
        self.hits = 1
 
    def update(self, bbox, frame):
        self.bbox = bbox
        self.last_frame = frame
        self.hits += 1
 
 
class SimpleTracker:
    def __init__(self, iou_threshold=0.3, max_age=5):
        self.tracks = []
        self.next_id = 1
        self.iou_threshold = iou_threshold
        self.max_age = max_age
 
    def step(self, detections, frame):
        if not self.tracks:
            for d in detections:
                self.tracks.append(Track(self.next_id, d, frame))
                self.next_id += 1
            return [(t.id, t.bbox) for t in self.tracks]
 
        track_boxes = np.array([t.bbox for t in self.tracks])
        det_boxes = np.array(detections) if len(detections) else np.empty((0, 4))
 
        iou = bbox_iou(track_boxes, det_boxes) if len(det_boxes) else np.zeros((len(track_boxes), 0))
        cost = 1 - iou
        cost[iou < self.iou_threshold] = 1e6
 
        matched_track = set()
        matched_det = set()
        if cost.size > 0:
            row, col = linear_sum_assignment(cost)
            for r, c in zip(row, col):
                if cost[r, c] < 1.0:
                    self.tracks[r].update(det_boxes[c], frame)
                    matched_track.add(r); matched_det.add(c)
 
        for i, d in enumerate(det_boxes):
            if i not in matched_det:
                self.tracks.append(Track(self.next_id, d, frame))
                self.next_id += 1
 
        self.tracks = [t for t in self.tracks if frame - t.last_frame <= self.max_age]
        return [(t.id, t.bbox) for t in self.tracks]

Sixty lines, and that's the whole contract: feed it per-frame detections, get back per-frame track IDs. A real system layers the Kalman predict, ByteTrack's second-stage re-match, and appearance features on top of this same core.

Step 3: Synthetic trajectory test

python
def synthetic_frames(num_frames=20, num_objects=3, H=240, W=320, seed=0):
    rng = np.random.default_rng(seed)
    starts = rng.uniform(20, 200, size=(num_objects, 2))
    velocities = rng.uniform(-5, 5, size=(num_objects, 2))
    frames = []
    for f in range(num_frames):
        dets = []
        for i in range(num_objects):
            cx, cy = starts[i] + f * velocities[i]
            dets.append([cx - 10, cy - 10, cx + 10, cy + 10])
        frames.append(dets)
    return frames
 
 
tracker = SimpleTracker()
for f, dets in enumerate(synthetic_frames()):
    tracks = tracker.step(dets, f)

Three objects moving in straight lines should keep the same ID across all 20 frames.

Step 4: ID-switch metric

python
def count_id_switches(tracks_per_frame, gt_per_frame):
    """
    tracks_per_frame:  list of list of (track_id, bbox)
    gt_per_frame:      list of list of (gt_id, bbox)
    Returns number of ID switches.
    """
    prev_assignment = {}
    switches = 0
    for tracks, gts in zip(tracks_per_frame, gt_per_frame):
        if not tracks or not gts:
            continue
        t_boxes = np.array([b for _, b in tracks])
        g_boxes = np.array([b for _, b in gts])
        iou = bbox_iou(g_boxes, t_boxes)
        for g_idx, (gt_id, _) in enumerate(gts):
            j = iou[g_idx].argmax()
            if iou[g_idx, j] > 0.5:
                t_id = tracks[j][0]
                if gt_id in prev_assignment and prev_assignment[gt_id] != t_id:
                    switches += 1
                prev_assignment[gt_id] = t_id
    return switches

Think of this as an IDF1-adjacent metric, simplified: it counts how many times a ground-truth object's assigned track ID changes. For the real MOTA, IDF1, and HOTA implementations, reach for py-motmetrics and TrackEval instead.

Check: Build It0/7
1. In `SimpleTracker.step`, why is `cost` set to 1e6 wherever IoU is below the threshold?Explain
A two by two table of two tracks against two detections, each cell holding an IoU and the cost of 1 minus that IoU, with iou_threshold 0.3 noted above, next to a dashed box asking which cells become cost 1e6.
IoU values computed in the figure script from a worked example of two track boxes and two detection boxes.
2. In the lesson's `SimpleTracker.step`, what determines whether a track is removed after a frame?Apply
One track's last matched frame followed by five frames in a row with no match, next to a dashed box asking when the track is removed.
Illustrative run of unmatched frames; no specific max_age value is shown.
3. `bbox_iou` is called with a = [[0, 0, 10, 10]] and b = [[5, 0, 15, 10]], both as [x1, y1, x2, y2]. What does it return?Apply
Two rectangles a and b, a at [0,0,10,10] and b at [5,0,15,10], drawn to scale with their overlap region visible but no numeric IoU shown.
Coordinates from the question; drawn to a shared scale, no ratio computed on screen.
4. A `SimpleTracker` with the default `iou_threshold=0.3` holds one track at [0, 0, 10, 10] and receives one detection at [5, 0, 15, 10] on the next frame. What happens?Apply
An existing track box and a new detection box drawn to scale with their overlap visible, the default iou_threshold of 0.3 noted below, and a dashed box asking for the outcome, with no overlap ratio printed.
Coordinates and threshold come from the question; no ratio is computed on screen.
5. A `SimpleTracker` with `max_age=5` last matched a track on frame 10 and never matches it again. On which frame is it first removed?Apply
A row of frame numbers from 10 to 18, frame 10 marked as the last match and every later frame marked with its gap since then, next to a dashed box asking at which frame the track is first removed.
Frame numbers and max_age of 5 come directly from the question.
6. In `count_id_switches`, ground-truth object 7 overlaps track 2 (IoU above 0.5) in frames 0 to 4, then overlaps track 3 (IoU above 0.5) in frames 5 to 9. What is the count for this object?Apply
Ten frames in a row, each showing which track id object 7 matched, track 2 for the first five frames and track 3 for the next five, next to a dashed box asking how many ID switches are recorded.
Matched track ids come directly from the question's own sequence.
7. A person is hidden for 3 frames and moves several box widths meanwhile. `SimpleTracker` has `max_age=5`, yet it issues a new ID when the person returns. Which missing piece explains this?Analyze
A timeline of frame 20 with id 7, three hidden frames, then frame 24 where the person reappears and gets a new id instead of id 7, next to a dashed box asking what is missing.
Timeline matches the question's own frame counts; the new-id outcome is given directly by the stem.

Use It

What's actually running in production in 2026:

  • ultralytics: YOLOv8 with ByteTrack or BoT-SORT built in. results = model.track(source, tracker="bytetrack.yaml"). This is the default most teams reach for first.
  • supervision (Roboflow): ByteTrack wrappers plus annotation utilities.
  • SAM 2 / SAM 3.1: memory-based tracking via processor.track().
  • A custom stack: a detector (YOLOv8 or RT-DETR) paired with sort-tracker, OC-SORT, or StrongSORT.

How to pick:

  • Pedestrians, cars, or boxes at 30+ fps: ByteTrack with ultralytics.
  • Many instances of one class in a crowd: SAM 3.1 Object Multiplex.
  • Heavy occlusions with identifiable appearance: DeepSORT or StrongSORT, using ReID features.
  • Sports or other complex interactions: BoT-SORT, or a learned tracker like MOTRv3.
Check: Use It0/3
1. For 'heavy occlusions with identifiable appearance', which tracker does the lesson's picking guidance recommend?Design
A requirements card stating heavy occlusions with objects that still look visually distinct, next to a dashed box asking for the best tracker, with no tracker names listed.
Scenario quoted from the question's own wording.
2. You track pedestrians at 30 fps with a fixed camera and want the fastest, cheapest option that keeps IDs reasonably stable. Which design fits?Design
A requirements card for pedestrians at 30 or more frames per second on a fixed camera, wanting the fastest and cheapest option with reasonably stable ids, next to a dashed box asking for the best design, with no tool names listed.
Scenario quoted from the question's own wording.
3. You must track 200 warehouse workers by text prompt ('worker'), and cost must not grow linearly with headcount. Which design fits?Design
A requirements card for 200 warehouse workers tracked by the text prompt worker, with cost required to not grow linearly with headcount, next to a dashed box asking for the best design, with no tool names listed.
Scenario quoted from the question's own wording.

Ship It

This lesson ships two reusable outputs:

  • outputs/prompt-tracker-picker.md: picks between SORT, ByteTrack, BoT-SORT, SAM 2, and SAM 3.1 given scene type, occlusion patterns, and latency budget.
  • outputs/skill-mot-evaluator.md: writes a full evaluation harness for MOTA, IDF1, and HOTA against ground-truth tracks.
Author's Note

The instinct in 2026 is to reach for SAM 2's memory bank because it sounds like the more sophisticated tool, and for crowds it usually is. But most tracking problems I've run into in production are still 20-30 boxes at 30 fps with a mostly fixed camera, and for that ByteTrack with a plain Kalman filter is faster, cheaper, and just as accurate. Reach for memory-based tracking when occlusion is the actual failure mode you're fighting — not because it's the newer name on the paper.

Exercises

  1. (Easy) Run the synthetic tracker above with 3, 10, and 30 objects. Report the ID-switch count for each, and pin down where plain IoU-only association starts to break.
  2. (Medium) Add a constant-velocity Kalman predict step before association. Show that short occlusions (2-3 frames) no longer cause ID switches.
  3. (Hard) Swap in SAM 2's memory-based tracker (via transformers) as an alternative backend. Run both SimpleTracker and SAM 2 on a 30-second clip of a crowd, manually label ground-truth IDs for 5 salient people, and compare ID-switch counts between the two.

Key Terms

TermWhat people sayWhat it actually means
Tracking-by-detection"Detect then associate"Per-frame detector + Hungarian assignment on IoU / appearance
Kalman filter"Motion predict"Linear dynamics + covariance for smooth track predictions and occlusion handling
Hungarian algorithm"Optimal assignment"Solves the minimum-cost bipartite matching problem; scipy.optimize.linear_sum_assignment
ByteTrack"Low-confidence second pass"Re-match unmatched tracks to low-confidence detections to recover short occlusions
DeepSORT"SORT + appearance"Adds a ReID feature for cross-frame matching; better for ID preservation
Memory bank"SAM 2 trick"Per-instance spatio-temporal features stored across frames; cross-attention replaces explicit association
Object Multiplex"SAM 3.1 shared memory"Single shared memory with per-instance queries for fast many-object tracking
HOTA"Modern tracking metric"Decomposes into detection and association accuracy; community standard

Further Reading