Skip to content

Feat: MOT dataset loader #29

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

rolson24
Copy link

Description

This PR is the start of the evaluation framework started in #7. It contains a base dataset prototype and a MOTChallenge dataset implementation. The MOTChallenge dataset class can load, parse, preprocess, and provide an iterator for each sequence in the dataset, much in the same way TrackEval does. It also supports loading "public detections" which are pre-computed detections stored in a dets.txt file for each sequence which standardizes the detections that object trackers are evaluated on.

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update

How has this change been tested, please provide a testcase or example of how you tested the change?

Example usage:

from pathlib import Path
import supervision as sv
from trackers.dataset.mot_challenge import MOTChallengeDataset

local_mot17_path = "/path/to/MOT17"
dataset_root = os.path.join(local_mot17_path, 'train')
mot_dataset_path = Path(dataset_root)

# Initialize the dataset with the path to the MOT folder.
mot_dataset = MOTChallengeDataset(dataset_path=mot_dataset_path)

# Print the sequences in the dataset
available_sequences = mot_dataset.get_sequence_names()
print("Available sequences:", available_sequences)

# Print the info of the first sequence
first_sequence_name = available_sequences[0]
seq_info = mot_dataset.get_sequence_info(first_sequence_name)
print("Sequence Info:", seq_info)

# Get frames from the iterator
frame_count = 0
for frame_info in mot_dataset.get_frame_iterator(first_sequence_name):
    if frame_count < 5:
        print(frame_info)
    frame_count += 1
print(f"Total frames iterated: {frame_count}")

# Load the ground truth tracks for the first sequence
gt_detections = mot_dataset.load_ground_truth(first_sequence_name) # sv.Detections object
for i in range(min(5, len(gt_detections))):
    print(f"Frame: {gt_detections.data['frame_idx'][i]}, \
            ID: {gt_detections.tracker_id[i]}, \
            Class: {gt_detections.class_id[i]}, \
            Conf/Vis: {gt_detections.confidence[i]:.2f}, \
            Box: {gt_detections.xyxy[i]}")

# Load public detections for the sequence
mot_dataset.load_public_detections(min_confidence=0.1) # Optional: set a confidence threshold

if mot_dataset.has_public_detections:
    frame_iter = mot_dataset.get_frame_iterator(sequence_name)
    first_frame_info = next(frame_iter)
    # Get the image path for the first frame
    first_frame_path = first_frame_info.get('image_path')

    if first_frame_path:
        print(f"Getting detections for image: {first_frame_path}")
        public_dets_frame1 = mot_dataset.get_public_detections(first_frame_path)
        print(f"Found {len(public_dets_frame1)} public detections for the first frame.")
        if len(public_dets_frame1) > 0:
            print("First 5 public detections for this frame:")
            for i in range(min(5, len(public_dets_frame1))):
                print(f"Conf: {public_dets_frame1.confidence[i]:.2f}, \
                        Box: {public_dets_frame1.xyxy[i]}")

I also have a colab notebook testing out the data loader

Any specific deployment considerations

For example, documentation changes, usability, usage/costs, secrets, etc.

Docs

  • Docs updated? What were the changes:

Copy link
Collaborator

@soumik12345 soumik12345 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for this PR!
Kudos for attaching such a detailed colab notebook with the PR, really helped me test the code quickly!

I have recommended some minor changes. Can you please address them?

soumik12345
soumik12345 previously approved these changes Apr 29, 2025
Copy link
Collaborator

@soumik12345 soumik12345 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @rolson24, the PR looks good to me.
I have some minor nitpick comments.

import supervision as sv


# --- Base Dataset ---
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let’s remove Python comments like these; we can already tell it’s a base dataset since it only has abstract methods and implements the ABC interface.


Returns:
A dictionary containing sequence information (e.g., 'frame_rate',
'seqLength', 'img_width', 'img_height', 'img_dir'). Keys and value
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we make sure all these dict keys follow the same naming convention? It looks like everything is in snake_case except for seqLength. Also, wouldn’t it make more sense to use a dataclass or a namedtuple instead of a regular dict here?

Comment on lines +66 to +67
iou_threshold: float = 0.5,
remove_distractor_matches: bool = True,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems that the preprocess implementation is highly dataset-specific. given that, does it really make sense to add iou_threshold and remove_distractor_matches to the base class, especially since there’s no guarantee they’ll be needed here? maybe it would be better to allow for additional keyword arguments instead?



# --- Base Dataset ---
class EvaluationDataset(abc.ABC):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we really call this dataset EvaluationDataset? Maybe it would make more sense to use a more general name, like TrackingDataset or MOTDataset instead?

logger = get_logger(__name__)


def relabel_ids(detections: sv.Detections) -> sv.Detections:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Let’s rename this function from relabel_ids to remap_ids.
  • We should also allow users to pass a custom mapping that defines how each source_id should be mapped to a target_id.
  • I definitely think we should add a unit test for this function.

Comment on lines +109 to +115
with open(file_path, "r") as f:
for line in f:
line = line.strip()
if not line:
continue

parts = line.split(",")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

supervision have a util for loading text file lines; let's use it here

# or detection confidence in det.txt
confidence = float(parts[6])

class_id = int(parts[7]) if len(parts) > 7 else -1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd move -1 magic number to the top of the file and set is as python constant.

Comment on lines +212 to +218
return sv.Detections(
xyxy=xyxy,
confidence=confidence,
class_id=class_id,
tracker_id=tracker_id,
data={"frame_idx": frame_idx},
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this method must return separate Detections object per frame.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So instead of storing the frame index in the data attribute of the Detections object, we should just return a list of Detections objects? That probably does align more with how supervision uses Detections objects.

logger.warning(f"No valid MOTChallenge sequences found in {self.root_path}")
return sorted(sequences)

def _parse_mot_file(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we would need to separate loading data from file and processing data; this will allow us to unit test the data processing part

Comment on lines +322 to +326
# Try common extensions explicitly if glob fails
if (img_dir / f"{1:06d}.jpg").exists():
img_ext = ".jpg"
elif (img_dir / f"{1:06d}.png").exists():
img_ext = ".png"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused as to how any of this would work if glob fails.

@SkalskiP
Copy link
Collaborator

Hi @rolson24 👋🏻 After my conversation with @soumik12345 yesterday, it became clear to me that we really need to enable benchmarking in the trackers package as soon as possible. Because of this, getting this PR over the finish line is my top priority for the week. I’ve left my comments on the PR—do you think you’ll have time to address them? If not, no worries, I can take care of it to keep things moving forward.

@rolson24
Copy link
Author

rolson24 commented Jun 6, 2025

Hi @rolson24 👋🏻 After my conversation with @soumik12345 yesterday, it became clear to me that we really need to enable benchmarking in the trackers package as soon as possible. Because of this, getting this PR over the finish line is my top priority for the week. I’ve left my comments on the PR—do you think you’ll have time to address them? If not, no worries, I can take care of it to keep things moving forward.

Hi @SkalskiP, I can address these request these changes this weekend. Sorry I was finishing up school last week, but I should have some more time now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants