• API
• FAQ
• Tools
• Archive
SHARE
TWEET # Untitled a guest May 23rd, 2019 50 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
1. """
3. Common utility functions and classes.
4. Copyright (c) 2017 Matterport, Inc.
5. Licensed under the MIT License (see LICENSE for details)
6. Written by Waleed Abdulla
7. """
8.
9. import sys
10. import os
11. import logging
12. import math
13. import random
14. import numpy as np
15. import tensorflow as tf
16. import scipy
17. import skimage.color
18. import skimage.io
19. import skimage.transform
20. import urllib.request
21. import shutil
22. import warnings
23. from distutils.version import LooseVersion
24.
25. # URL from which to download the latest COCO trained weights
27.
28.
29. ############################################################
30. #  Bounding Boxes
31. ############################################################
32.
34.     """Compute bounding boxes from masks.
35.     mask: [height, width, num_instances]. Mask pixels are either 1 or 0.
36.     Returns: bbox array [num_instances, (y1, x1, y2, x2)].
37.     """
38.     boxes = np.zeros([mask.shape[-1], 4], dtype=np.int32)
39.     for i in range(mask.shape[-1]):
40.         m = mask[:, :, i]
41.         # Bounding box.
42.         horizontal_indicies = np.where(np.any(m, axis=0))
43.         vertical_indicies = np.where(np.any(m, axis=1))
44.         if horizontal_indicies.shape:
45.             x1, x2 = horizontal_indicies[[0, -1]]
46.             y1, y2 = vertical_indicies[[0, -1]]
47.             # x2 and y2 should not be part of the box. Increment by 1.
48.             x2 += 1
49.             y2 += 1
50.         else:
51.             # No mask for this instance. Might happen due to
52.             # resizing or cropping. Set bbox to zeros
53.             x1, x2, y1, y2 = 0, 0, 0, 0
54.         boxes[i] = np.array([y1, x1, y2, x2])
55.     return boxes.astype(np.int32)
56.
57.
58. def compute_iou(box, boxes, box_area, boxes_area):
59.     """Calculates IoU of the given box with the array of the given boxes.
60.     box: 1D vector [y1, x1, y2, x2]
61.     boxes: [boxes_count, (y1, x1, y2, x2)]
62.     box_area: float. the area of 'box'
63.     boxes_area: array of length boxes_count.
64.     Note: the areas are passed in rather than calculated here for
65.     efficiency. Calculate once in the caller to avoid duplicate work.
66.     """
67.     # Calculate intersection areas
68.     y1 = np.maximum(box, boxes[:, 0])
69.     y2 = np.minimum(box, boxes[:, 2])
70.     x1 = np.maximum(box, boxes[:, 1])
71.     x2 = np.minimum(box, boxes[:, 3])
72.     intersection = np.maximum(x2 - x1, 0) * np.maximum(y2 - y1, 0)
73.     union = box_area + boxes_area[:] - intersection[:]
74.     iou = intersection / union
75.     return iou
76.
77.
78. def compute_overlaps(boxes1, boxes2):
79.     """Computes IoU overlaps between two sets of boxes.
80.     boxes1, boxes2: [N, (y1, x1, y2, x2)].
81.     For better performance, pass the largest set first and the smaller second.
82.     """
83.     # Areas of anchors and GT boxes
84.     area1 = (boxes1[:, 2] - boxes1[:, 0]) * (boxes1[:, 3] - boxes1[:, 1])
85.     area2 = (boxes2[:, 2] - boxes2[:, 0]) * (boxes2[:, 3] - boxes2[:, 1])
86.
87.     # Compute overlaps to generate matrix [boxes1 count, boxes2 count]
88.     # Each cell contains the IoU value.
89.     overlaps = np.zeros((boxes1.shape, boxes2.shape))
90.     for i in range(overlaps.shape):
91.         box2 = boxes2[i]
92.         overlaps[:, i] = compute_iou(box2, boxes1, area2[i], area1)
93.     return overlaps
94.
95.
97.     """Computes IoU overlaps between two sets of masks.
99.     """
100.
101.     # If either set of masks is empty return empty result
102.     if masks1.shape[-1] == 0 or masks2.shape[-1] == 0:
104.     # flatten masks and compute their areas
107.     area1 = np.sum(masks1, axis=0)
108.     area2 = np.sum(masks2, axis=0)
109.
110.     # intersections and union
112.     union = area1[:, None] + area2[None, :] - intersections
113.     overlaps = intersections / union
114.
115.     return overlaps
116.
117.
118. def non_max_suppression(boxes, scores, threshold):
119.     """Performs non-maximum suppression and returns indices of kept boxes.
120.     boxes: [N, (y1, x1, y2, x2)]. Notice that (y2, x2) lays outside the box.
121.     scores: 1-D array of box scores.
122.     threshold: Float. IoU threshold to use for filtering.
123.     """
124.     assert boxes.shape > 0
125.     if boxes.dtype.kind != "f":
126.         boxes = boxes.astype(np.float32)
127.
128.     # Compute box areas
129.     y1 = boxes[:, 0]
130.     x1 = boxes[:, 1]
131.     y2 = boxes[:, 2]
132.     x2 = boxes[:, 3]
133.     area = (y2 - y1) * (x2 - x1)
134.
135.     # Get indicies of boxes sorted by scores (highest first)
136.     ixs = scores.argsort()[::-1]
137.
138.     pick = []
139.     while len(ixs) > 0:
140.         # Pick top box and add its index to the list
141.         i = ixs
142.         pick.append(i)
143.         # Compute IoU of the picked box with the rest
144.         iou = compute_iou(boxes[i], boxes[ixs[1:]], area[i], area[ixs[1:]])
145.         # Identify boxes with IoU over the threshold. This
146.         # returns indices into ixs[1:], so add 1 to get
147.         # indices into ixs.
148.         remove_ixs = np.where(iou > threshold) + 1
149.         # Remove indices of the picked and overlapped boxes.
150.         ixs = np.delete(ixs, remove_ixs)
151.         ixs = np.delete(ixs, 0)
152.     return np.array(pick, dtype=np.int32)
153.
154.
155. def apply_box_deltas(boxes, deltas):
156.     """Applies the given deltas to the given boxes.
157.     boxes: [N, (y1, x1, y2, x2)]. Note that (y2, x2) is outside the box.
158.     deltas: [N, (dy, dx, log(dh), log(dw))]
159.     """
160.     boxes = boxes.astype(np.float32)
161.     # Convert to y, x, h, w
162.     height = boxes[:, 2] - boxes[:, 0]
163.     width = boxes[:, 3] - boxes[:, 1]
164.     center_y = boxes[:, 0] + 0.5 * height
165.     center_x = boxes[:, 1] + 0.5 * width
166.     # Apply deltas
167.     center_y += deltas[:, 0] * height
168.     center_x += deltas[:, 1] * width
169.     height *= np.exp(deltas[:, 2])
170.     width *= np.exp(deltas[:, 3])
171.     # Convert back to y1, x1, y2, x2
172.     y1 = center_y - 0.5 * height
173.     x1 = center_x - 0.5 * width
174.     y2 = y1 + height
175.     x2 = x1 + width
176.     return np.stack([y1, x1, y2, x2], axis=1)
177.
178.
179. def box_refinement_graph(box, gt_box):
180.     """Compute refinement needed to transform box to gt_box.
181.     box and gt_box are [N, (y1, x1, y2, x2)]
182.     """
183.     box = tf.cast(box, tf.float32)
184.     gt_box = tf.cast(gt_box, tf.float32)
185.
186.     height = box[:, 2] - box[:, 0]
187.     width = box[:, 3] - box[:, 1]
188.     center_y = box[:, 0] + 0.5 * height
189.     center_x = box[:, 1] + 0.5 * width
190.
191.     gt_height = gt_box[:, 2] - gt_box[:, 0]
192.     gt_width = gt_box[:, 3] - gt_box[:, 1]
193.     gt_center_y = gt_box[:, 0] + 0.5 * gt_height
194.     gt_center_x = gt_box[:, 1] + 0.5 * gt_width
195.
196.     dy = (gt_center_y - center_y) / height
197.     dx = (gt_center_x - center_x) / width
198.     dh = tf.log(gt_height / height)
199.     dw = tf.log(gt_width / width)
200.
201.     result = tf.stack([dy, dx, dh, dw], axis=1)
202.     return result
203.
204.
205. def box_refinement(box, gt_box):
206.     """Compute refinement needed to transform box to gt_box.
207.     box and gt_box are [N, (y1, x1, y2, x2)]. (y2, x2) is
208.     assumed to be outside the box.
209.     """
210.     box = box.astype(np.float32)
211.     gt_box = gt_box.astype(np.float32)
212.
213.     height = box[:, 2] - box[:, 0]
214.     width = box[:, 3] - box[:, 1]
215.     center_y = box[:, 0] + 0.5 * height
216.     center_x = box[:, 1] + 0.5 * width
217.
218.     gt_height = gt_box[:, 2] - gt_box[:, 0]
219.     gt_width = gt_box[:, 3] - gt_box[:, 1]
220.     gt_center_y = gt_box[:, 0] + 0.5 * gt_height
221.     gt_center_x = gt_box[:, 1] + 0.5 * gt_width
222.
223.     dy = (gt_center_y - center_y) / height
224.     dx = (gt_center_x - center_x) / width
225.     dh = np.log(gt_height / height)
226.     dw = np.log(gt_width / width)
227.
228.     return np.stack([dy, dx, dh, dw], axis=1)
229.
230.
231. ############################################################
232. #  Dataset
233. ############################################################
234.
235. class Dataset(object):
236.     """The base class for dataset classes.
237.     To use it, create a new class that adds functions specific to the dataset
238.     you want to use. For example:
239.     class CatsAndDogsDataset(Dataset):
241.             ...
243.             ...
244.         def image_reference(self, image_id):
245.             ...
246.     See COCODataset and ShapesDataset as examples.
247.     """
248.
249.     def __init__(self, class_map=None):
250.         self._image_ids = []
251.         self.image_info = []
252.         # Background is always the first class
253.         self.class_info = [{"source": "", "id": 0, "name": "BG"}]
254.         self.source_class_ids = {}
255.
256.     def add_class(self, source, class_id, class_name):
257.         assert "." not in source, "Source name cannot contain a dot"
258.         # Does the class exist already?
259.         for info in self.class_info:
260.             if info['source'] == source and info["id"] == class_id:
261.                 # source.class_id combination already available, skip
262.                 return
263.         # Add the class
264.         self.class_info.append({
265.             "source": source,
266.             "id": class_id,
267.             "name": class_name,
268.         })
269.
270.     def add_image(self, source, image_id, path, **kwargs):
271.         image_info = {
272.             "id": image_id,
273.             "source": source,
274.             "path": path,
275.         }
276.         image_info.update(kwargs)
277.         self.image_info.append(image_info)
278.
279.     def image_reference(self, image_id):
280.         """Return a link to the image in its source Website or details about
281.         the image that help looking it up or debugging it.
282.         Override for your dataset, but pass to this function
283.         if you encounter images not in your dataset.
284.         """
285.         return ""
286.
287.     def prepare(self, class_map=None):
288.         """Prepares the Dataset class for use.
289.         TODO: class map is not supported yet. When done, it should handle mapping
290.               classes from different datasets to the same class ID.
291.         """
292.
293.         def clean_name(name):
294.             """Returns a shorter version of object names for cleaner display."""
295.             return ",".join(name.split(",")[:1])
296.
297.         # Build (or rebuild) everything else from the info dicts.
298.         self.num_classes = len(self.class_info)
299.         self.class_ids = np.arange(self.num_classes)
300.         self.class_names = [clean_name(c["name"]) for c in self.class_info]
301.         self.num_images = len(self.image_info)
302.         self._image_ids = np.arange(self.num_images)
303.
304.         # Mapping from source class and image IDs to internal IDs
305.         self.class_from_source_map = {"{}.{}".format(info['source'], info['id']): id
306.                                       for info, id in zip(self.class_info, self.class_ids)}
307.         self.image_from_source_map = {"{}.{}".format(info['source'], info['id']): id
308.                                       for info, id in zip(self.image_info, self.image_ids)}
309.
310.         # Map sources to class_ids they support
311.         self.sources = list(set([i['source'] for i in self.class_info]))
312.         self.source_class_ids = {}
313.         # Loop over datasets
314.         for source in self.sources:
315.             self.source_class_ids[source] = []
316.             # Find classes that belong to this dataset
317.             for i, info in enumerate(self.class_info):
318.                 # Include BG class in all datasets
319.                 if i == 0 or source == info['source']:
320.                     self.source_class_ids[source].append(i)
321.
322.     def map_source_class_id(self, source_class_id):
323.         """Takes a source class ID and returns the int class ID assigned to it.
324.         For example:
325.         dataset.map_source_class_id("coco.12") -> 23
326.         """
327.         return self.class_from_source_map[source_class_id]
328.
329.     def get_source_class_id(self, class_id, source):
330.         """Map an internal class ID to the corresponding class ID in the source dataset."""
331.         info = self.class_info[class_id]
332.         assert info['source'] == source
333.         return info['id']
334.
335.     @property
336.     def image_ids(self):
337.         return self._image_ids
338.
339.     def source_image_link(self, image_id):
340.         """Returns the path or URL to the image.
341.         Override this to return a URL to the image if it's available online for easy
342.         debugging.
343.         """
344.         return self.image_info[image_id]["path"]
345.
346.     def load_image(self, image_id):
347.         """Load the specified image and return a [H,W,3] Numpy array.
348.         """
349.         # Load image
350.         image = skimage.io.imread(self.image_info[image_id]['path'])
351.         # If grayscale. Convert to RGB for consistency.
352.         if image.ndim != 3:
353.             image = skimage.color.gray2rgb(image)
354.         # If has an alpha channel, remove it for consistency
355.         if image.shape[-1] == 4:
356.             image = image[..., :3]
357.         return image
358.
360.         """Load instance masks for the given image.
361.         Different datasets use different ways to store masks. Override this
362.         method to load instance masks and return them in the form of am
363.         array of binary masks of shape [height, width, instances].
364.         Returns:
365.             masks: A bool array of shape [height, width, instance count] with
366.                 a binary mask per instance.
367.             class_ids: a 1D array of class IDs of the instance masks.
368.         """
369.         # Override this function to load a mask from your dataset.
370.         # Otherwise, it returns an empty mask.
371.         logging.warning("You are using the default load_mask(), maybe you need to define your own one.")
372.         mask = np.empty([0, 0, 0])
373.         class_ids = np.empty(, np.int32)
374.         return mask, class_ids
375.
376.
377. def resize_image(image, min_dim=None, max_dim=None, min_scale=None, mode="square", padding=None):
378.     """Resizes an image keeping the aspect ratio unchanged.
379.     min_dim: if provided, resizes the image such that it's smaller
380.         dimension == min_dim
381.     max_dim: if provided, ensures that the image longest side doesn't
382.         exceed this value.
383.     min_scale: if provided, ensure that the image is scaled up by at least
384.         this percent even if min_dim doesn't require it.
385.     mode: Resizing mode.
386.         none: No resizing. Return the image unchanged.
387.         square: Resize and pad with zeros to get a square image
388.             of size [max_dim, max_dim].
389.         pad64: Pads width and height with zeros to make them multiples of 64.
390.                If min_dim or min_scale are provided, it scales the image up
391.                before padding. max_dim is ignored in this mode.
392.                The multiple of 64 is needed to ensure smooth scaling of feature
393.                maps up and down the 6 levels of the FPN pyramid (2**6=64).
394.         crop: Picks random crops from the image. First, scales the image based
395.               on min_dim and min_scale, then picks a random crop of
396.               size min_dim x min_dim. Can be used in training only.
397.               max_dim is not used in this mode.
398.     Returns:
399.     image: the resized image
400.     window: (y1, x1, y2, x2). If max_dim is provided, padding might
401.         be inserted in the returned image. If so, this window is the
402.         coordinates of the image part of the full image (excluding
403.         the padding). The x2, y2 pixels are not included.
404.     scale: The scale factor used to resize the image
405.     padding: Padding added to the image [(top, bottom), (left, right), (0, 0)]
406.     """
407.     # Keep track of image dtype and return results in the same dtype
408.     image_dtype = image.dtype
409.     # Default window (y1, x1, y2, x2) and default scale == 1.
410.     h, w = image.shape[:2]
411.     window = (0, 0, h, w)
412.     scale = 1
413.     padding = [(0, 0), (0, 0), (0, 0)]
414.     crop = None
415.
416.     if mode == "none":
417.         return image, window, scale, padding, crop
418.
419.     # Scale?
420.     if min_dim:
421.         # Scale up but not down
422.         scale = max(1, min_dim / min(h, w))
423.     if min_scale and scale < min_scale:
424.         scale = min_scale
425.
426.     # Does it exceed max dim?
427.     if max_dim and mode == "square":
428.         image_max = max(h, w)
429.         if round(image_max * scale) > max_dim:
430.             scale = max_dim / image_max
431.
432.     # Resize image using bilinear interpolation
433.     if scale != 1:
434.         image = resize(image, (round(h * scale), round(w * scale)),
435.                        preserve_range=True)
436.
437.     # Need padding or cropping?
438.     if mode == "square":
439.         # Get new height and width
440.         h, w = image.shape[:2]
441.         top_pad = (max_dim - h) // 2
442.         bottom_pad = max_dim - h - top_pad
443.         left_pad = (max_dim - w) // 2
444.         right_pad = max_dim - w - left_pad
446.         image = np.pad(image, padding, mode='constant', constant_values=0)
448.     elif mode == "pad64":
449.         h, w = image.shape[:2]
450.         # Both sides must be divisible by 64
451.         assert min_dim % 64 == 0, "Minimum dimension must be a multiple of 64"
452.         # Height
453.         if h % 64 > 0:
454.             max_h = h - (h % 64) + 64
455.             top_pad = (max_h - h) // 2
456.             bottom_pad = max_h - h - top_pad
457.         else:
459.         # Width
460.         if w % 64 > 0:
461.             max_w = w - (w % 64) + 64
462.             left_pad = (max_w - w) // 2
463.             right_pad = max_w - w - left_pad
464.         else:
467.         image = np.pad(image, padding, mode='constant', constant_values=0)
469.     elif mode == "crop":
470.         # Pick a random crop
471.         h, w = image.shape[:2]
472.         y = random.randint(0, (h - min_dim))
473.         x = random.randint(0, (w - min_dim))
474.         crop = (y, x, min_dim, min_dim)
475.         image = image[y:y + min_dim, x:x + min_dim]
476.         window = (0, 0, min_dim, min_dim)
477.     elif mode == "mask":
478.         pass
479.     else:
480.         raise Exception("Mode {} not supported".format(mode))
481.     return image.astype(image_dtype), window, scale, padding, crop
482.
483.
485.     """Resizes a mask using the given scale and padding.
486.     Typically, you get the scale and padding from resize_image() to
487.     ensure both, the image and the mask, are resized consistently.
488.     scale: mask scaling factor
490.             [(top, bottom), (left, right), (0, 0)]
491.     """
492.     # Suppress warning from scipy 0.13.0, the output shape of zoom() is
493.     # calculated with round() instead of int()
494.     with warnings.catch_warnings():
495.         warnings.simplefilter("ignore")
496.         mask = scipy.ndimage.zoom(mask, zoom=[scale, scale, 1], order=0)
497.     if crop is not None:
498.         y, x, h, w = crop
499.         mask = mask[y:y + h, x:x + w]
500.     else:
503.
504.
506.     """Resize masks to a smaller version to reduce memory load.
507.     Mini-masks can be resized back to image scale using expand_masks()
508.     See inspect_data.ipynb notebook for more details.
509.     """
510.     mini_mask = np.zeros(mini_shape + (mask.shape[-1],), dtype=bool)
511.     for i in range(mask.shape[-1]):
512.         # Pick slice and cast to bool in case load_mask() returned wrong dtype
513.         m = mask[:, :, i].astype(bool)
514.         y1, x1, y2, x2 = bbox[i][:4]
515.         m = m[y1:y2, x1:x2]
516.         if m.size == 0:
517.             raise Exception("Invalid bounding box with area of zero")
518.         # Resize with bilinear interpolation
519.         m = resize(m, mini_shape)
520.         mini_mask[:, :, i] = np.around(m).astype(np.bool)
522.
523.
525.     """Resizes mini masks back to image size. Reverses the change
527.     See inspect_data.ipynb notebook for more details.
528.     """
529.     mask = np.zeros(image_shape[:2] + (mini_mask.shape[-1],), dtype=bool)
530.     for i in range(mask.shape[-1]):
531.         m = mini_mask[:, :, i]
532.         y1, x1, y2, x2 = bbox[i][:4]
533.         h = y2 - y1
534.         w = x2 - x1
535.         # Resize with bilinear interpolation
536.         m = resize(m, (h, w))
537.         mask[y1:y2, x1:x2, i] = np.around(m).astype(np.bool)
539.
540.
541. # TODO: Build and use this function to reduce code duplication
543.     pass
544.
545.
547.     """Converts a mask generated by the neural network to a format similar
548.     to its original shape.
549.     mask: [height, width] of type float. A small, typically 28x28 mask.
550.     bbox: [y1, x1, y2, x2]. The box to fit the mask in.
551.     Returns a binary mask with the same size as the original image.
552.     """
553.     threshold = 0.5
554.     y1, x1, y2, x2 = bbox
555.     mask = resize(mask, (y2 - y1, x2 - x1))
556.     mask = np.where(mask >= threshold, 1, 0).astype(np.bool)
557.
558.     # Put the mask in the right location.
559.     full_mask = np.zeros(image_shape[:2], dtype=np.bool)
562.
563.
564. ############################################################
565. #  Anchors
566. ############################################################
567.
568. def generate_anchors(scales, ratios, shape, feature_stride, anchor_stride):
569.     """
570.     scales: 1D array of anchor sizes in pixels. Example: [32, 64, 128]
571.     ratios: 1D array of anchor ratios of width/height. Example: [0.5, 1, 2]
572.     shape: [height, width] spatial shape of the feature map over which
573.             to generate anchors.
574.     feature_stride: Stride of the feature map relative to the image in pixels.
575.     anchor_stride: Stride of anchors on the feature map. For example, if the
576.         value is 2 then generate anchors for every other feature map pixel.
577.     """
578.     # Get all combinations of scales and ratios
579.     scales, ratios = np.meshgrid(np.array(scales), np.array(ratios))
580.     scales = scales.flatten()
581.     ratios = ratios.flatten()
582.
583.     # Enumerate heights and widths from scales and ratios
584.     heights = scales / np.sqrt(ratios)
585.     widths = scales * np.sqrt(ratios)
586.
587.     # Enumerate shifts in feature space
588.     shifts_y = np.arange(0, shape, anchor_stride) * feature_stride
589.     shifts_x = np.arange(0, shape, anchor_stride) * feature_stride
590.     shifts_x, shifts_y = np.meshgrid(shifts_x, shifts_y)
591.
592.     # Enumerate combinations of shifts, widths, and heights
593.     box_widths, box_centers_x = np.meshgrid(widths, shifts_x)
594.     box_heights, box_centers_y = np.meshgrid(heights, shifts_y)
595.
596.     # Reshape to get a list of (y, x) and a list of (h, w)
597.     box_centers = np.stack(
598.         [box_centers_y, box_centers_x], axis=2).reshape([-1, 2])
599.     box_sizes = np.stack([box_heights, box_widths], axis=2).reshape([-1, 2])
600.
601.     # Convert to corner coordinates (y1, x1, y2, x2)
602.     boxes = np.concatenate([box_centers - 0.5 * box_sizes,
603.                             box_centers + 0.5 * box_sizes], axis=1)
604.     return boxes
605.
606.
607. def generate_pyramid_anchors(scales, ratios, feature_shapes, feature_strides,
608.                              anchor_stride):
609.     """Generate anchors at different levels of a feature pyramid. Each scale
610.     is associated with a level of the pyramid, but each ratio is used in
611.     all levels of the pyramid.
612.     Returns:
613.     anchors: [N, (y1, x1, y2, x2)]. All generated anchors in one array. Sorted
614.         with the same order of the given scales. So, anchors of scale come
615.         first, then anchors of scale, and so on.
616.     """
617.     # Anchors
618.     # [anchor_count, (y1, x1, y2, x2)]
619.     anchors = []
620.     for i in range(len(scales)):
621.         anchors.append(generate_anchors(scales[i], ratios, feature_shapes[i],
622.                                         feature_strides[i], anchor_stride))
623.     return np.concatenate(anchors, axis=0)
624.
625.
626. ############################################################
627. #  Miscellaneous
628. ############################################################
629.
630. def trim_zeros(x):
631.     """It's common to have tensors larger than the available data and
632.     pad with zeros. This function removes rows that are all zeros.
633.     x: [rows, columns].
634.     """
635.     assert len(x.shape) == 2
636.     return x[~np.all(x == 0, axis=1)]
637.
638.
639. def compute_matches(gt_boxes, gt_class_ids, gt_masks,
640.                     pred_boxes, pred_class_ids, pred_scores, pred_masks,
641.                     iou_threshold=0.5, score_threshold=0.0):
642.     """Finds matches between prediction and ground truth instances.
643.     Returns:
644.         gt_match: 1-D array. For each GT box it has the index of the matched
645.                   predicted box.
646.         pred_match: 1-D array. For each predicted box, it has the index of
647.                     the matched ground truth box.
648.         overlaps: [pred_boxes, gt_boxes] IoU overlaps.
649.     """
650.     # Trim zero padding
651.     # TODO: cleaner to do zero unpadding upstream
652.     gt_boxes = trim_zeros(gt_boxes)
654.     pred_boxes = trim_zeros(pred_boxes)
655.     pred_scores = pred_scores[:pred_boxes.shape]
656.     # Sort predictions by score from high to low
657.     indices = np.argsort(pred_scores)[::-1]
658.     pred_boxes = pred_boxes[indices]
659.     pred_class_ids = pred_class_ids[indices]
660.     pred_scores = pred_scores[indices]
662.
663.     # Compute IoU overlaps [pred_masks, gt_masks]
665.
666.     # Loop through predictions and find matching ground truth boxes
667.     match_count = 0
668.     pred_match = -1 * np.ones([pred_boxes.shape])
669.     gt_match = -1 * np.ones([gt_boxes.shape])
670.     for i in range(len(pred_boxes)):
671.         # Find best matching ground truth box
672.         # 1. Sort matches by score
673.         sorted_ixs = np.argsort(overlaps[i])[::-1]
674.         # 2. Remove low scores
675.         low_score_idx = np.where(overlaps[i, sorted_ixs] < score_threshold)
676.         if low_score_idx.size > 0:
677.             sorted_ixs = sorted_ixs[:low_score_idx]
678.         # 3. Find the match
679.         for j in sorted_ixs:
680.             # If ground truth box is already matched, go to next one
681.             if gt_match[j] > -1:
682.                 continue
683.             # If we reach IoU smaller than the threshold, end the loop
684.             iou = overlaps[i, j]
685.             if iou < iou_threshold:
686.                 break
687.             # Do we have a match?
688.             if pred_class_ids[i] == gt_class_ids[j]:
689.                 match_count += 1
690.                 gt_match[j] = i
691.                 pred_match[i] = j
692.                 break
693.
694.     return gt_match, pred_match, overlaps
695.
696.
697. def compute_ap(gt_boxes, gt_class_ids, gt_masks,
698.                pred_boxes, pred_class_ids, pred_scores, pred_masks,
699.                iou_threshold=0.5):
700.     """Compute Average Precision at a set IoU threshold (default 0.5).
701.     Returns:
702.     mAP: Mean Average Precision
703.     precisions: List of precisions at different class score thresholds.
704.     recalls: List of recall values at different class score thresholds.
705.     overlaps: [pred_boxes, gt_boxes] IoU overlaps.
706.     """
707.     # Get matches and overlaps
708.     gt_match, pred_match, overlaps = compute_matches(
709.         gt_boxes, gt_class_ids, gt_masks,
710.         pred_boxes, pred_class_ids, pred_scores, pred_masks,
711.         iou_threshold)
712.
713.     # Compute precision and recall at each prediction box step
714.     precisions = np.cumsum(pred_match > -1) / (np.arange(len(pred_match)) + 1)
715.     recalls = np.cumsum(pred_match > -1).astype(np.float32) / len(gt_match)
716.
717.     # Pad with start and end values to simplify the math
718.     precisions = np.concatenate([, precisions, ])
719.     recalls = np.concatenate([, recalls, ])
720.
721.     # Ensure precision values decrease but don't increase. This way, the
722.     # precision value at each recall threshold is the maximum it can be
723.     # for all following recall thresholds, as specified by the VOC paper.
724.     for i in range(len(precisions) - 2, -1, -1):
725.         precisions[i] = np.maximum(precisions[i], precisions[i + 1])
726.
727.     # Compute mean AP over recall range
728.     indices = np.where(recalls[:-1] != recalls[1:]) + 1
729.     mAP = np.sum((recalls[indices] - recalls[indices - 1]) *
730.                  precisions[indices])
731.
732.     return mAP, precisions, recalls, overlaps
733.
734.
735. def compute_ap_range(gt_box, gt_class_id, gt_mask,
736.                      pred_box, pred_class_id, pred_score, pred_mask,
737.                      iou_thresholds=None, verbose=1):
738.     """Compute AP over a range or IoU thresholds. Default range is 0.5-0.95."""
739.     # Default is 0.5 to 0.95 with increments of 0.05
740.     iou_thresholds = iou_thresholds or np.arange(0.5, 1.0, 0.05)
741.
742.     # Compute AP over range of IoU thresholds
743.     AP = []
744.     for iou_threshold in iou_thresholds:
745.         ap, precisions, recalls, overlaps =\
746.             compute_ap(gt_box, gt_class_id, gt_mask,
747.                         pred_box, pred_class_id, pred_score, pred_mask,
748.                         iou_threshold=iou_threshold)
749.         if verbose:
750.             print("AP @{:.2f}:\t {:.3f}".format(iou_threshold, ap))
751.         AP.append(ap)
752.     AP = np.array(AP).mean()
753.     if verbose:
754.         print("AP @{:.2f}-{:.2f}:\t {:.3f}".format(
755.             iou_thresholds, iou_thresholds[-1], AP))
756.     return AP
757.
758.
759. def compute_recall(pred_boxes, gt_boxes, iou):
760.     """Compute the recall at the given IoU threshold. It's an indication
761.     of how many GT boxes were found by the given prediction boxes.
762.     pred_boxes: [N, (y1, x1, y2, x2)] in image coordinates
763.     gt_boxes: [N, (y1, x1, y2, x2)] in image coordinates
764.     """
765.     # Measure overlaps
766.     overlaps = compute_overlaps(pred_boxes, gt_boxes)
767.     iou_max = np.max(overlaps, axis=1)
768.     iou_argmax = np.argmax(overlaps, axis=1)
769.     positive_ids = np.where(iou_max >= iou)
770.     matched_gt_boxes = iou_argmax[positive_ids]
771.
772.     recall = len(set(matched_gt_boxes)) / gt_boxes.shape
773.     return recall, positive_ids
774.
775.
776. # ## Batch Slicing
777. # Some custom layers support a batch size of 1 only, and require a lot of work
778. # to support batches greater than 1. This function slices an input tensor
779. # across the batch dimension and feeds batches of size 1. Effectively,
780. # an easy way to support batches > 1 quickly with little code modification.
781. # In the long run, it's more efficient to modify the code to support large
782. # batches and getting rid of this function. Consider this a temporary solution
783. def batch_slice(inputs, graph_fn, batch_size, names=None):
784.     """Splits inputs into slices and feeds each slice to a copy of the given
785.     computation graph and then combines the results. It allows you to run a
786.     graph on a batch of inputs even if the graph is written to support one
787.     instance only.
788.     inputs: list of tensors. All must have the same first dimension length
789.     graph_fn: A function that returns a TF tensor that's part of a graph.
790.     batch_size: number of slices to divide the data into.
791.     names: If provided, assigns names to the resulting tensors.
792.     """
793.     if not isinstance(inputs, list):
794.         inputs = [inputs]
795.
796.     outputs = []
797.     for i in range(batch_size):
798.         inputs_slice = [x[i] for x in inputs]
799.         output_slice = graph_fn(*inputs_slice)
800.         if not isinstance(output_slice, (tuple, list)):
801.             output_slice = [output_slice]
802.         outputs.append(output_slice)
803.     # Change outputs from a list of slices where each is
804.     # a list of outputs to a list of outputs and each has
805.     # a list of slices
806.     outputs = list(zip(*outputs))
807.
808.     if names is None:
809.         names = [None] * len(outputs)
810.
811.     result = [tf.stack(o, axis=0, name=n)
812.               for o, n in zip(outputs, names)]
813.     if len(result) == 1:
814.         result = result
815.
816.     return result
817.
818.
820.     """Download COCO trained weights from Releases.
821.     coco_model_path: local path of COCO trained weights
822.     """
823.     if verbose > 0:
824.         print("Downloading pretrained model to " + coco_model_path + " ...")
825.     with urllib.request.urlopen(COCO_MODEL_URL) as resp, open(coco_model_path, 'wb') as out:
826.         shutil.copyfileobj(resp, out)
827.     if verbose > 0:
829.
830.
831. def norm_boxes(boxes, shape):
832.     """Converts boxes from pixel coordinates to normalized coordinates.
833.     boxes: [N, (y1, x1, y2, x2)] in pixel coordinates
834.     shape: [..., (height, width)] in pixels
835.     Note: In pixel coordinates (y2, x2) is outside the box. But in normalized
836.     coordinates it's inside the box.
837.     Returns:
838.         [N, (y1, x1, y2, x2)] in normalized coordinates
839.     """
840.     h, w = shape
841.     scale = np.array([h - 1, w - 1, h - 1, w - 1])
842.     shift = np.array([0, 0, 1, 1])
843.     return np.divide((boxes - shift), scale).astype(np.float32)
844.
845.
846. def denorm_boxes(boxes, shape):
847.     """Converts boxes from normalized coordinates to pixel coordinates.
848.     boxes: [N, (y1, x1, y2, x2)] in normalized coordinates
849.     shape: [..., (height, width)] in pixels
850.     Note: In pixel coordinates (y2, x2) is outside the box. But in normalized
851.     coordinates it's inside the box.
852.     Returns:
853.         [N, (y1, x1, y2, x2)] in pixel coordinates
854.     """
855.     h, w = shape
856.     scale = np.array([h - 1, w - 1, h - 1, w - 1])
857.     shift = np.array([0, 0, 1, 1])
858.     return np.around(np.multiply(boxes, scale) + shift).astype(np.int32)
859.
860.
861. def resize(image, output_shape, order=1, mode='constant', cval=0, clip=True,
862.            preserve_range=False, anti_aliasing=False, anti_aliasing_sigma=None):
863.     """A wrapper for Scikit-Image resize().
864.     Scikit-Image generates warnings on every call to resize() if it doesn't
865.     receive the right parameters. The right parameters depend on the version
866.     of skimage. This solves the problem by using different parameters per
867.     version. And it provides a central place to control resizing defaults.
868.     """
869.     if LooseVersion(skimage.__version__) >= LooseVersion("0.14"):
870.         # New in 0.14: anti_aliasing. Default it to False for backward
871.         # compatibility with skimage 0.13.
872.         return skimage.transform.resize(
873.             image, output_shape,
874.             order=order, mode=mode, cval=cval, clip=clip,
875.             preserve_range=preserve_range, anti_aliasing=anti_aliasing,
876.             anti_aliasing_sigma=anti_aliasing_sigma)
877.     else:
878.         return skimage.transform.resize(
879.             image, output_shape,
880.             order=order, mode=mode, cval=cval, clip=clip,
881.             preserve_range=preserve_range)
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy.
Not a member of Pastebin yet?
Sign Up, it unlocks many cool features!

Top