deep_sort_app.py
Go to the documentation of this file.
1 # vim: expandtab:ts=4:sw=4
2 from __future__ import division, print_function, absolute_import
3 
4 import argparse
5 import os
6 
7 import cv2
8 import numpy as np
9 
10 from application_util import preprocessing
11 from application_util import visualization
12 from deep_sort import nn_matching
13 from deep_sort.detection import Detection
14 from deep_sort.tracker import Tracker
15 
16 
17 def gather_sequence_info(sequence_dir, detection_file):
18  """Gather sequence information, such as image filenames, detections,
19  groundtruth (if available).
20 
21  Parameters
22  ----------
23  sequence_dir : str
24  Path to the MOTChallenge sequence directory.
25  detection_file : str
26  Path to the detection file.
27 
28  Returns
29  -------
30  Dict
31  A dictionary of the following sequence information:
32 
33  * sequence_name: Name of the sequence
34  * image_filenames: A dictionary that maps frame indices to image
35  filenames.
36  * detections: A numpy array of detections in MOTChallenge format.
37  * groundtruth: A numpy array of ground truth in MOTChallenge format.
38  * image_size: Image size (height, width).
39  * min_frame_idx: Index of the first frame.
40  * max_frame_idx: Index of the last frame.
41 
42  """
43  image_dir = os.path.join(sequence_dir, "img1")
44  image_filenames = {
45  int(os.path.splitext(f)[0]): os.path.join(image_dir, f)
46  for f in os.listdir(image_dir)}
47  groundtruth_file = os.path.join(sequence_dir, "gt/gt.txt")
48 
49  detections = None
50  if detection_file is not None:
51  detections = np.load(detection_file)
52  groundtruth = None
53  if os.path.exists(groundtruth_file):
54  groundtruth = np.loadtxt(groundtruth_file, delimiter=',')
55 
56  if len(image_filenames) > 0:
57  image = cv2.imread(next(iter(image_filenames.values())),
58  cv2.IMREAD_GRAYSCALE)
59  image_size = image.shape
60  else:
61  image_size = None
62 
63  if len(image_filenames) > 0:
64  min_frame_idx = min(image_filenames.keys())
65  max_frame_idx = max(image_filenames.keys())
66  else:
67  min_frame_idx = int(detections[:, 0].min())
68  max_frame_idx = int(detections[:, 0].max())
69 
70  info_filename = os.path.join(sequence_dir, "seqinfo.ini")
71  if os.path.exists(info_filename):
72  with open(info_filename, "r") as f:
73  line_splits = [l.split('=') for l in f.read().splitlines()[1:]]
74  info_dict = dict(
75  s for s in line_splits if isinstance(s, list) and len(s) == 2)
76 
77  update_ms = 1000 / int(info_dict["frameRate"])
78  else:
79  update_ms = None
80 
81  feature_dim = detections.shape[1] - 10 if detections is not None else 0
82  seq_info = {
83  "sequence_name": os.path.basename(sequence_dir),
84  "image_filenames": image_filenames,
85  "detections": detections,
86  "groundtruth": groundtruth,
87  "image_size": image_size,
88  "min_frame_idx": min_frame_idx,
89  "max_frame_idx": max_frame_idx,
90  "feature_dim": feature_dim,
91  "update_ms": update_ms
92  }
93  return seq_info
94 
95 
96 def create_detections(detection_mat, frame_idx, min_height=0):
97  """Create detections for given frame index from the raw detection matrix.
98 
99  Parameters
100  ----------
101  detection_mat : ndarray
102  Matrix of detections. The first 10 columns of the detection matrix are
103  in the standard MOTChallenge detection format. In the remaining columns
104  store the feature vector associated with each detection.
105  frame_idx : int
106  The frame index.
107  min_height : Optional[int]
108  A minimum detection bounding box height. Detections that are smaller
109  than this value are disregarded.
110 
111  Returns
112  -------
113  List[tracker.Detection]
114  Returns detection responses at given frame index.
115 
116  """
117  frame_indices = detection_mat[:, 0].astype(np.int)
118  mask = frame_indices == frame_idx
119 
120  detection_list = []
121  for row in detection_mat[mask]:
122  bbox, confidence, feature = row[2:6], row[6], row[10:]
123  if bbox[3] < min_height:
124  continue
125  detection_list.append(Detection(bbox, confidence, feature))
126  return detection_list
127 
128 
129 def run(sequence_dir, detection_file, output_file, min_confidence,
130  nms_max_overlap, min_detection_height, max_cosine_distance,
131  nn_budget, display):
132  """Run multi-target tracker on a particular sequence.
133 
134  Parameters
135  ----------
136  sequence_dir : str
137  Path to the MOTChallenge sequence directory.
138  detection_file : str
139  Path to the detections file.
140  output_file : str
141  Path to the tracking output file. This file will contain the tracking
142  results on completion.
143  min_confidence : float
144  Detection confidence threshold. Disregard all detections that have
145  a confidence lower than this value.
146  nms_max_overlap: float
147  Maximum detection overlap (non-maxima suppression threshold).
148  min_detection_height : int
149  Detection height threshold. Disregard all detections that have
150  a height lower than this value.
151  max_cosine_distance : float
152  Gating threshold for cosine distance metric (object appearance).
153  nn_budget : Optional[int]
154  Maximum size of the appearance descriptor gallery. If None, no budget
155  is enforced.
156  display : bool
157  If True, show visualization of intermediate tracking results.
158 
159  """
160  seq_info = gather_sequence_info(sequence_dir, detection_file)
161  metric = nn_matching.NearestNeighborDistanceMetric(
162  "cosine", max_cosine_distance, nn_budget)
163  tracker = Tracker(metric)
164  results = []
165 
166  def frame_callback(vis, frame_idx):
167  print("Processing frame %05d" % frame_idx)
168 
169  # Load image and generate detections.
170  detections = create_detections(
171  seq_info["detections"], frame_idx, min_detection_height)
172  detections = [d for d in detections if d.confidence >= min_confidence]
173 
174  # Run non-maxima suppression.
175  boxes = np.array([d.tlwh for d in detections])
176  scores = np.array([d.confidence for d in detections])
177  indices = preprocessing.non_max_suppression(
178  boxes, nms_max_overlap, scores)
179  detections = [detections[i] for i in indices]
180 
181  # Update tracker.
182  tracker.predict()
183  tracker.update(detections)
184 
185  # Update visualization.
186  if display:
187  image = cv2.imread(
188  seq_info["image_filenames"][frame_idx], cv2.IMREAD_COLOR)
189  vis.set_image(image.copy())
190  vis.draw_detections(detections)
191  vis.draw_trackers(tracker.tracks)
192 
193  # Store results.
194  for track in tracker.tracks:
195  if not track.is_confirmed() or track.time_since_update > 1:
196  continue
197  bbox = track.to_tlwh()
198  results.append([
199  frame_idx, track.track_id, bbox[0], bbox[1], bbox[2], bbox[3]])
200 
201  # Run tracker.
202  if display:
203  visualizer = visualization.Visualization(seq_info, update_ms=5)
204  else:
205  visualizer = visualization.NoVisualization(seq_info)
206  visualizer.run(frame_callback)
207 
208  # Store results.
209  f = open(output_file, 'w')
210  for row in results:
211  print('%d,%d,%.2f,%.2f,%.2f,%.2f,1,-1,-1,-1' % (
212  row[0], row[1], row[2], row[3], row[4], row[5]),file=f)
213 
214 
216  """ Parse command line arguments.
217  """
218  parser = argparse.ArgumentParser(description="Deep SORT")
219  parser.add_argument(
220  "--sequence_dir", help="Path to MOTChallenge sequence directory",
221  default=None, required=True)
222  parser.add_argument(
223  "--detection_file", help="Path to custom detections.", default=None,
224  required=True)
225  parser.add_argument(
226  "--output_file", help="Path to the tracking output file. This file will"
227  " contain the tracking results on completion.",
228  default="/tmp/hypotheses.txt")
229  parser.add_argument(
230  "--min_confidence", help="Detection confidence threshold. Disregard "
231  "all detections that have a confidence lower than this value.",
232  default=0.8, type=float)
233  parser.add_argument(
234  "--min_detection_height", help="Threshold on the detection bounding "
235  "box height. Detections with height smaller than this value are "
236  "disregarded", default=0, type=int)
237  parser.add_argument(
238  "--nms_max_overlap", help="Non-maxima suppression threshold: Maximum "
239  "detection overlap.", default=1.0, type=float)
240  parser.add_argument(
241  "--max_cosine_distance", help="Gating threshold for cosine distance "
242  "metric (object appearance).", type=float, default=0.2)
243  parser.add_argument(
244  "--nn_budget", help="Maximum size of the appearance descriptors "
245  "gallery. If None, no budget is enforced.", type=int, default=None)
246  parser.add_argument(
247  "--display", help="Show intermediate tracking results",
248  default=True, type=bool)
249  return parser.parse_args()
250 
251 
252 if __name__ == "__main__":
253  args = parse_args()
254  run(
255  args.sequence_dir, args.detection_file, args.output_file,
256  args.min_confidence, args.nms_max_overlap, args.min_detection_height,
257  args.max_cosine_distance, args.nn_budget, args.display)
def create_detections(detection_mat, frame_idx, min_height=0)
def run(sequence_dir, detection_file, output_file, min_confidence, nms_max_overlap, min_detection_height, max_cosine_distance, nn_budget, display)
def gather_sequence_info(sequence_dir, detection_file)


jsk_perception
Author(s): Manabu Saito, Ryohei Ueda
autogenerated on Mon May 3 2021 03:03:27