Skip to content

Feat/core/oc sort alex#162

Draft
AlexBodner wants to merge 40 commits intodevelopfrom
feat/core/oc-sort-alex
Draft

Feat/core/oc sort alex#162
AlexBodner wants to merge 40 commits intodevelopfrom
feat/core/oc-sort-alex

Conversation

@AlexBodner
Copy link
Collaborator

What does this PR do?

OC-SORT in process

Type of Change

  • New feature (non-breaking change that adds functionality)

Testing

Benchmark soccernet: https://colab.research.google.com/drive/1zzBf4xBSN9KBSDUTDQnMy7Zvny2DOeJL?usp=sharing

# OC-SORT

[![arXiv](https://img.shields.io/badge/arXiv-2203.14360-b31b1b.svg)](https://arxiv.org/abs/2203.14360)
[![colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/roboflow-ai/notebooks/blob/main/notebooks/how-to-track-objects-with-sort-tracker.ipynb)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Remember to update it later for OC-SORT

Copy link
Collaborator

Choose a reason for hiding this comment

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

If you look at the current docs, for example the ByteTrack page, you will see badges removed from the top for now. https://trackers.roboflow.com/develop/trackers/bytetrack/

Instead, a Tutorials section appears on the main page, with links to Google Colab notebooks. https://trackers.roboflow.com/develop/

1. Observation-Centre Re-Update (ORU): runs a predict-update loop with a 'virtual trajectory'
depending on the last observation and new observation when a track is re-activated after being lost.
2. Observation-Centric Momentum (OCM): incorporate the direction consistency of tracks in the cost matrix for the association.
3. Observation-centric Recovery (OCR): a second-stage association step between the last observation of unmatched tracks and the unmatched observations after the usual association. It attempts to recover tracks that were lost due to object stopping or short-term occlusion. Uses only IoU.
Copy link
Collaborator

Choose a reason for hiding this comment

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

"Uses only IoIU" as a separate line?

Copy link
Collaborator Author

@AlexBodner AlexBodner Jan 29, 2026

Choose a reason for hiding this comment

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

I think that is a problem of the visualization here maybe, because I see it in the same line


## Overview

OC-SORT remains Simple, Online, and Real-Time ([SORT](../sort/tracker.md)) but improves robustness during occlusion and non-linear motion.
Copy link
Collaborator

Choose a reason for hiding this comment

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

"remains... like SORT". Otherwise, it sounds confusing

improving tracking through occlusions, but may increase the possibility
of ID switching for objects with similar appearance.
frame_rate (float): Frame rate of the video (frames per second).
Used to calculate the maximum time a track can be lost.
Copy link
Collaborator

@tstanczyk95 tstanczyk95 Jan 28, 2026

Choose a reason for hiding this comment

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

specify that we talk about the number of frames: for how many frames a track/tracked entity can be lost

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I didnt understand the comment, can you explain further?

form [x1, y1, x2, y2].
"""
for detection_idx in unmatched_detections:
new_tracker = OCSORTTracklet(detections.xyxy[detection_idx])
Copy link
Collaborator

Choose a reason for hiding this comment

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

This might be super confusing in the future. I would opt for creating a new track or new tracklet, not a new tracker.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Looks like new traklet to me.

Copy link
Collaborator Author

@AlexBodner AlexBodner Jan 29, 2026

Choose a reason for hiding this comment

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

yes, agree. this came from me basing the code on SORT

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

also, one comment on this, sv.Detections have the attribute 'tracker_id', so we have this colliding term there as well.

for t, tracklet in enumerate(tracklets):
if tracklet.previous_to_last_observation is None: # if there is no previous box
continue
# In case the track is lost we use the last known box
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is it by the original OC-SORT design? It might introduce some measurement noise and lead towards a drift.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yes, if there is not enough information to calculate a direction then that component is 0

Copy link
Collaborator

@tstanczyk95 tstanczyk95 left a comment

Choose a reason for hiding this comment

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

The implementation code looks overall good with a few remarks from my side. In any case, we will need to discuss it online to clarify some parts and design choices as well as ensure about the parameters.

@SkalskiP SkalskiP changed the base branch from main to develop January 28, 2026 22:20
# OC-SORT

[![arXiv](https://img.shields.io/badge/arXiv-2203.14360-b31b1b.svg)](https://arxiv.org/abs/2203.14360)
[![colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/roboflow-ai/notebooks/blob/main/notebooks/how-to-track-objects-with-sort-tracker.ipynb)
Copy link
Collaborator

Choose a reason for hiding this comment

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

If you look at the current docs, for example the ByteTrack page, you will see badges removed from the top for now. https://trackers.roboflow.com/develop/trackers/bytetrack/

Instead, a Tutorials section appears on the main page, with links to Google Colab notebooks. https://trackers.roboflow.com/develop/

Comment on lines 12 to 18
OC-SORT remains Simple, Online, and Real-Time ([SORT](../sort/tracker.md)) but improves robustness during occlusion and non-linear motion.
It recognizes limitations from SORT and the linear motion assumption of the Kalman filter, and adds three
mechanisms to enhance tracking:
1. Observation-Centre Re-Update (ORU): runs a predict-update loop with a 'virtual trajectory'
depending on the last observation and new observation when a track is re-activated after being lost.
2. Observation-Centric Momentum (OCM): incorporate the direction consistency of tracks in the cost matrix for the association.
3. Observation-centric Recovery (OCR): a second-stage association step between the last observation of unmatched tracks and the unmatched observations after the usual association. It attempts to recover tracks that were lost due to object stopping or short-term occlusion. Uses only IoU.
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 keep the text simple, like in the ByteTrack docs. https://trackers.roboflow.com/develop/trackers/bytetrack/ No lists. A single paragraph of text.

depending on the last observation and new observation when a track is re-activated after being lost.
2. Observation-Centric Momentum (OCM): incorporate the direction consistency of tracks in the cost matrix for the association.
3. Observation-centric Recovery (OCR): a second-stage association step between the last observation of unmatched tracks and the unmatched observations after the usual association. It attempts to recover tracks that were lost due to object stopping or short-term occlusion. Uses only IoU.

Copy link
Collaborator

Choose a reason for hiding this comment

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

We should have Benchmarks section here.

Comment on lines +20 to +131
## Examples

=== "inference"

```python hl_lines="2 5 12"
import supervision as sv
from trackers import OCSORTTracker
from inference import get_model

tracker = OCSORTTracker()
model = get_model(model_id="rfdetr-medium")
annotator = sv.LabelAnnotator(text_position=sv.Position.CENTER)

def callback(frame, _):
result = model.infer(frame)[0]
detections = sv.Detections.from_inference(result)
detections = tracker.update(detections)
return annotator.annotate(frame, detections, labels=detections.tracker_id)

sv.process_video(
source_path="<INPUT_VIDEO_PATH>",
target_path="<OUTPUT_VIDEO_PATH>",
callback=callback,
)
```

=== "rf-detr"

```python hl_lines="2 5 11"
import supervision as sv
from trackers import OCSORTTracker
from rfdetr import RFDETRMedium

tracker = OCSORTTracker()
model = RFDETRMedium()
annotator = sv.LabelAnnotator(text_position=sv.Position.CENTER)

def callback(frame, _):
detections = model.predict(frame)
detections = tracker.update(detections)
return annotator.annotate(frame, detections, labels=detections.tracker_id)

sv.process_video(
source_path="<INPUT_VIDEO_PATH>",
target_path="<OUTPUT_VIDEO_PATH>",
callback=callback,
)
```

=== "ultralytics"

```python hl_lines="2 5 12"
import supervision as sv
from trackers import OCSORTTracker
from ultralytics import YOLO

tracker = OCSORTTracker()
model = YOLO("yolo11m.pt")
annotator = sv.LabelAnnotator(text_position=sv.Position.CENTER)

def callback(frame, _):
result = model(frame)[0]
detections = sv.Detections.from_ultralytics(result)
detections = tracker.update(detections)
return annotator.annotate(frame, detections, labels=detections.tracker_id)

sv.process_video(
source_path="<INPUT_VIDEO_PATH>",
target_path="<OUTPUT_VIDEO_PATH>",
callback=callback,
)
```

=== "transformers"

```python hl_lines="3 6 28"
import torch
import supervision as sv
from trackers import OCSORTTracker
from transformers import RTDetrV2ForObjectDetection, RTDetrImageProcessor

tracker = OCSORTTracker()
processor = RTDetrImageProcessor.from_pretrained("PekingU/rtdetr_v2_r18vd")
model = RTDetrV2ForObjectDetection.from_pretrained("PekingU/rtdetr_v2_r18vd")
annotator = sv.LabelAnnotator(text_position=sv.Position.CENTER)

def callback(frame, _):
inputs = processor(images=frame, return_tensors="pt")
with torch.no_grad():
outputs = model(**inputs)

h, w, _ = frame.shape
results = processor.post_process_object_detection(
outputs,
target_sizes=torch.tensor([(h, w)]),
threshold=0.5
)[0]

detections = sv.Detections.from_transformers(
transformers_results=results,
id2label=model.config.id2label
)

detections = tracker.update(detections)
return annotator.annotate(frame, detections, labels=detections.tracker_id)

sv.process_video(
source_path="<INPUT_VIDEO_PATH>",
target_path="<OUTPUT_VIDEO_PATH>",
callback=callback,
)
```
Copy link
Collaborator

Choose a reason for hiding this comment

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

Before v2.1.0 release I updated the format of similar examples for SORT and ByteTrack. Please make sure we follow similar pattern here.

to the unmatched observations after the usual association. It attempts to recover tracks that were lost
due to object stopping or short-term occlusion.
Args:
lost_track_buffer (int): Number of frames to buffer when a track is lost.
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 drop types from docstring, we only keep it in code. We are slowly doing the same in other repos. I did that in trackers before v2.1.0 release. I'm flagging it here, but we would need to do it in the whole PR.

h = bbox[3] - bbox[1]
x = bbox[0] + w / 2.0
y = bbox[1] + h / 2.0
s = w * h # scale is just area
Copy link
Collaborator

Choose a reason for hiding this comment

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

Comments like this seem redundent, it's a pretty simple math operation. You can point out in docstring. that scale is an area.

@@ -0,0 +1,34 @@
import numpy as np
Copy link
Collaborator

Choose a reason for hiding this comment

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

would rename the whole file to converters.py

from trackers.log import get_logger

__all__ = ["ByteTrackTracker", "SORTTracker"]
__all__ = ["OCSORTTracker", "SORTTracker"]
Copy link
Collaborator

Choose a reason for hiding this comment

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

seem like this file is out of date, please make sure all develop chanegs are in this branch

- ByteTrack: trackers/bytetrack.md
- SORT: trackers/core/sort/tracker.md
- DeepSORT: trackers/core/deepsort/tracker.md
- OC-SORT: trackers/core/ocsort/tracker.md
Copy link
Collaborator

Choose a reason for hiding this comment

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

seem like this file is out of date, please make sure all develop chanegs are in this branch

@@ -0,0 +1,180 @@
from copy import deepcopy
Copy link
Collaborator

Choose a reason for hiding this comment

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

why not move this whole file to core/ocsort and rename it to utils.py if it's OC-SORT specific? is this code entierly copy-pasted from their repo?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ok! because before we were putting all tracker specific utils there i continued putting them there, but sounds good to do so

@SkalskiP
Copy link
Collaborator

@AlexBodner can we close it?

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.

3 participants