Skip to content

Commit 4b56173

Browse files
committed
Update in Centroid Trackers
1 parent 6ec7536 commit 4b56173

File tree

10 files changed

+477
-614
lines changed

10 files changed

+477
-614
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ detector.YOLOv3
2626
`YOLOv3 + CentroidTracker` | `TF-MobileNetSSD + CentroidTracker`
2727
:-------------------------:|:-------------------------:
2828
![Cars with YOLO][cars-yolo-output] | ![Cows with tf-SSD][cows-tf-ssd-output]
29-
Video source: [link](https://flic.kr/p/89KYXt) | Video source: [link](https://flic.kr/p/26WeEWy)
29+
Video source: [link](https://flic.kr/p/L6qyxj) | Video source: [link](https://flic.kr/p/26WeEWy)
3030

3131

3232
### Installation

examples/example_scripts/mot_TF_SSDMobileNet.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import cv2 as cv
22
from motrackers.detectors import TF_SSDMobileNetV2
3-
from motrackers import CentroidTracker, CentroidKF_Tracker
3+
from motrackers import CentroidTracker, CentroidKF_Tracker, SORT
44
from motrackers.utils import draw_tracks
55

66

@@ -9,7 +9,7 @@ def main(video_path, weights_path, config_path, use_gpu, tracker):
99
model = TF_SSDMobileNetV2(
1010
weights_path=weights_path,
1111
configfile_path=config_path,
12-
confidence_threshold=0.3,
12+
confidence_threshold=0.4,
1313
nms_threshold=0.2,
1414
draw_bboxes=True,
1515
use_gpu=use_gpu
@@ -24,15 +24,13 @@ def main(video_path, weights_path, config_path, use_gpu, tracker):
2424
break
2525

2626
bboxes, confidences, class_ids = model.detect(image)
27-
2827
tracks = tracker.update(bboxes, confidences, class_ids)
29-
30-
updated_image = model.draw_bboxes(image, bboxes, confidences, class_ids)
28+
updated_image = model.draw_bboxes(image.copy(), bboxes, confidences, class_ids)
3129

3230
updated_image = draw_tracks(updated_image, tracks)
3331

3432
cv.imshow("image", updated_image)
35-
if cv.waitKey(1) & 0xFF == ord('q'):
33+
if cv.waitKey(0) & 0xFF == ord('q'):
3634
break
3735

3836
cap.release()
@@ -55,7 +53,8 @@ def main(video_path, weights_path, config_path, use_gpu, tracker):
5553
)
5654

5755
parser.add_argument(
58-
'--config', '-c', type=str,
56+
'--config', '-c',
57+
type=str,
5958
default="./../pretrained_models/tensorflow_weights/ssd_mobilenet_v2_coco_2018_03_29.pbtxt",
6059
help='path to config file of Caffe-MobileNetSSD (`.pbtxt` file).'
6160
)
@@ -66,15 +65,17 @@ def main(video_path, weights_path, config_path, use_gpu, tracker):
6665
)
6766

6867
parser.add_argument(
69-
'--tracker', type=str, default='CentroidKF_Tracker',
70-
help="Tracker used to track objects. Options include ['CentroidTracker', 'CentroidKF_Tracker']")
68+
'--tracker', type=str, default='SORT',
69+
help="Tracker used to track objects. Options include ['CentroidTracker', 'CentroidKF_Tracker', 'SORT']")
7170

7271
args = parser.parse_args()
7372

7473
if args.tracker == 'CentroidTracker':
75-
tracker = CentroidTracker(max_lost=5, tracker_output_format='mot_challenge')
74+
tracker = CentroidTracker(max_lost=0, tracker_output_format='mot_challenge')
7675
elif args.tracker == 'CentroidKF_Tracker':
77-
tracker = CentroidKF_Tracker(max_lost=3, tracker_output_format='mot_challenge')
76+
tracker = CentroidKF_Tracker(max_lost=0, tracker_output_format='mot_challenge')
77+
elif args.tracker == 'SORT':
78+
tracker = SORT(max_lost=2, tracker_output_format='mot_challenge', iou_threshold=0.5, time_step=1)
7879
else:
7980
raise NotImplementedError
8081

