charlesollion commited on
Commit
8689046
·
1 Parent(s): 54c4dfe

wip tracking uniformization with main repo

Browse files
tracking/postprocess_and_count_tracks.py CHANGED
@@ -3,6 +3,7 @@ import argparse
3
  from scipy.signal import convolve
4
  from tracking.utils import write_tracking_results_to_file, read_tracking_results
5
  from collections import defaultdict
 
6
 
7
  import json
8
 
@@ -21,14 +22,20 @@ def filter_tracks(tracklets, kappa, tau):
21
  for det in dets:
22
  results.append((det[0], tracker_nb, det[1], det[2], det[3], det[4]))
23
 
24
- # for tracker_nb, associated_detections in enumerate(tracks):
25
- # for det in associated_detections:
26
- # results.append((associated_detection[0], tracker_nb, associated_detection[1], associated_detection[2]))
27
-
28
  results = sorted(results, key=lambda x: x[0])
29
  return results
30
 
31
 
 
 
 
 
 
 
 
 
 
 
32
  def postprocess_for_api(results, class_dict=defaultdict(lambda: "fragment")):
33
  """ Converts tracking results into json object for API
34
  """
@@ -47,11 +54,18 @@ def postprocess_for_api(results, class_dict=defaultdict(lambda: "fragment")):
47
  result_list.append({"label":classname,
48
  "id": id,
49
  "frame_to_box": {str(frame_number): box},
50
- "frame_to_conf": {str(frame_number): conf}})
51
  # otherwise, retrieve the jsonline and append the box
52
  else:
53
  result_list[id_list[id]]["frame_to_box"][str(frame_number)] = box
54
- result_list[id_list[id]]["frame_to_conf"][str(frame_number)] = conf
 
 
 
 
 
 
 
55
 
56
  return {"detected_trash": result_list}
57
 
@@ -63,10 +77,11 @@ def count_objects(input_json, class_dict):
63
  results[trash["label"]] += 1
64
  total += 1
65
 
66
- if total==0:
67
  total = 1
68
  return {k+f": {str(v)}":v/total for k,v in results.items()}
69
 
 
70
  def write(results, output_name):
71
  """ Writes the results in two files:
72
  - tracking in a Mathis format xxx_track.txt (frame, id, box_x, box_y, ...)
 
3
  from scipy.signal import convolve
4
  from tracking.utils import write_tracking_results_to_file, read_tracking_results
5
  from collections import defaultdict
6
+ from statistics import mode
7
 
8
  import json
9
 
 
22
  for det in dets:
23
  results.append((det[0], tracker_nb, det[1], det[2], det[3], det[4]))
24
 
 
 
 
 
25
  results = sorted(results, key=lambda x: x[0])
26
  return results
27
 
28
 
29
+ def process_class_and_confidences(class_confs):
30
+ ''' Finds the majority and most confident class from list [(classid, conf), ...]
31
+ '''
32
+ d = defaultdict(lambda: (0, 0.0))
33
+ for (cls, conf) in class_confs:
34
+ d[cls] = (d[cls][0] + 1, d[cls][1] + conf)
35
+ best_class = sorted(d.items(), key=lambda v: v[1][0]+v[1][1])[-1]
36
+ return best_class[0], round(best_class[1][1]/best_class[1][0],2)
37
+
38
+
39
  def postprocess_for_api(results, class_dict=defaultdict(lambda: "fragment")):
40
  """ Converts tracking results into json object for API
41
  """
 
54
  result_list.append({"label":classname,
55
  "id": id,
56
  "frame_to_box": {str(frame_number): box},
57
+ "frame_to_class_conf": {str(frame_number): (res[5], conf)}})
58
  # otherwise, retrieve the jsonline and append the box
59
  else:
60
  result_list[id_list[id]]["frame_to_box"][str(frame_number)] = box
61
+ result_list[id_list[id]]["frame_to_class_conf"][str(frame_number)] = (res[5], conf)
62
+
63
+ # Finally, collapse the confidence and class
64
+ for res in result_list:
65
+ classid, avg_conf = process_class_and_confidences(res.pop("frame_to_class_conf").values())
66
+ res["avg_conf"] = avg_conf
67
+ # update the label
68
+ res["label"] = class_dict[classid]
69
 
70
  return {"detected_trash": result_list}
71
 
 
77
  results[trash["label"]] += 1
78
  total += 1
79
 
80
+ if total==0:
81
  total = 1
82
  return {k+f": {str(v)}":v/total for k,v in results.items()}
83
 
84
+
85
  def write(results, output_name):
86
  """ Writes the results in two files:
