-
Notifications
You must be signed in to change notification settings - Fork 209
Feature: Offline Tracker (KSP Tracker) #89
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
Draft
Ashp116
wants to merge
107
commits into
roboflow:main
Choose a base branch
from
Ashp116:feature/offline-tracker-ksp
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 88 commits
Commits
Show all changes
107 commits
Select commit
Hold shift + click to select a range
d2e524e
ADD: Added KSPTracker for offline trackering
Ashp116 b83da07
ADD: Added IOU calcuations of bboxes
Ashp116 53b8c91
ADD: Added a function to build a directed graph from detections
Ashp116 cb557cb
ADD: Added K-Shortest Paths algorithm
Ashp116 b082cd0
ADD: Added a function to process tracks from source to sink
Ashp116 0c260e3
BUG: Fixed the reset function
Ashp116 1adc1cf
BUG: Fixed IOU calculation, can_connected_nodes(), and update_detecti…
Ashp116 f24c25a
FIX: Fixes for bugs found for the KSP tracker issues during testing
Ashp116 1aba6c2
MISC: Added comments for functions and fixed formatting
Ashp116 db52617
MISC: Removed unused imports
Ashp116 31b5a5b
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] 206d532
MISC: Fixed formatting
Ashp116 0544904
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] f5dcaf3
MISC: Add type annotation for pre-commit
Ashp116 9d5d37d
MISC: Add networkx to dependency
Ashp116 0452a2f
FIX: Switch to sv.box_iou_batch
Ashp116 f3b849b
FIX: process_tracks returned None
Ashp116 614b2fb
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] 0e6bc41
UPDATE: Updated the way the directed acyclic graph is built
Ashp116 f82a112
UPDATE: Updated process_tasks
Ashp116 0543291
FIX: Removed the width and height parameters
Ashp116 1235657
FIX: Updated xyxy array type
Ashp116 c85e0b8
FIX: Fixes after testing
Ashp116 49a6b2e
UPDATE: Update KSP
Ashp116 1f965e6
ADD: Added Docstrings
Ashp116 1c0626a
Merge branch 'feature/offline-tracker-ksp' into fix/ksp-tracker
Ashp116 3ab72d9
Merge pull request #1 from Ashp116/fix/ksp-tracker
Ashp116 8f9e00b
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] 0e37b38
FIX: precommit fix
Ashp116 cc1f5d7
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] 1f43fc5
UPDATE: Made process_tracks return list of detections
Ashp116 2cf26d6
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] 11b68a4
UPDATE: removed all unused occurrences of 'max_gap'
Ashp116 6b8fbc3
UPDATE: Updated KSP solver
Ashp116 5134e5e
ADD: Docstrings
Ashp116 c0a0269
UPDATE: Updated graph to be a member variable
Ashp116 31715af
UPDATE: Docstrings
Ashp116 c8535ff
BUG: Diagnosing untracked detections
Ashp116 35404f2
BUG: Debugging KSP thru visuals
Ashp116 01c8cf1
BUG: Bellman Ford path visualization
Ashp116 e113c24
BUG: Same edge weights
Ashp116 0aaec74
BUG: Debugging KSP...
Ashp116 764c776
PROGRESS: Able to get less tracer switchs
Ashp116 ccd77af
CHECKPOINT: Changing the entirely
Ashp116 af750b6
UPDATE: Changed the entire logic for KSP
Ashp116 c764107
FIX: Testing and changing got something relative to expectation?
Ashp116 1113154
MISC: Clean up!
Ashp116 daa90dc
UPDATE: Tracker with the least change in detection position track a d…
Ashp116 4e79595
MISC: Docstrings
Ashp116 1a3a37a
Merge branch 'roboflow:main' into fix/ksp-disjoints
Ashp116 253a158
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] ab49445
Merge pull request #2 from Ashp116/fix/ksp-disjoints
Ashp116 23672e4
MISC: Precommit
Ashp116 13f2de2
MISC: Precommit
Ashp116 18e52c9
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] 704f476
MISC: Precommit
Ashp116 077167b
Merge branch 'feature/offline-tracker-ksp' of https://github.com/Ashp…
Ashp116 789d8cf
MISC: Precommit
Ashp116 a23cf66
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] 2aa2e0a
UPDATE: Added num_of_tracks param
Ashp116 dd2b4ff
Merge branch 'feature/offline-tracker-ksp' of https://github.com/Ashp…
Ashp116 9b70a31
UPDATE: Added tqdm and small changes
Ashp116 12b924a
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] b6ce7a2
UPDATE: Changes reflecting the comments from the code review
Ashp116 286f946
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] 89e792f
Pre-commit
Ashp116 9aadfa8
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] 2de57cc
Debug: Debugging the KSP solve
Ashp116 04dc790
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] 23efbe9
Debug: Debugging the KSP ith itr
Ashp116 47066b7
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] aa5bae4
Debug: Debugging the KSP solve
Ashp116 589c1dc
Debug: Change base penalty
Ashp116 9c7b994
UPDATE: Changed base_penalty to path_overlap_penalty hyper params
Ashp116 69bc783
UPDATE: Docstrings
Ashp116 203d936
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] eb61e98
Pre-commit
Ashp116 e5397ad
FIX: process_tracks docstrings
Ashp116 048de8c
FIX: process_tracks docstrings
Ashp116 7dcc03c
Pre-commit check
Ashp116 97eda16
Pre-commit
Ashp116 4a14d05
UPDATE: Updates referring to the code review
Ashp116 44d78aa
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] 875acd5
Pre-commit
Ashp116 19fad3b
ADD: Added documentation and changed folder name
Ashp116 e6020c4
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] b80eca7
OOPS: The ksp files were not added
Ashp116 d82f341
UPDATE: Updated mkdocs
Ashp116 6fe4324
UPDATE: Changes for code review
Ashp116 0a6869d
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] 35c06e0
ADD: Added doors
Ashp116 ee98e51
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] ceb0218
ADD: Added customizable doors and frame doors
Ashp116 1bf7d8e
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] f521b47
UPDATE: Removed debug path lens
Ashp116 8f1cc52
UPDATE: Docstrings
Ashp116 ab51903
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] 1ecd0f3
Precommit
Ashp116 297c919
Merge branch 'feature/offline-tracker-ksp' of https://github.com/Ashp…
Ashp116 55eed45
Precommit
Ashp116 5029b88
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] b62d140
Precommit
Ashp116 784fbb8
UPDATE: Updated docs
Ashp116 35cea66
UPDATE: Editable hyper parameters for entry and exit costs
Ashp116 7eeb8b8
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] b8d8289
BUG: Debugging the detections vs tracker Id
Ashp116 a9866bd
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
--- | ||
comments: true | ||
--- | ||
|
||
# KSP | ||
|
||
[](https://doi.org/10.1109/TPAMI.2011.21) | ||
[](http://vision.stanford.edu/teaching/cs231b_spring1415/papers/Berclaz-tracking.pdf) | ||
[](https://colab.research.google.com/github/roboflow-ai/notebooks/blob/main/notebooks/how-to-track-objects-with-sort-tracker.ipynb) | ||
|
||
## Overview | ||
|
||
**KSP Tracker** (K-Shortest Paths Tracker) is an offline, tracking-by-detection method that formulates multi-object tracking as a global optimization problem over a directed graph. Each object detection is represented as a node, and feasible transitions between detections are modeled as edges weighted by spatial and temporal consistency. By solving a K-shortest paths problem, the tracker extracts globally optimal trajectories that span the entire sequence. | ||
|
||
Unlike online trackers, which make frame-by-frame decisions, KSP Tracker leverages the full temporal context of a video to achieve greater robustness against occlusions, missed detections, and fragmented tracks. This makes it especially suitable for applications where high tracking accuracy is required, such as surveillance review, sports analytics, or autonomous system evaluation. However, the reliance on global optimization introduces higher computational cost and requires access to the full sequence before tracking can be performed. | ||
|
||
## Examples | ||
|
||
=== "inference" | ||
|
||
```python hl_lines="2 6 11 15" | ||
import supervision as sv | ||
from trackers import KSPTracker | ||
from inference import get_model | ||
import numpy as np | ||
|
||
tracker = KSPTracker() | ||
model = get_model(model_id="yolo11x") | ||
box_annotator = sv.BoxAnnotator() | ||
label_annotator = sv.LabelAnnotator(text_position=sv.Position.TOP_LEFT) | ||
|
||
def get_model_detections(frame: np.ndarray): | ||
result = model.infer(frame)[0] | ||
return sv.Detections.from_inference(result) | ||
|
||
tracked_dets = tracker.track( | ||
source_path="<INPUT_VIDEO_PATH>", | ||
get_model_detections=get_model_detections | ||
) | ||
|
||
frame_idx_to_dets = {i: tracked_dets[i] for i in range(len(tracked_dets))} | ||
|
||
def annotate_frame(frame: np.ndarray, i: int) -> np.ndarray: | ||
detections = frame_idx_to_dets.get(i, sv.Detections.empty()) | ||
detections.tracker_id = detections.tracker_id or np.zeros(len(detections), dtype=int) | ||
labels = [f"{tid}" for tid in detections.tracker_id] | ||
ann = box_annotator.annotate(frame.copy(), detections) | ||
return label_annotator.annotate(ann, detections, labels=labels) | ||
|
||
sv.process_video( | ||
source_path="<INPUT_VIDEO_PATH>", | ||
target_path="<OUTPUT_VIDEO_PATH>", | ||
callback=annotate_frame, | ||
) | ||
``` | ||
|
||
=== "rf-detr" | ||
|
||
```python hl_lines="2 6 11 14" | ||
import supervision as sv | ||
from trackers import KSPTracker | ||
from rfdetr import RFDETRBase | ||
import numpy as np | ||
|
||
tracker = KSPTracker() | ||
model = RFDETRBase() | ||
box_annotator = sv.BoxAnnotator() | ||
label_annotator = sv.LabelAnnotator(text_position=sv.Position.TOP_LEFT) | ||
|
||
def get_model_detections(frame: np.ndarray): | ||
return model.predict(frame) | ||
|
||
tracked_dets = tracker.track( | ||
source_path="<INPUT_VIDEO_PATH>", | ||
get_model_detections=get_model_detections | ||
) | ||
|
||
frame_idx_to_dets = {i: tracked_dets[i] for i in range(len(tracked_dets))} | ||
|
||
def annotate_frame(frame: np.ndarray, i: int) -> np.ndarray: | ||
detections = frame_idx_to_dets.get(i, sv.Detections.empty()) | ||
detections.tracker_id = detections.tracker_id or np.zeros(len(detections), dtype=int) | ||
labels = [f"{tid}" for tid in detections.tracker_id] | ||
ann = box_annotator.annotate(frame.copy(), detections) | ||
return label_annotator.annotate(ann, detections, labels=labels) | ||
|
||
sv.process_video( | ||
source_path="<INPUT_VIDEO_PATH>", | ||
target_path="<OUTPUT_VIDEO_PATH>", | ||
callback=annotate_frame, | ||
) | ||
``` | ||
|
||
=== "ultralytics" | ||
|
||
```python hl_lines="2 6 11 16" | ||
import supervision as sv | ||
from trackers import KSPTracker | ||
from ultralytics import YOLO | ||
import numpy as np | ||
|
||
tracker = KSPTracker() | ||
model = YOLO("yolo11m.pt") | ||
box_annotator = sv.BoxAnnotator() | ||
label_annotator = sv.LabelAnnotator(text_position=sv.Position.TOP_LEFT) | ||
|
||
def get_model_detections(frame: np.ndarray): | ||
result = model(frame, imgsz=1280, verbose=False)[0] | ||
detections = sv.Detections.from_ultralytics(result) | ||
return detections[detections.class_id == 0] if not detections.is_empty() else detections | ||
|
||
tracked_dets = tracker.track( | ||
source_path="<INPUT_VIDEO_PATH>", | ||
get_model_detections=get_model_detections | ||
) | ||
|
||
frame_idx_to_dets = {i: tracked_dets[i] for i in range(len(tracked_dets))} | ||
|
||
def annotate_frame(frame: np.ndarray, i: int) -> np.ndarray: | ||
detections = frame_idx_to_dets.get(i, sv.Detections.empty()) | ||
detections.tracker_id = detections.tracker_id or np.zeros(len(detections), dtype=int) | ||
labels = [f"{tid}" for tid in detections.tracker_id] | ||
ann = box_annotator.annotate(frame.copy(), detections) | ||
return label_annotator.annotate(ann, detections, labels=labels) | ||
|
||
sv.process_video( | ||
source_path="<INPUT_VIDEO_PATH>", | ||
target_path="<OUTPUT_VIDEO_PATH>", | ||
callback=annotate_frame, | ||
) | ||
``` | ||
|
||
=== "transformers" | ||
|
||
```python hl_lines="3 7 13 27" | ||
import torch | ||
import supervision as sv | ||
from trackers import KSPTracker | ||
from transformers import RTDetrV2ForObjectDetection, RTDetrImageProcessor | ||
import numpy as np | ||
|
||
tracker = KSPTracker() | ||
processor = RTDetrImageProcessor.from_pretrained("PekingU/rtdetr_v2_r18vd") | ||
model = RTDetrV2ForObjectDetection.from_pretrained("PekingU/rtdetr_v2_r18vd") | ||
box_annotator = sv.BoxAnnotator() | ||
label_annotator = sv.LabelAnnotator(text_position=sv.Position.TOP_LEFT) | ||
|
||
def get_model_detections(frame: np.ndarray): | ||
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] | ||
|
||
return sv.Detections.from_transformers(results, id2label=model.config.id2label) | ||
|
||
tracked_dets = tracker.track( | ||
"<INPUT_VIDEO_PATH>", | ||
get_model_detections=get_model_detections | ||
) | ||
|
||
frame_idx_to_dets = {i: tracked_dets[i] for i in range(len(tracked_dets))} | ||
|
||
def annotate_frame(frame: np.ndarray, i: int) -> np.ndarray: | ||
detections = frame_idx_to_dets.get(i, sv.Detections.empty()) | ||
detections.tracker_id = detections.tracker_id or np.zeros(len(detections), dtype=int) | ||
labels = [f"{tid}" for tid in detections.tracker_id] | ||
ann = box_annotator.annotate(frame.copy(), detections) | ||
return label_annotator.annotate(ann, detections, labels=labels) | ||
|
||
sv.process_video( | ||
source_path="<INPUT_VIDEO_PATH>", | ||
target_path="<OUTPUT_VIDEO_PATH>", | ||
callback=callback, | ||
) | ||
``` | ||
|
||
## API | ||
|
||
::: trackers.core.ksp.tracker.KSPTracker |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please fix this colab link. Can you please raise a corresponding PR on roboflow/notebooks?