motrackers/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88

99

1010
from motrackers.tracker import Tracker as CentroidTracker
11-
from motrackers.tracker import CentroidKF_Tracker
12-
from motrackers.iou_tracker import IOUTracker
11+
from motrackers.centroid_kf_tracker import CentroidKF_Tracker
1312
from motrackers.sort_tracker import SORT
13+
# from motrackers.iou_tracker import IOUTracker

motrackers/centroid_kf_tracker.py

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
from collections import OrderedDict
2+
import numpy as np
3+
from scipy.spatial import distance
4+
from scipy.optimize import linear_sum_assignment
5+
from motrackers.tracker import Tracker
6+
from motrackers.track import KFTrackCentroid
7+
from motrackers.utils.misc import get_centroid
8+
9+
10+
def assign_tracks2detection_centroid_distances(bbox_tracks, bbox_detections, distance_threshold=10.):
11+
"""
12+
Assigns detected bounding boxes to tracked bounding boxes using IoU as a distance metric.
13+
14+
Args:
15+
bbox_tracks (numpy.ndarray): tracked bounding boxes with shape (n, 4) each row as (xmin, ymin, width, height).
16+
bbox_detections (numpy.ndarray): detection bounding boxes with shape (m, 4) each row as (xmin, ymin, width, height).
17+
distance_threshold (float): Minimum distance between the tracked object and new detection to consider for assignment.
18+
19+
Returns:
20+
tuple: Tuple containing the following elements:
21+
- matches (numpy.ndarray): Array of shape `(n, 2)` where `n` is number of pairs formed after
22+
matching tracks to detections. This is an array of tuples with each element as matched pair
23+
of indices`(track_index, detection_index)`.
24+
- unmatched_detections (numpy.ndarray): Array of shape `(m,)` where `m` is number of unmatched detections.
25+
- unmatched_tracks (numpy.ndarray): Array of shape `(k,)` where `k` is the number of unmatched tracks.
26+
"""
27+
28+
if (bbox_tracks.size == 0) or (bbox_detections.size == 0):
29+
return np.empty((0, 2), dtype=int), np.arange(len(bbox_detections), dtype=int), np.empty((0,), dtype=int)
30+
31+
if len(bbox_tracks.shape) == 1:
32+
bbox_tracks = bbox_tracks[None, :]
33+
34+
if len(bbox_detections.shape) == 1:
35+
bbox_detections = bbox_detections[None, :]
36+
37+
estimated_track_centroids = get_centroid(bbox_tracks)
38+
detection_centroids = get_centroid(bbox_detections)
39+
centroid_distances = distance.cdist(estimated_track_centroids, detection_centroids)
40+
41+
assigned_tracks, assigned_detections = linear_sum_assignment(centroid_distances)
42+
43+
unmatched_detections, unmatched_tracks = [], []
44+
45+
for d in range(bbox_detections.shape[0]):
46+
if d not in assigned_detections:
47+
unmatched_detections.append(d)
48+
49+
for t in range(bbox_tracks.shape[0]):
50+
if t not in assigned_tracks:
51+
unmatched_tracks.append(t)
52+
53+
# filter out matched with high distance between centroids
54+
matches = []
55+
for t, d in zip(assigned_tracks, assigned_detections):
56+
if centroid_distances[t, d] > distance_threshold:
57+
unmatched_detections.append(d)
58+
unmatched_tracks.append(t)
59+
else:
60+
matches.append((t, d))
61+
62+
if len(matches):
63+
matches = np.array(matches)
64+
else:
65+
matches = np.empty((0, 2), dtype=int)
66+
67+
return matches, np.array(unmatched_detections), np.array(unmatched_tracks)
68+
69+
70+
class CentroidKF_Tracker(Tracker):
71+
"""
72+
Kalman filter based tracking of multiple detected objects.
73+
74+
Parameters
75+
----------
76+
max_lost : int
77+
Maximum number of consecutive frames object was not detected.
78+
tracker_output_format : str
79+
Output format of the tracker.
80+
process_noise_scale : float or numpy.ndarray
81+
Process noise covariance matrix of shape (3, 3) or covariance magnitude as scalar value.
82+
measurement_noise_scale : float or numpy.ndarray
83+
Measurement noise covariance matrix of shape (1,) or covariance magnitude as scalar value.
84+
time_step : int or float
85+
Time step for Kalman Filter.
86+
"""
87+
88+
def __init__(
89+
self,
90+
max_lost=1,
91+
centroid_distance_threshold=30.,
92+
tracker_output_format='mot_challenge',
93+
process_noise_scale=1.0,
94+
measurement_noise_scale=1.0,
95+
time_step=1
96+
):
97+
self.time_step = time_step
98+
self.process_noise_scale = process_noise_scale
99+
self.measurement_noise_scale = measurement_noise_scale
100+
self.centroid_distance_threshold = centroid_distance_threshold
101+
self.kalman_trackers = OrderedDict()
102+
super().__init__(max_lost, tracker_output_format)
103+
104+
def _add_track(self, frame_id, bbox, detection_confidence, class_id, **kwargs):
105+
self.tracks[self.next_track_id] = KFTrackCentroid(
106+
self.next_track_id, frame_id, bbox, detection_confidence, class_id=class_id,
107+
data_output_format=self.tracker_output_format, process_noise_scale=self.process_noise_scale,
108+
measurement_noise_scale=self.measurement_noise_scale, **kwargs
109+
)
110+
self.next_track_id += 1
111+
112+
def update(self, bboxes, detection_scores, class_ids):
113+
self.frame_count += 1
114+
bbox_detections = np.array(bboxes, dtype='int')
115+
116+
track_ids = list(self.tracks.keys())
117+
bbox_tracks = []
118+
for track_id in track_ids:
119+
bbox_tracks.append(self.tracks[track_id].predict())
120+
bbox_tracks = np.array(bbox_tracks)
121+
122+
matches, unmatched_detections, unmatched_tracks = assign_tracks2detection_centroid_distances(
123+
bbox_tracks, bbox_detections, distance_threshold=self.centroid_distance_threshold
124+
)
125+
126+
for i in range(matches.shape[0]):
127+
t, d = matches[i, :]
128+
track_id = track_ids[t]
129+
bbox = bboxes[d, :]
130+
cid = class_ids[d]
131+
confidence = detection_scores[d]
132+
self._update_track(track_id, self.frame_count, bbox, confidence, cid, lost=0)
133+
134+
for d in unmatched_detections:
135+
bbox = bboxes[d, :]
136+
cid = class_ids[d]
137+
confidence = detection_scores[d]
138+
self._add_track(self.frame_count, bbox, confidence, cid)
139+
140+
for t in unmatched_tracks:
141+
track_id = track_ids[t]
142+
bbox = bbox_tracks[t, :]
143+
confidence = self.tracks[track_id].detection_confidence
144+
cid = self.tracks[track_id].class_id
145+
self._update_track(track_id, self.frame_count, bbox, confidence, cid, lost=1)
146+
147+
if self.tracks[track_id].lost > self.max_lost:
148+
self._remove_track(track_id)
149+
150+
outputs = self._get_tracks(self.tracks)
151+
return outputs

motrackers/iou_tracker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class IOUTracker(Tracker):
2626

2727
def __init__(
2828
self,
29-
max_lost=2,
29+
max_lost=0,
3030
iou_threshold=0.5,
3131
min_detection_confidence=0.4,
3232
max_detection_confidence=0.7,

0 commit comments

Comments
 (0)