87
  - tracking in a Mathis format xxx_track.txt (frame, id, box_x, box_y, ...)
tracking/track_video.py CHANGED
@@ -2,56 +2,22 @@ import cv2
2
  import numpy as np
3
  import os
4
  from detection.detect import detect
5
- from tracking.utils import in_frame, init_trackers
6
  from tools.optical_flow import compute_flow
7
  from tracking.trackers import get_tracker
8
- import matplotlib.pyplot as plt
9
  from scipy.spatial.distance import euclidean
10
  from scipy.optimize import linear_sum_assignment
11
  import torch
12
 
13
 
14
- class Display:
15
- def __init__(self, on, interactive=True):
16
- self.on = on
17
- self.fig, self.ax = plt.subplots()
18
- self.interactive = interactive
19
- if interactive:
20
- plt.ion()
21
- self.colors = plt.rcParams['axes.prop_cycle'].by_key()['color']
22
- self.legends = []
23
- self.plot_count = 0
24
-
25
- def display(self, trackers):
26
-
27
- something_to_show = False
28
- for tracker_nb, tracker in enumerate(trackers):
29
- if tracker.enabled:
30
- tracker.fill_display(self, tracker_nb)
31
- something_to_show = True
32
-
33
- self.ax.imshow(self.latest_frame_to_show)
34
-
35
- if len(self.latest_detections):
36
- self.ax.scatter(self.latest_detections[:, 0], self.latest_detections[:, 1], c='r', s=40)
37
-
38
- if something_to_show:
39
- self.ax.xaxis.tick_top()
40
- plt.legend(handles=self.legends)
41
- self.fig.canvas.draw()
42
- if self.interactive:
43
- plt.show()
44
- while not plt.waitforbuttonpress():
45
- continue
46
- else:
47
- plt.savefig(os.path.join('plots',str(self.plot_count)))
48
- self.ax.cla()
49
- self.legends = []
50
- self.plot_count+=1
51
-
52
- def update_detections_and_frame(self, latest_detections, frame):
53
- self.latest_detections = latest_detections
54
- self.latest_frame_to_show = cv2.cvtColor(cv2.resize(frame, self.display_shape), cv2.COLOR_BGR2RGB)
55
 
56
 
57
  def build_confidence_function_for_trackers(trackers, flow01):
@@ -63,19 +29,23 @@ def build_confidence_function_for_trackers(trackers, flow01):
63
  confidence_functions.append(tracker.build_confidence_function(flow01))
64
  return tracker_nbs, confidence_functions
65
 
66
- def associate_detections_to_trackers(detections_for_frame, trackers, flow01, confidence_threshold):
 
67
  tracker_nbs, confidence_functions = build_confidence_function_for_trackers(trackers, flow01)
68
  assigned_trackers = [None]*len(detections_for_frame)
69
  if len(tracker_nbs):
70
  cost_matrix = np.zeros(shape=(len(detections_for_frame),len(tracker_nbs)))
71
- for detection_nb, detection in enumerate(detections_for_frame):
72
  for tracker_id, confidence_function in enumerate(confidence_functions):
73
  score = confidence_function(detection)
 
 
 
74
  if score > confidence_threshold:
75
- cost_matrix[detection_nb,tracker_id] = score
76
  else:
77
- cost_matrix[detection_nb,tracker_id] = 0
78
- row_inds, col_inds = linear_sum_assignment(cost_matrix,maximize=True)
79
  for row_ind, col_ind in zip(row_inds, col_inds):
80
  if cost_matrix[row_ind,col_ind] > confidence_threshold: assigned_trackers[row_ind] = tracker_nbs[col_ind]
81
 
