You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

229 lines
7.6 KiB

# Copyright 2017 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Contains functions which are convenient for unit testing."""
import numpy as np
import tensorflow as tf
from object_detection.core import anchor_generator
from object_detection.core import box_coder
from object_detection.core import box_list
from object_detection.core import box_predictor
from object_detection.core import matcher
from object_detection.utils import shape_utils
# Default size (both width and height) used for testing mask predictions.
DEFAULT_MASK_SIZE = 5
class MockBoxCoder(box_coder.BoxCoder):
"""Simple `difference` BoxCoder."""
@property
def code_size(self):
return 4
def _encode(self, boxes, anchors):
return boxes.get() - anchors.get()
def _decode(self, rel_codes, anchors):
return box_list.BoxList(rel_codes + anchors.get())
class MockMaskHead(object):
"""Simple maskhead that returns all zeros as mask predictions."""
def __init__(self, num_classes):
self._num_classes = num_classes
def predict(self, features):
batch_size = tf.shape(features)[0]
return tf.zeros((batch_size, 1, self._num_classes, DEFAULT_MASK_SIZE,
DEFAULT_MASK_SIZE),
dtype=tf.float32)
class MockBoxPredictor(box_predictor.BoxPredictor):
"""Simple box predictor that ignores inputs and outputs all zeros."""
def __init__(self, is_training, num_classes, add_background_class=True):
super(MockBoxPredictor, self).__init__(is_training, num_classes)
self._add_background_class = add_background_class
def _predict(self, image_features, num_predictions_per_location):
image_feature = image_features[0]
combined_feature_shape = shape_utils.combined_static_and_dynamic_shape(
image_feature)
batch_size = combined_feature_shape[0]
num_anchors = (combined_feature_shape[1] * combined_feature_shape[2])
code_size = 4
zero = tf.reduce_sum(0 * image_feature)
num_class_slots = self.num_classes
if self._add_background_class:
num_class_slots = num_class_slots + 1
box_encodings = zero + tf.zeros(
(batch_size, num_anchors, 1, code_size), dtype=tf.float32)
class_predictions_with_background = zero + tf.zeros(
(batch_size, num_anchors, num_class_slots), dtype=tf.float32)
predictions_dict = {
box_predictor.BOX_ENCODINGS:
box_encodings,
box_predictor.CLASS_PREDICTIONS_WITH_BACKGROUND:
class_predictions_with_background
}
return predictions_dict
class MockKerasBoxPredictor(box_predictor.KerasBoxPredictor):
"""Simple box predictor that ignores inputs and outputs all zeros."""
def __init__(self, is_training, num_classes, add_background_class=True):
super(MockKerasBoxPredictor, self).__init__(
is_training, num_classes, False, False)
self._add_background_class = add_background_class
def _predict(self, image_features, **kwargs):
image_feature = image_features[0]
combined_feature_shape = shape_utils.combined_static_and_dynamic_shape(
image_feature)
batch_size = combined_feature_shape[0]
num_anchors = (combined_feature_shape[1] * combined_feature_shape[2])
code_size = 4
zero = tf.reduce_sum(0 * image_feature)
num_class_slots = self.num_classes
if self._add_background_class:
num_class_slots = num_class_slots + 1
box_encodings = zero + tf.zeros(
(batch_size, num_anchors, 1, code_size), dtype=tf.float32)
class_predictions_with_background = zero + tf.zeros(
(batch_size, num_anchors, num_class_slots), dtype=tf.float32)
predictions_dict = {
box_predictor.BOX_ENCODINGS:
box_encodings,
box_predictor.CLASS_PREDICTIONS_WITH_BACKGROUND:
class_predictions_with_background
}
return predictions_dict
class MockAnchorGenerator(anchor_generator.AnchorGenerator):
"""Mock anchor generator."""
def name_scope(self):
return 'MockAnchorGenerator'
def num_anchors_per_location(self):
return [1]
def _generate(self, feature_map_shape_list):
num_anchors = sum([shape[0] * shape[1] for shape in feature_map_shape_list])
return box_list.BoxList(tf.zeros((num_anchors, 4), dtype=tf.float32))
class MockMatcher(matcher.Matcher):
"""Simple matcher that matches first anchor to first groundtruth box."""
def _match(self, similarity_matrix, valid_rows):
return tf.constant([0, -1, -1, -1], dtype=tf.int32)
def create_diagonal_gradient_image(height, width, depth):
"""Creates pyramid image. Useful for testing.
For example, pyramid_image(5, 6, 1) looks like:
# [[[ 5. 4. 3. 2. 1. 0.]
# [ 6. 5. 4. 3. 2. 1.]
# [ 7. 6. 5. 4. 3. 2.]
# [ 8. 7. 6. 5. 4. 3.]
# [ 9. 8. 7. 6. 5. 4.]]]
Args:
height: height of image
width: width of image
depth: depth of image
Returns:
pyramid image
"""
row = np.arange(height)
col = np.arange(width)[::-1]
image_layer = np.expand_dims(row, 1) + col
image_layer = np.expand_dims(image_layer, 2)
image = image_layer
for i in range(1, depth):
image = np.concatenate((image, image_layer * pow(10, i)), 2)
return image.astype(np.float32)
def create_random_boxes(num_boxes, max_height, max_width):
"""Creates random bounding boxes of specific maximum height and width.
Args:
num_boxes: number of boxes.
max_height: maximum height of boxes.
max_width: maximum width of boxes.
Returns:
boxes: numpy array of shape [num_boxes, 4]. Each row is in form
[y_min, x_min, y_max, x_max].
"""
y_1 = np.random.uniform(size=(1, num_boxes)) * max_height
y_2 = np.random.uniform(size=(1, num_boxes)) * max_height
x_1 = np.random.uniform(size=(1, num_boxes)) * max_width
x_2 = np.random.uniform(size=(1, num_boxes)) * max_width
boxes = np.zeros(shape=(num_boxes, 4))
boxes[:, 0] = np.minimum(y_1, y_2)
boxes[:, 1] = np.minimum(x_1, x_2)
boxes[:, 2] = np.maximum(y_1, y_2)
boxes[:, 3] = np.maximum(x_1, x_2)
return boxes.astype(np.float32)
def first_rows_close_as_set(a, b, k=None, rtol=1e-6, atol=1e-6):
"""Checks if first K entries of two lists are close, up to permutation.
Inputs to this assert are lists of items which can be compared via
numpy.allclose(...) and can be sorted.
Args:
a: list of items which can be compared via numpy.allclose(...) and are
sortable.
b: list of items which can be compared via numpy.allclose(...) and are
sortable.
k: a non-negative integer. If not provided, k is set to be len(a).
rtol: relative tolerance.
atol: absolute tolerance.
Returns:
boolean, True if input lists a and b have the same length and
the first k entries of the inputs satisfy numpy.allclose() after
sorting entries.
"""
if not isinstance(a, list) or not isinstance(b, list) or len(a) != len(b):
return False
if not k:
k = len(a)
k = min(k, len(a))
a_sorted = sorted(a[:k])
b_sorted = sorted(b[:k])
return all([
np.allclose(entry_a, entry_b, rtol, atol)
for (entry_a, entry_b) in zip(a_sorted, b_sorted)
])