Load a video dataset
CheckMAITE reads video datasets through DataMAITE, the same library it uses for image datasets. DataMAITE returns a MAITE multi-object-tracking (MOT) dataset, which CheckMAITE accepts like any other MAITE dataset. DataMAITE's video support, including PyAV for decoding MP4, is installed with CheckMAITE.
Load a flat folder of MP4 videos
Put the .mp4 files directly in one folder. DataMAITE accepts H.264 and MPEG-2
video, which is what the JATIC interoperability standard IR-3.3-S-1 requires,
and skips other codecs with a warning.
from checkmaite.core.multiobject_tracking import load_mot_dataset
dataset = load_mot_dataset("path/to/videos", "flat_mp4")
stream, target, metadata = dataset[0] # one datum per video
for frame in stream:
frame.pixels # uint8 array, shape (3, height, width)
load_mot_dataset calls DataMAITE's load_mot and always emits every frame of
every video. It also gives the dataset an id derived from its root, format and
options: CheckMAITE caches predictions and evaluations by dataset id, and
load_mot gives every dataset the same default id, so load through
load_mot_dataset or pass your own dataset_id.
Run a model over the videos
The result is a MAITE MOT dataset, so it goes anywhere CheckMAITE takes a MOT dataset. Today that is the cached prediction task, which runs a MAITE multi-object-tracking model over every video:
from modelmaite import ByteTrackMOTModel, TorchvisionODModel
from checkmaite import cached_tasks
detector = TorchvisionODModel(model_name="fasterrcnn_resnet50_fpn")
tracker = ByteTrackMOTModel(detector=detector)
predictions, _ = cached_tasks.predict(model=tracker, dataset=dataset)
tracker can be any MAITE MOT model: it takes a batch of video streams and
returns one target per video, with one set of tracks per frame. This example
uses modelmaite's ByteTrack wrapper around a torchvision detector, which needs
modelmaite[mot]. CheckMAITE does not install that extra.
Evaluate a tracker
checkmaite.core.multiobject_tracking.MaiteEvaluation scores a MOT model on an
annotated dataset, the same way the object-detection and image-classification
MaiteEvaluation capabilities do. The built-in metric, computed with
py-motmetrics, reports MOTA (the
headline value), MOTP, IDF1 and the number of identity switches. MOTP follows
the motmetrics convention: the mean IoU distance (1 - IoU) of matched boxes,
so lower is better. A score that is undefined for the evaluation is left out of
the results, such as MOTP when the tracker matched nothing; if MOTA is undefined
because the dataset has no ground-truth objects, the evaluation fails with an
error.
The metric matches boxes regardless of class, so keep only the classes you
want to score in both the dataset (classes=) and the tracker's output. MOT17
labels only pedestrians, while the torchvision detector above predicts every
COCO class, so this example keeps only its person boxes before tracking;
otherwise every other box counts as a false positive. Give the wrapped detector
its own id, because CheckMAITE caches results by model id.
import numpy as np
from modelmaite import ByteTrackMOTModel
from modelmaite.object_detection import DetectionTarget
from checkmaite.core.multiobject_tracking import MaiteEvaluation, load_mot_dataset
from checkmaite.core.multiobject_tracking.metrics import clear_mot_metrics_factory
class PersonDetector:
"""Keep only the detector's `person` boxes."""
def __init__(self, detector):
self.detector = detector
labels = detector.metadata["index2label"]
self.person = next(i for i, name in labels.items() if name == "person")
self.metadata = {**detector.metadata, "id": f"{detector.metadata['id']}_person"}
def __call__(self, batch):
targets = []
for prediction in self.detector(batch):
keep = np.asarray(prediction.labels) == self.person
targets.append(
DetectionTarget(
boxes=np.asarray(prediction.boxes)[keep],
labels=np.asarray(prediction.labels)[keep],
scores=np.asarray(prediction.scores)[keep],
)
)
return targets
person_tracker = ByteTrackMOTModel(detector=PersonDetector(detector))
dataset = load_mot_dataset("path/to/MOT17", "motchallenge", classes={1})
run = MaiteEvaluation().run(models=[person_tracker], datasets=[dataset], metrics=[clear_mot_metrics_factory()])
run.outputs.metrics
Predictions with track ID -1, which MAITE reserves for untracked detections,
are not scored. MOTChallenge distractor classes and ignored regions are not
handled, so scores on MOTChallenge data differ from the official leaderboard.
Supported formats
The evaluation scores every frame, and a frame without boxes counts as empty, so
detections on it are false positives. That is only correct when every frame is
labeled, so load_mot_dataset accepts "motchallenge" and "flat_mp4", and
refuses a sequence whose exact frame count is unknown.
DataMAITE also reads "tao", "hmie" and "visdrone_video", but
load_mot_dataset refuses them for now. TAO and HMIE usually label only some
frames, which needs scoring on the labeled frames while tracking over every
frame. DataMAITE currently drops VisDrone ground-truth tracks whose id is 0.
For video classification datasets in the Hugging Face VideoFolder layout, use
datamaite.load_vc. See the DataMAITE documentation for each format's layout.