@@ -83,8 +53,7 @@ def associate_detections_to_trackers(detections_for_frame, trackers, flow01, con
83
 
84
 
85
  def interpret_detection(detections_for_frame, downsampling_factor, is_yolo=False):
86
- """
87
- normalizes the detections depending whether they come from centernet or yolo
88
  """
89
  if not is_yolo:
90
  confs = [0.0]*len(detections_for_frame)
@@ -112,7 +81,6 @@ def track_video(reader, detections, args, engine, transition_variance, observati
112
  delta = 0.005*max_distance
113
 
114
  if display is not None and display.on:
115
-
116
  display.display_shape = (reader.output_shape[0] // args.downsampling_factor, reader.output_shape[1] // args.downsampling_factor)
117
  display.update_detections_and_frame(detections_for_frame, frame0)
118
 
@@ -132,15 +100,12 @@ def track_video(reader, detections, args, engine, transition_variance, observati
132
  if len(detections_for_frame):
133
  trackers = init_trackers(engine, detections_for_frame, confs, labels, frame_nb, transition_variance, observation_variance, delta)
134
  init = True
135
-
136
  else:
137
-
138
  new_trackers = []
139
  flow01 = compute_flow(frame0, frame1, args.downsampling_factor)
140
 
141
  if len(detections_for_frame):
142
-
143
- assigned_trackers = associate_detections_to_trackers(detections_for_frame, trackers,
144
  flow01, args.confidence_threshold)
145
 
146
  for detection, conf, label, assigned_tracker in zip(detections_for_frame, confs, labels, assigned_trackers):
 
2
  import numpy as np
3
  import os
4
  from detection.detect import detect
5
+ from tracking.utils import in_frame
6
  from tools.optical_flow import compute_flow
7
  from tracking.trackers import get_tracker
 
8
  from scipy.spatial.distance import euclidean
9
  from scipy.optimize import linear_sum_assignment
10
  import torch
11
 
12
 
13
+ def init_trackers(engine, detections, confs, labels, frame_nb, state_variance, observation_variance, delta):
14
+ """ Initializes the trackers based on detections
15
+ """
16
+ trackers = []
17
+ for detection, conf, label in zip(detections, confs, labels):
18
+ tracker_for_detection = engine(frame_nb, detection, conf, label, state_variance, observation_variance, delta)
19
+ trackers.append(tracker_for_detection)
20
+ return trackers
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
 
23
  def build_confidence_function_for_trackers(trackers, flow01):
 
29
  confidence_functions.append(tracker.build_confidence_function(flow01))
30
  return tracker_nbs, confidence_functions
31
 
32
+
33
+ def associate_detections_to_trackers(detections_for_frame, confs, labels, trackers, flow01, confidence_threshold):
34
  tracker_nbs, confidence_functions = build_confidence_function_for_trackers(trackers, flow01)
35
  assigned_trackers = [None]*len(detections_for_frame)
36
  if len(tracker_nbs):
37
  cost_matrix = np.zeros(shape=(len(detections_for_frame),len(tracker_nbs)))
38
+ for detection_nb, (detection, conf, label) in enumerate(zip(detections_for_frame, confs, labels)):
39
  for tracker_id, confidence_function in enumerate(confidence_functions):
40
  score = confidence_function(detection)
41
+ cls_score = trackers[tracker_id].cls_score_function(conf, label)
42
+ if cls_score < 0.5:
43
+ score = score * 0.1 # if wrong class, reduce the score, to tweak
44
  if score > confidence_threshold:
45
+ cost_matrix[detection_nb, tracker_id] = score
46
  else:
47
+ cost_matrix[detection_nb, tracker_id] = 0
48
+ row_inds, col_inds = linear_sum_assignment(cost_matrix, maximize=True)
49
  for row_ind, col_ind in zip(row_inds, col_inds):
50
  if cost_matrix[row_ind,col_ind] > confidence_threshold: assigned_trackers[row_ind] = tracker_nbs[col_ind]
51
 
 
53
 
54
 
55
  def interpret_detection(detections_for_frame, downsampling_factor, is_yolo=False):
56
+ """ normalizes the detections depending whether they come from centernet or yolo
 
57
  """
58
  if not is_yolo:
59
  confs = [0.0]*len(detections_for_frame)
 
81
  delta = 0.005*max_distance
82
 
83
  if display is not None and display.on:
 
84
  display.display_shape = (reader.output_shape[0] // args.downsampling_factor, reader.output_shape[1] // args.downsampling_factor)
85
  display.update_detections_and_frame(detections_for_frame, frame0)
86
 
 
100
  if len(detections_for_frame):
101
  trackers = init_trackers(engine, detections_for_frame, confs, labels, frame_nb, transition_variance, observation_variance, delta)
102
  init = True
 
103
  else:
 
104
  new_trackers = []
105
  flow01 = compute_flow(frame0, frame1, args.downsampling_factor)
106
 
107
  if len(detections_for_frame):
108
+ assigned_trackers = associate_detections_to_trackers(detections_for_frame, confs, labels, trackers,
 
109
  flow01, args.confidence_threshold)
110
 
111
  for detection, conf, label, assigned_tracker in zip(detections_for_frame, confs, labels, assigned_trackers):
tracking/trackers.py CHANGED
@@ -8,7 +8,6 @@ import matplotlib.patches as mpatches
8
  class Tracker:
9
 
10
  def __init__(self, frame_nb, X0, confidence, class_id, transition_variance, observation_variance, delta):
11
-
12
  self.transition_covariance = np.diag(transition_variance)
13
  self.observation_covariance = np.diag(observation_variance)
14
  self.updated = False
@@ -49,12 +48,20 @@ class Tracker:
49
 
50
  return lambda coord: confidence_from_multivariate_distribution(coord, distribution)
51
 
 
 
 
 
 
 
 
52
  def get_display_colors(self, display, tracker_nb):
53
  colors = display.colors
54
  color = colors[tracker_nb % len(colors)]
55
  display.legends.append(mpatches.Patch(color=color, label=len(self.tracklet)))
56
  return colors[tracker_nb % len(colors)]
57
 
 
58
  class SMC(Tracker):
59
  def set_param(param):
60
  SMC.n_particles = int(param)
 
8
  class Tracker:
9
 
10
  def __init__(self, frame_nb, X0, confidence, class_id, transition_variance, observation_variance, delta):
 
11
  self.transition_covariance = np.diag(transition_variance)
12
  self.observation_covariance = np.diag(observation_variance)
13
  self.updated = False
 
48
 
49
  return lambda coord: confidence_from_multivariate_distribution(coord, distribution)
50
 
51
+ def cls_score_function(self, conf, label):
52
+ """ generates a score based on classes associated with observation in this tracker
53
+ """
54
+ class_conf = sum([tr[2] for tr in self.tracklet if tr[3]==label])
55
+ other_conf = sum([tr[2] for tr in self.tracklet])
56
+ return (class_conf+conf) / (other_conf+conf)
57
+
58
  def get_display_colors(self, display, tracker_nb):
59
  colors = display.colors
60
  color = colors[tracker_nb % len(colors)]
61
  display.legends.append(mpatches.Patch(color=color, label=len(self.tracklet)))
62
  return colors[tracker_nb % len(colors)]
63
 
64
+
65
  class SMC(Tracker):
66
  def set_param(param):
67
  SMC.n_particles = int(param)
tracking/utils.py CHANGED
@@ -10,8 +10,10 @@ from detection.transforms import TransformFrames
10
  from collections import defaultdict
11
  from skimage.transform import downscale_local_mean
12
  from skvideo.io import FFmpegWriter
 
13
  from tracking.gps import get_media_duration
14
 
 
15
  class GaussianMixture(object):
16
  def __init__(self, means, covariance, weights):
17
  self.components = [multivariate_normal(
@@ -33,22 +35,13 @@ class GaussianMixture(object):
33
  result += weight*component.cdf(x)
34
  return result
35
 
36
- def init_trackers(engine, detections, confs, labels, frame_nb, state_variance, observation_variance, delta):
37
- trackers = []
38
-
39
- for detection, conf, label in zip(detections, confs, labels):
40
- tracker_for_detection = engine(frame_nb, detection, conf, label, state_variance, observation_variance, delta)
41
- trackers.append(tracker_for_detection)
42
-
43
- return trackers
44
 
45
  def exp_and_normalise(lw):
46
  w = np.exp(lw - lw.max())
47
  return w / w.sum()
48
 
49
- def in_frame(position, shape, border=0.02):
50
-
51
 
 
52
  shape_x = shape[1]
53
  shape_y = shape[0]
54
  x = position[0]
@@ -56,6 +49,7 @@ def in_frame(position, shape, border=0.02):
56
 
57
  return x > border*shape_x and x < (1-border)*shape_x and y > border*shape_y and y < (1-border)*shape_y
58
 
 
59
  def gather_filenames_for_video_in_annotations(video, images, data_dir):
60
  images_for_video = [image for image in images
61
  if image['video_id'] == video['id']]
@@ -65,8 +59,8 @@ def gather_filenames_for_video_in_annotations(video, images, data_dir):
65
  return [os.path.join(data_dir, image['file_name'])
66
  for image in images_for_video]
67
 
68
- def get_detections_for_video(reader, detector, batch_size=16, device=None):
69
 
 
70
  detections = []
71
  dataset = TorchIterableFromReader(reader, TransformFrames())
72
  loader = DataLoader(dataset, batch_size=batch_size)
@@ -244,6 +238,7 @@ def gather_tracklets(tracklist):
244
  tracklets = list(tracklets.values())
245
  return tracklets
246
 
 
247
  class FramesWithInfo:
248
  def __init__(self, frames, output_shape=None):
249
  self.frames = frames
@@ -264,3 +259,47 @@ class FramesWithInfo:
264
 
265
  def __iter__(self):
266
  return self
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  from collections import defaultdict
11
  from skimage.transform import downscale_local_mean
12
  from skvideo.io import FFmpegWriter
13
+ import matplotlib.pyplot as plt
14
  from tracking.gps import get_media_duration
15
 
16
+
17
  class GaussianMixture(object):
18
  def __init__(self, means, covariance, weights):
19
  self.components = [multivariate_normal(
 
35
  result += weight*component.cdf(x)
36
  return result
37
 
 
 
 
 
 
 
 
 
38
 
39
  def exp_and_normalise(lw):
40
  w = np.exp(lw - lw.max())
41
  return w / w.sum()
42
 
 
 
43
 
44
+ def in_frame(position, shape, border=0.02):
45
  shape_x = shape[1]
46
  shape_y = shape[0]
47
  x = position[0]
 
49
 
50
  return x > border*shape_x and x < (1-border)*shape_x and y > border*shape_y and y < (1-border)*shape_y
51
 
52
+
53
  def gather_filenames_for_video_in_annotations(video, images, data_dir):
54
  images_for_video = [image for image in images
55
  if image['video_id'] == video['id']]
 
59
  return [os.path.join(data_dir, image['file_name'])
60
  for image in images_for_video]
61
 
 
62
 
63
+ def get_detections_for_video(reader, detector, batch_size=16, device=None):
64
  detections = []
65
  dataset = TorchIterableFromReader(reader, TransformFrames())
66
  loader = DataLoader(dataset, batch_size=batch_size)
 
238
  tracklets = list(tracklets.values())
239
  return tracklets
240
 
241
+
242
  class FramesWithInfo:
243
  def __init__(self, frames, output_shape=None):
244
  self.frames = frames
 
259
 
260
  def __iter__(self):
261
  return self
262
+
263
+ class Display:
264
+ """ Display tracking
265
+ """
266
+ def __init__(self, on, interactive=True):
267
+ self.on = on
268
+ self.fig, self.ax = plt.subplots()
269
+ self.interactive = interactive
270
+ if interactive:
271
+ plt.ion()
272
+ self.colors = plt.rcParams['axes.prop_cycle'].by_key()['color']
273
+ self.legends = []
274
+ self.plot_count = 0
275
+
276
+ def display(self, trackers):
277
+
278
+ something_to_show = False
279
+ for tracker_nb, tracker in enumerate(trackers):
280
+ if tracker.enabled:
281
+ tracker.fill_display(self, tracker_nb)
282
+ something_to_show = True
283
+
284
+ self.ax.imshow(self.latest_frame_to_show)
285
+
286
+ if len(self.latest_detections):
287
+ self.ax.scatter(self.latest_detections[:, 0], self.latest_detections[:, 1], c='r', s=40)
288
+
289
+ if something_to_show:
290
+ self.ax.xaxis.tick_top()
291
+ plt.legend(handles=self.legends)
292
+ self.fig.canvas.draw()
293
+ if self.interactive:
294
+ plt.show()
295
+ while not plt.waitforbuttonpress():
296
+ continue
297
+ else:
298
+ plt.savefig(os.path.join('plots',str(self.plot_count)))
299
+ self.ax.cla()
300
+ self.legends = []
301
+ self.plot_count+=1
302
+
303
+ def update_detections_and_frame(self, latest_detections, frame):
304
+ self.latest_detections = latest_detections
305
+ self.latest_frame_to_show = cv2.cvtColor(cv2.resize(frame, self.display_shape), cv2.COLOR_BGR2RGB)