generate_detections.py
Go to the documentation of this file.
1 # vim: expandtab:ts=4:sw=4
2 import os
3 import errno
4 import argparse
5 import numpy as np
6 import cv2
7 import tensorflow as tf
8 
9 
10 def _run_in_batches(f, data_dict, out, batch_size):
11  data_len = len(out)
12  num_batches = int(data_len / batch_size)
13 
14  s, e = 0, 0
15  for i in range(num_batches):
16  s, e = i * batch_size, (i + 1) * batch_size
17  batch_data_dict = {k: v[s:e] for k, v in data_dict.items()}
18  out[s:e] = f(batch_data_dict)
19  if e < len(out):
20  batch_data_dict = {k: v[e:] for k, v in data_dict.items()}
21  out[e:] = f(batch_data_dict)
22 
23 
24 def extract_image_patch(image, bbox, patch_shape):
25  """Extract image patch from bounding box.
26 
27  Parameters
28  ----------
29  image : ndarray
30  The full image.
31  bbox : array_like
32  The bounding box in format (x, y, width, height).
33  patch_shape : Optional[array_like]
34  This parameter can be used to enforce a desired patch shape
35  (height, width). First, the `bbox` is adapted to the aspect ratio
36  of the patch shape, then it is clipped at the image boundaries.
37  If None, the shape is computed from :arg:`bbox`.
38 
39  Returns
40  -------
41  ndarray | NoneType
42  An image patch showing the :arg:`bbox`, optionally reshaped to
43  :arg:`patch_shape`.
44  Returns None if the bounding box is empty or fully outside of the image
45  boundaries.
46 
47  """
48  bbox = np.array(bbox)
49  if patch_shape is not None:
50  # correct aspect ratio to patch shape
51  target_aspect = float(patch_shape[1]) / patch_shape[0]
52  new_width = target_aspect * bbox[3]
53  bbox[0] -= (new_width - bbox[2]) / 2
54  bbox[2] = new_width
55 
56  # convert to top left, bottom right
57  bbox[2:] += bbox[:2]
58  bbox = bbox.astype(np.int)
59 
60  # clip at image boundaries
61  bbox[:2] = np.maximum(0, bbox[:2])
62  bbox[2:] = np.minimum(np.asarray(image.shape[:2][::-1]) - 1, bbox[2:])
63  if np.any(bbox[:2] >= bbox[2:]):
64  return None
65  sx, sy, ex, ey = bbox
66  image = image[sy:ey, sx:ex]
67  image = cv2.resize(image, tuple(patch_shape[::-1]))
68  return image
69 
70 
72 
73  def __init__(self, checkpoint_filename, input_name="images",
74  output_name="features"):
75  self.session = tf.Session()
76  with tf.gfile.GFile(checkpoint_filename, "rb") as file_handle:
77  graph_def = tf.GraphDef()
78  graph_def.ParseFromString(file_handle.read())
79  tf.import_graph_def(graph_def, name="net")
80  self.input_var = tf.get_default_graph().get_tensor_by_name(
81  "net/%s:0" % input_name)
82  self.output_var = tf.get_default_graph().get_tensor_by_name(
83  "net/%s:0" % output_name)
84 
85  assert len(self.output_var.get_shape()) == 2
86  assert len(self.input_var.get_shape()) == 4
87  self.feature_dim = self.output_var.get_shape().as_list()[-1]
88  self.image_shape = self.input_var.get_shape().as_list()[1:]
89 
90  def __call__(self, data_x, batch_size=32):
91  out = np.zeros((len(data_x), self.feature_dim), np.float32)
93  lambda x: self.session.run(self.output_var, feed_dict=x),
94  {self.input_var: data_x}, out, batch_size)
95  return out
96 
97 
98 def create_box_encoder(model_filename, input_name="images",
99  output_name="features", batch_size=32):
100  image_encoder = ImageEncoder(model_filename, input_name, output_name)
101  image_shape = image_encoder.image_shape
102 
103  def encoder(image, boxes):
104  image_patches = []
105  for box in boxes:
106  patch = extract_image_patch(image, box, image_shape[:2])
107  if patch is None:
108  print("WARNING: Failed to extract image patch: %s." % str(box))
109  patch = np.random.uniform(
110  0., 255., image_shape).astype(np.uint8)
111  image_patches.append(patch)
112  image_patches = np.asarray(image_patches)
113  return image_encoder(image_patches, batch_size)
114 
115  return encoder
116 
117 
118 def generate_detections(encoder, mot_dir, output_dir, detection_dir=None):
119  """Generate detections with features.
120 
121  Parameters
122  ----------
123  encoder : Callable[image, ndarray] -> ndarray
124  The encoder function takes as input a BGR color image and a matrix of
125  bounding boxes in format `(x, y, w, h)` and returns a matrix of
126  corresponding feature vectors.
127  mot_dir : str
128  Path to the MOTChallenge directory (can be either train or test).
129  output_dir
130  Path to the output directory. Will be created if it does not exist.
131  detection_dir
132  Path to custom detections. The directory structure should be the default
133  MOTChallenge structure: `[sequence]/det/det.txt`. If None, uses the
134  standard MOTChallenge detections.
135 
136  """
137  if detection_dir is None:
138  detection_dir = mot_dir
139  try:
140  os.makedirs(output_dir)
141  except OSError as exception:
142  if exception.errno == errno.EEXIST and os.path.isdir(output_dir):
143  pass
144  else:
145  raise ValueError(
146  "Failed to created output directory '%s'" % output_dir)
147 
148  for sequence in os.listdir(mot_dir):
149  print("Processing %s" % sequence)
150  sequence_dir = os.path.join(mot_dir, sequence)
151 
152  image_dir = os.path.join(sequence_dir, "img1")
153  image_filenames = {
154  int(os.path.splitext(f)[0]): os.path.join(image_dir, f)
155  for f in os.listdir(image_dir)}
156 
157  detection_file = os.path.join(
158  detection_dir, sequence, "det/det.txt")
159  detections_in = np.loadtxt(detection_file, delimiter=',')
160  detections_out = []
161 
162  frame_indices = detections_in[:, 0].astype(np.int)
163  min_frame_idx = frame_indices.astype(np.int).min()
164  max_frame_idx = frame_indices.astype(np.int).max()
165  for frame_idx in range(min_frame_idx, max_frame_idx + 1):
166  print("Frame %05d/%05d" % (frame_idx, max_frame_idx))
167  mask = frame_indices == frame_idx
168  rows = detections_in[mask]
169 
170  if frame_idx not in image_filenames:
171  print("WARNING could not find image for frame %d" % frame_idx)
172  continue
173  bgr_image = cv2.imread(
174  image_filenames[frame_idx], cv2.IMREAD_COLOR)
175  features = encoder(bgr_image, rows[:, 2:6].copy())
176  detections_out += [np.r_[(row, feature)] for row, feature
177  in zip(rows, features)]
178 
179  output_filename = os.path.join(output_dir, "%s.npy" % sequence)
180  np.save(
181  output_filename, np.asarray(detections_out), allow_pickle=False)
182 
183 
185  """Parse command line arguments.
186  """
187  parser = argparse.ArgumentParser(description="Re-ID feature extractor")
188  parser.add_argument(
189  "--model",
190  default="resources/networks/mars-small128.pb",
191  help="Path to freezed inference graph protobuf.")
192  parser.add_argument(
193  "--mot_dir", help="Path to MOTChallenge directory (train or test)",
194  required=True)
195  parser.add_argument(
196  "--detection_dir", help="Path to custom detections. Defaults to "
197  "standard MOT detections Directory structure should be the default "
198  "MOTChallenge structure: [sequence]/det/det.txt", default=None)
199  parser.add_argument(
200  "--output_dir", help="Output directory. Will be created if it does not"
201  " exist.", default="detections")
202  return parser.parse_args()
203 
204 
205 def main():
206  args = parse_args()
207  encoder = create_box_encoder(args.model, batch_size=32)
208  generate_detections(encoder, args.mot_dir, args.output_dir,
209  args.detection_dir)
210 
211 
212 if __name__ == "__main__":
213  main()
def _run_in_batches(f, data_dict, out, batch_size)
def create_box_encoder(model_filename, input_name="images", output_name="features", batch_size=32)
def __init__(self, checkpoint_filename, input_name="images", output_name="features")
def __call__(self, data_x, batch_size=32)
def extract_image_patch(image, bbox, patch_shape)
def generate_detections(encoder, mot_dir, output_dir, detection_dir=None)


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