Multi object tracking

This page contains advanced examples for the tno.quantum.problems.mot package.

Requirements

Install the following dependencies to run the examples below:

pip install tno.quantum.problems.mot
pip install ultralytics

Example: getting detections with ultralytics

Starting from a video file, first generate a detection file compatible with ultralytics. This can be done, for example, with a pre-trained YOLO model, as shown below

 1import csv
 2from ultralytics import YOLO
 3
 4model = YOLO("path/to/model")
 5video_path = "path/to/video"
 6detections_csv_path = "path/to/output.csv"
 7
 8# Run inference frame-by-frame
 9results = model(
10    video_path,
11    stream=True,  # important for video
12    conf=0.25,
13)
14
15with open(detections_csv_path, mode="w", newline="") as f:
16    writer = csv.writer(f)
17    for frame_idx, r in enumerate(results):
18        if r.boxes is None:
19            continue
20
21        for box in r.boxes:
22            cls_id = int(box.cls[0])
23            conf = float(box.conf[0])
24            x, y, w, h = box.xywhn[0].tolist()
25
26            writer.writerow(
27                [
28                    frame_idx,
29                    cls_id,
30                    f"{x:.6f}",
31                    f"{y:.6f}",
32                    f"{w:.6f}",
33                    f"{h:.6f}",
34                    f"{conf:.6f}",
35                ]
36            )

Example: re-id module

The following example shows how to run the tracking algorithm of ultralytics and the re-id module on top to fix an ID switch.

 1import importlib.resources
 2import pandas as pd
 3
 4from ultralytics.trackers import BOTSORT
 5from ultralytics.utils import YAML, IterableSimpleNamespace
 6
 7from tno.quantum.problems.mot import (
 8    DetectionSequence,
 9    ultralytics_track,
10    FlowReID,
11)
12from tno.quantum.problems.mot.dataset import (
13    make_gif_with_detections,
14)
15
16# Load detections from CSV file
17detections_csv_path = "path/to/detections.csv"
18detections = pd.read_csv(detections_csv_path, header=None)
19detections = DetectionSequence.from_dataframe(detections)
20
21# Run ultralytics tracking algorithm
22tracker_path = str(
23    importlib.resources.files("ultralytics.cfg") / "trackers" / "botsort.yaml"
24)
25cfg = IterableSimpleNamespace(**YAML.load(tracker_path))
26tracker = BOTSORT(cfg)
27detections = ultralytics_track(detections, tracker)
28
29# Select a snippet that contains an ID loss
30flow_reid_obj = FlowReID()
31first_frame, last_frame = flow_reid_obj.pick_sequences(detections)[0]
32detections_snippet = detections[first_frame:last_frame]
33reid_detections_snippet = flow_reid_obj.run_classic(detections_snippet)
34
35# Create visualization
36video_path = "path/to/video"
37make_gif_with_detections(
38    detections_snippet,
39    input_filename=video_path,
40    output_filename="before.gif",
41    fps=20,
42    first_frame=first_frame,
43)
44make_gif_with_detections(
45    reid_detections_snippet,
46    input_filename=video_path,
47    output_filename="after.gif",
48    fps=20,
49    first_frame=first_frame,
50)

This results in the following video snippets.

Tracking before re-id. Tracking after re-id.

Instead of selecting a frame sequence manually, use the pick_sequences method to find frame sequences where an ID loss or ID switch is suspected.

 1import importlib.resources
 2
 3import pandas as pd
 4from ultralytics.trackers import BOTSORT
 5from ultralytics.utils import YAML, IterableSimpleNamespace
 6
 7from tno.quantum.problems.mot import (
 8    DetectionSequence,
 9    FlowReID,
10    ultralytics_track,
11)
12from tno.quantum.problems.mot.dataset import (
13    make_video_with_detections,
14)
15
16# Load detections from CSV file
17detections_csv_path = "path/to/detections.csv"
18detections = pd.read_csv(detections_csv_path, header=None)
19detections = DetectionSequence.from_dataframe(detections)
20
21# Run ultralytics tracking algorithm; pick_sequences needs track IDs
22tracker_path = str(
23    importlib.resources.files("ultralytics.cfg") / "trackers" / "botsort.yaml"
24)
25cfg = IterableSimpleNamespace(**YAML.load(tracker_path))
26detections = ultralytics_track(detections, BOTSORT(cfg))
27
28# Pick sequences
29flow_reid_obj = FlowReID()
30sequences = flow_reid_obj.pick_sequences(detections)
31print(sequences)
32print(f"Found {len(sequences)} frame sequences for re-id.")
33
34# To run re-id continuously on each of the detected sequences, use
35# run_continuous_reid.
36reid_detections = flow_reid_obj.run_continuous_reid(detections)
37
38# Save the video after re-id
39make_video_with_detections(
40    reid_detections,
41    input_filename="path/to/video",
42    output_filename="after.avi",
43    fps=50,
44)