Commit b968a6ce authored by Mark Sandler's avatar Mark Sandler Committed by Jonathan Huang
Browse files

Merged commit includes the following changes: (#7800)

280142968  by Zhichao Lu:

    Opensource MobilenetEdgeTPU + ssdlite into third-party object detection APIs on EdgeTPU.

--
280134001  by Zhichao Lu:

    Adds MobilenetEdgeTpu + ssdlite into internal object detection APIs on EdgeTPU.

--
278941778  by Zhichao Lu:

    Add support for fixed input shapes for 'encoded_image_string_tensor' and 'tf_example' inputs.

--
278933274  by Zhichao Lu:

      Adding fool proof check to avoid using 1x1 depthwise conv op.

--
278762192  by Zhichao Lu:

    Ensure correct number of iterations after training resumes.

--
278746440  by Zhichao Lu:

    Internal change.

--
278006953  by Zhichao Lu:

    Internal changes to tf.contrib symbols

--
278006330  by Zhichao Lu:

    Internal changes to tf.contrib symbols

--
277593959  by Zhichao Lu:

      Make the ssd_feature_extractor_test.py PY3 compatible. The "six.zip" will use "itertools.izip" in Python 2 and "zip" in Python 3.

--
277344551  by Zhichao Lu:

    Internal change.

--
277154953  by Zhichao Lu:

    Conditionally use keras based optimizers so that check-pointing works correctly.
    This change also enables summaries on TPU which were previously not enabled
    due to a bug.

--
277087572  by Zhichao Lu:

    Fix resizing boxes when using keep_aspect_ratio_rezier with padding.

--
275898543  by Zhichao Lu:

    Support label_map_proto as input in label_map_util.

--
275347137  by Zhichao Lu:

    Add force_no_resize flag in eval.proto which replaces
    the resize config with identity resizer. This is useful
    when we want to test at the original image resolution.

--

PiperOrigin-RevId: 280142968
parent c3bd5082
...@@ -101,6 +101,19 @@ reporting an issue. ...@@ -101,6 +101,19 @@ reporting an issue.
## Release information ## Release information
### Nov 13th, 2019
We have released MobileNetEdgeTPU SSDLite model.
* SSDLite with MobileNetEdgeTPU backbone, which achieves 10% mAP higher than
MobileNetV2 SSDLite (24.3 mAP vs 22 mAP) on a Google Pixel4 at comparable
latency (6.6ms vs 6.8ms).
Along with the model definition, we are also releasing model checkpoints
trained on the COCO dataset.
<b>Thanks to contributors</b>: Yunyang Xiong, Bo Chen, Suyog Gupta, Hanxiao Liu,
Gabriel Bender, Mingxing Tan, Berkin Akin, Zhichao Lu, Quoc Le
### Oct 15th, 2019 ### Oct 15th, 2019
We have released two MobileNet V3 SSDLite models (presented in We have released two MobileNet V3 SSDLite models (presented in
[Searching for MobileNetV3](https://arxiv.org/abs/1905.02244)). [Searching for MobileNetV3](https://arxiv.org/abs/1905.02244)).
......
...@@ -44,6 +44,7 @@ from object_detection.models import ssd_resnet_v1_ppn_feature_extractor as ssd_r ...@@ -44,6 +44,7 @@ from object_detection.models import ssd_resnet_v1_ppn_feature_extractor as ssd_r
from object_detection.models.embedded_ssd_mobilenet_v1_feature_extractor import EmbeddedSSDMobileNetV1FeatureExtractor from object_detection.models.embedded_ssd_mobilenet_v1_feature_extractor import EmbeddedSSDMobileNetV1FeatureExtractor
from object_detection.models.ssd_inception_v2_feature_extractor import SSDInceptionV2FeatureExtractor from object_detection.models.ssd_inception_v2_feature_extractor import SSDInceptionV2FeatureExtractor
from object_detection.models.ssd_inception_v3_feature_extractor import SSDInceptionV3FeatureExtractor from object_detection.models.ssd_inception_v3_feature_extractor import SSDInceptionV3FeatureExtractor
from object_detection.models.ssd_mobilenet_edgetpu_feature_extractor import SSDMobileNetEdgeTPUFeatureExtractor
from object_detection.models.ssd_mobilenet_v1_feature_extractor import SSDMobileNetV1FeatureExtractor from object_detection.models.ssd_mobilenet_v1_feature_extractor import SSDMobileNetV1FeatureExtractor
from object_detection.models.ssd_mobilenet_v1_fpn_feature_extractor import SSDMobileNetV1FpnFeatureExtractor from object_detection.models.ssd_mobilenet_v1_fpn_feature_extractor import SSDMobileNetV1FpnFeatureExtractor
from object_detection.models.ssd_mobilenet_v1_fpn_keras_feature_extractor import SSDMobileNetV1FpnKerasFeatureExtractor from object_detection.models.ssd_mobilenet_v1_fpn_keras_feature_extractor import SSDMobileNetV1FpnKerasFeatureExtractor
...@@ -73,6 +74,7 @@ SSD_FEATURE_EXTRACTOR_CLASS_MAP = { ...@@ -73,6 +74,7 @@ SSD_FEATURE_EXTRACTOR_CLASS_MAP = {
'ssd_mobilenet_v2_fpn': SSDMobileNetV2FpnFeatureExtractor, 'ssd_mobilenet_v2_fpn': SSDMobileNetV2FpnFeatureExtractor,
'ssd_mobilenet_v3_large': SSDMobileNetV3LargeFeatureExtractor, 'ssd_mobilenet_v3_large': SSDMobileNetV3LargeFeatureExtractor,
'ssd_mobilenet_v3_small': SSDMobileNetV3SmallFeatureExtractor, 'ssd_mobilenet_v3_small': SSDMobileNetV3SmallFeatureExtractor,
'ssd_mobilenet_edgetpu': SSDMobileNetEdgeTPUFeatureExtractor,
'ssd_resnet50_v1_fpn': ssd_resnet_v1_fpn.SSDResnet50V1FpnFeatureExtractor, 'ssd_resnet50_v1_fpn': ssd_resnet_v1_fpn.SSDResnet50V1FpnFeatureExtractor,
'ssd_resnet101_v1_fpn': ssd_resnet_v1_fpn.SSDResnet101V1FpnFeatureExtractor, 'ssd_resnet101_v1_fpn': ssd_resnet_v1_fpn.SSDResnet101V1FpnFeatureExtractor,
'ssd_resnet152_v1_fpn': ssd_resnet_v1_fpn.SSDResnet152V1FpnFeatureExtractor, 'ssd_resnet152_v1_fpn': ssd_resnet_v1_fpn.SSDResnet152V1FpnFeatureExtractor,
......
...@@ -21,8 +21,8 @@ import tensorflow as tf ...@@ -21,8 +21,8 @@ import tensorflow as tf
from object_detection.utils import learning_schedules from object_detection.utils import learning_schedules
def build(optimizer_config, global_step=None): def build_optimizers_tf_v1(optimizer_config, global_step=None):
"""Create optimizer based on config. """Create a TF v1 compatible optimizer based on config.
Args: Args:
optimizer_config: A Optimizer proto message. optimizer_config: A Optimizer proto message.
...@@ -77,6 +77,68 @@ def build(optimizer_config, global_step=None): ...@@ -77,6 +77,68 @@ def build(optimizer_config, global_step=None):
return optimizer, summary_vars return optimizer, summary_vars
def build_optimizers_tf_v2(optimizer_config, global_step=None):
"""Create a TF v2 compatible optimizer based on config.
Args:
optimizer_config: A Optimizer proto message.
global_step: A variable representing the current step.
If None, defaults to tf.train.get_or_create_global_step()
Returns:
An optimizer and a list of variables for summary.
Raises:
ValueError: when using an unsupported input data type.
"""
optimizer_type = optimizer_config.WhichOneof('optimizer')
optimizer = None
summary_vars = []
if optimizer_type == 'rms_prop_optimizer':
config = optimizer_config.rms_prop_optimizer
learning_rate = _create_learning_rate(config.learning_rate,
global_step=global_step)
summary_vars.append(learning_rate)
optimizer = tf.keras.optimizers.RMSprop(
learning_rate,
decay=config.decay,
momentum=config.momentum_optimizer_value,
epsilon=config.epsilon)
if optimizer_type == 'momentum_optimizer':
config = optimizer_config.momentum_optimizer
learning_rate = _create_learning_rate(config.learning_rate,
global_step=global_step)
summary_vars.append(learning_rate)
optimizer = tf.keras.optimizers.SGD(
learning_rate,
momentum=config.momentum_optimizer_value)
if optimizer_type == 'adam_optimizer':
config = optimizer_config.adam_optimizer
learning_rate = _create_learning_rate(config.learning_rate,
global_step=global_step)
summary_vars.append(learning_rate)
optimizer = tf.keras.optimizers.Adam(learning_rate)
if optimizer is None:
raise ValueError('Optimizer %s not supported.' % optimizer_type)
if optimizer_config.use_moving_average:
raise ValueError('Moving average not supported in eager mode.')
return optimizer, summary_vars
def build(config, global_step=None):
if tf.executing_eagerly():
return build_optimizers_tf_v2(config, global_step)
else:
return build_optimizers_tf_v1(config, global_step)
def _create_learning_rate(learning_rate_config, global_step=None): def _create_learning_rate(learning_rate_config, global_step=None):
"""Create optimizer learning rate based on config. """Create optimizer learning rate based on config.
......
...@@ -101,7 +101,9 @@ def _build_non_max_suppressor(nms_config): ...@@ -101,7 +101,9 @@ def _build_non_max_suppressor(nms_config):
max_classes_per_detection=nms_config.max_classes_per_detection, max_classes_per_detection=nms_config.max_classes_per_detection,
soft_nms_sigma=nms_config.soft_nms_sigma, soft_nms_sigma=nms_config.soft_nms_sigma,
use_partitioned_nms=nms_config.use_partitioned_nms, use_partitioned_nms=nms_config.use_partitioned_nms,
use_combined_nms=nms_config.use_combined_nms) use_combined_nms=nms_config.use_combined_nms,
change_coordinate_frame=True)
return non_max_suppressor_fn return non_max_suppressor_fn
......
...@@ -846,8 +846,28 @@ def result_dict_for_batched_example(images, ...@@ -846,8 +846,28 @@ def result_dict_for_batched_example(images,
dtype=tf.uint8)) dtype=tf.uint8))
output_dict.update(groundtruth) output_dict.update(groundtruth)
if scale_to_absolute:
image_shape = tf.cast(tf.shape(images), tf.float32)
image_height, image_width = image_shape[1], image_shape[2]
def _scale_box_to_normalized_true_image(args):
"""Scale the box coordinates to be relative to the true image shape."""
boxes, true_image_shape = args
true_image_shape = tf.cast(true_image_shape, tf.float32)
true_height, true_width = true_image_shape[0], true_image_shape[1]
normalized_window = tf.stack([0.0, 0.0, true_height / image_height,
true_width / image_width])
return box_list_ops.change_coordinate_frame(
box_list.BoxList(boxes), normalized_window).get()
groundtruth_boxes = groundtruth[input_data_fields.groundtruth_boxes] groundtruth_boxes = groundtruth[input_data_fields.groundtruth_boxes]
groundtruth_boxes = shape_utils.static_or_dynamic_map_fn(
_scale_box_to_normalized_true_image,
elems=[groundtruth_boxes, true_image_shapes], dtype=tf.float32)
output_dict[input_data_fields.groundtruth_boxes] = groundtruth_boxes
if scale_to_absolute:
groundtruth_boxes = output_dict[input_data_fields.groundtruth_boxes]
output_dict[input_data_fields.groundtruth_boxes] = ( output_dict[input_data_fields.groundtruth_boxes] = (
shape_utils.static_or_dynamic_map_fn( shape_utils.static_or_dynamic_map_fn(
_scale_box_to_absolute, _scale_box_to_absolute,
......
...@@ -20,6 +20,7 @@ from __future__ import print_function ...@@ -20,6 +20,7 @@ from __future__ import print_function
from absl.testing import parameterized from absl.testing import parameterized
import numpy as np
import six import six
from six.moves import range from six.moves import range
import tensorflow as tf import tensorflow as tf
...@@ -254,6 +255,56 @@ class EvalUtilTest(test_case.TestCase, parameterized.TestCase): ...@@ -254,6 +255,56 @@ class EvalUtilTest(test_case.TestCase, parameterized.TestCase):
self.assertAlmostEqual(evaluator[1]._recall_lower_bound, 0.0) self.assertAlmostEqual(evaluator[1]._recall_lower_bound, 0.0)
self.assertAlmostEqual(evaluator[1]._recall_upper_bound, 1.0) self.assertAlmostEqual(evaluator[1]._recall_upper_bound, 1.0)
def test_padded_image_result_dict(self):
input_data_fields = fields.InputDataFields
detection_fields = fields.DetectionResultFields
key = tf.constant([str(i) for i in range(2)])
detection_boxes = np.array([[[0., 0., 1., 1.]], [[0.0, 0.0, 0.5, 0.5]]],
dtype=np.float32)
detections = {
detection_fields.detection_boxes:
tf.constant(detection_boxes),
detection_fields.detection_scores:
tf.constant([[1.], [1.]]),
detection_fields.detection_classes:
tf.constant([[1], [2]]),
detection_fields.num_detections:
tf.constant([1, 1])
}
gt_boxes = detection_boxes
groundtruth = {
input_data_fields.groundtruth_boxes:
tf.constant(gt_boxes),
input_data_fields.groundtruth_classes:
tf.constant([[1.], [1.]]),
}
image = tf.zeros((2, 100, 100, 3), dtype=tf.float32)
true_image_shapes = tf.constant([[100, 100, 3], [50, 100, 3]])
original_image_spatial_shapes = tf.constant([[200, 200], [150, 300]])
result = eval_util.result_dict_for_batched_example(
image, key, detections, groundtruth,
scale_to_absolute=True,
true_image_shapes=true_image_shapes,
original_image_spatial_shapes=original_image_spatial_shapes,
max_gt_boxes=tf.constant(1))
with self.test_session() as sess:
result = sess.run(result)
self.assertAllEqual(
[[[0., 0., 200., 200.]], [[0.0, 0.0, 150., 150.]]],
result[input_data_fields.groundtruth_boxes])
# Predictions from the model are not scaled.
self.assertAllEqual(
[[[0., 0., 200., 200.]], [[0.0, 0.0, 75., 150.]]],
result[detection_fields.detection_boxes])
if __name__ == '__main__': if __name__ == '__main__':
tf.test.main() tf.test.main()
...@@ -123,9 +123,12 @@ def _image_tensor_input_placeholder(input_shape=None): ...@@ -123,9 +123,12 @@ def _image_tensor_input_placeholder(input_shape=None):
return input_tensor, input_tensor return input_tensor, input_tensor
def _tf_example_input_placeholder(): def _tf_example_input_placeholder(input_shape=None):
"""Returns input that accepts a batch of strings with tf examples. """Returns input that accepts a batch of strings with tf examples.
Args:
input_shape: the shape to resize the output decoded images to (optional).
Returns: Returns:
a tuple of input placeholder and the output decoded images. a tuple of input placeholder and the output decoded images.
""" """
...@@ -135,6 +138,8 @@ def _tf_example_input_placeholder(): ...@@ -135,6 +138,8 @@ def _tf_example_input_placeholder():
tensor_dict = tf_example_decoder.TfExampleDecoder().decode( tensor_dict = tf_example_decoder.TfExampleDecoder().decode(
tf_example_string_tensor) tf_example_string_tensor)
image_tensor = tensor_dict[fields.InputDataFields.image] image_tensor = tensor_dict[fields.InputDataFields.image]
if input_shape is not None:
image_tensor = tf.image.resize(image_tensor, input_shape[1:3])
return image_tensor return image_tensor
return (batch_tf_example_placeholder, return (batch_tf_example_placeholder,
shape_utils.static_or_dynamic_map_fn( shape_utils.static_or_dynamic_map_fn(
...@@ -145,9 +150,12 @@ def _tf_example_input_placeholder(): ...@@ -145,9 +150,12 @@ def _tf_example_input_placeholder():
back_prop=False)) back_prop=False))
def _encoded_image_string_tensor_input_placeholder(): def _encoded_image_string_tensor_input_placeholder(input_shape=None):
"""Returns input that accepts a batch of PNG or JPEG strings. """Returns input that accepts a batch of PNG or JPEG strings.
Args:
input_shape: the shape to resize the output decoded images to (optional).
Returns: Returns:
a tuple of input placeholder and the output decoded images. a tuple of input placeholder and the output decoded images.
""" """
...@@ -159,6 +167,8 @@ def _encoded_image_string_tensor_input_placeholder(): ...@@ -159,6 +167,8 @@ def _encoded_image_string_tensor_input_placeholder():
image_tensor = tf.image.decode_image(encoded_image_string_tensor, image_tensor = tf.image.decode_image(encoded_image_string_tensor,
channels=3) channels=3)
image_tensor.set_shape((None, None, 3)) image_tensor.set_shape((None, None, 3))
if input_shape is not None:
image_tensor = tf.image.resize(image_tensor, input_shape[1:3])
return image_tensor return image_tensor
return (batch_image_str_placeholder, return (batch_image_str_placeholder,
tf.map_fn( tf.map_fn(
...@@ -355,8 +365,11 @@ def build_detection_graph(input_type, detection_model, input_shape, ...@@ -355,8 +365,11 @@ def build_detection_graph(input_type, detection_model, input_shape,
raise ValueError('Unknown input type: {}'.format(input_type)) raise ValueError('Unknown input type: {}'.format(input_type))
placeholder_args = {} placeholder_args = {}
if input_shape is not None: if input_shape is not None:
if input_type != 'image_tensor': if (input_type != 'image_tensor' and
raise ValueError('Can only specify input shape for `image_tensor` ' input_type != 'encoded_image_string_tensor' and
input_type != 'tf_example'):
raise ValueError('Can only specify input shape for `image_tensor`, '
'`encoded_image_string_tensor`, or `tf_example` '
'inputs.') 'inputs.')
placeholder_args['input_shape'] = input_shape placeholder_args['input_shape'] = input_shape
placeholder_tensor, input_tensors = input_placeholder_fn_map[input_type]( placeholder_tensor, input_tensors = input_placeholder_fn_map[input_type](
......
...@@ -246,6 +246,29 @@ class ExportInferenceGraphTest(tf.test.TestCase): ...@@ -246,6 +246,29 @@ class ExportInferenceGraphTest(tf.test.TestCase):
self.assertTrue(os.path.exists(os.path.join( self.assertTrue(os.path.exists(os.path.join(
output_directory, 'saved_model', 'saved_model.pb'))) output_directory, 'saved_model', 'saved_model.pb')))
def test_export_graph_with_fixed_size_tf_example_input(self):
input_shape = [1, 320, 320, 3]
tmp_dir = self.get_temp_dir()
trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt')
self._save_checkpoint_from_mock_model(
trained_checkpoint_prefix, use_moving_averages=False)
with mock.patch.object(
model_builder, 'build', autospec=True) as mock_builder:
mock_builder.return_value = FakeModel()
output_directory = os.path.join(tmp_dir, 'output')
pipeline_config = pipeline_pb2.TrainEvalPipelineConfig()
pipeline_config.eval_config.use_moving_averages = False
exporter.export_inference_graph(
input_type='tf_example',
pipeline_config=pipeline_config,
trained_checkpoint_prefix=trained_checkpoint_prefix,
output_directory=output_directory,
input_shape=input_shape)
saved_model_path = os.path.join(output_directory, 'saved_model')
self.assertTrue(
os.path.exists(os.path.join(saved_model_path, 'saved_model.pb')))
def test_export_graph_with_encoded_image_string_input(self): def test_export_graph_with_encoded_image_string_input(self):
tmp_dir = self.get_temp_dir() tmp_dir = self.get_temp_dir()
trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt')
...@@ -265,6 +288,29 @@ class ExportInferenceGraphTest(tf.test.TestCase): ...@@ -265,6 +288,29 @@ class ExportInferenceGraphTest(tf.test.TestCase):
self.assertTrue(os.path.exists(os.path.join( self.assertTrue(os.path.exists(os.path.join(
output_directory, 'saved_model', 'saved_model.pb'))) output_directory, 'saved_model', 'saved_model.pb')))
def test_export_graph_with_fixed_size_encoded_image_string_input(self):
input_shape = [1, 320, 320, 3]
tmp_dir = self.get_temp_dir()
trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt')
self._save_checkpoint_from_mock_model(
trained_checkpoint_prefix, use_moving_averages=False)
with mock.patch.object(
model_builder, 'build', autospec=True) as mock_builder:
mock_builder.return_value = FakeModel()
output_directory = os.path.join(tmp_dir, 'output')
pipeline_config = pipeline_pb2.TrainEvalPipelineConfig()
pipeline_config.eval_config.use_moving_averages = False
exporter.export_inference_graph(
input_type='encoded_image_string_tensor',
pipeline_config=pipeline_config,
trained_checkpoint_prefix=trained_checkpoint_prefix,
output_directory=output_directory,
input_shape=input_shape)
saved_model_path = os.path.join(output_directory, 'saved_model')
self.assertTrue(
os.path.exists(os.path.join(saved_model_path, 'saved_model.pb')))
def _get_variables_in_checkpoint(self, checkpoint_file): def _get_variables_in_checkpoint(self, checkpoint_file):
return set([ return set([
var_name var_name
......
...@@ -103,6 +103,7 @@ Note: The asterisk (☆) at the end of model name indicates that this model supp ...@@ -103,6 +103,7 @@ Note: The asterisk (☆) at the end of model name indicates that this model supp
Note: If you download the tar.gz file of quantized models and un-tar, you will get different set of files - a checkpoint, a config file and tflite frozen graphs (txt/binary). Note: If you download the tar.gz file of quantized models and un-tar, you will get different set of files - a checkpoint, a config file and tflite frozen graphs (txt/binary).
### Mobile models ### Mobile models
Model name | Pixel 1 Latency (ms) | COCO mAP | Outputs Model name | Pixel 1 Latency (ms) | COCO mAP | Outputs
...@@ -110,6 +111,11 @@ Model name ...@@ -110,6 +111,11 @@ Model name
[ssd_mobilenet_v3_large_coco](http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v3_large_coco_2019_08_14.tar.gz) | 119 | 22.3 | Boxes [ssd_mobilenet_v3_large_coco](http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v3_large_coco_2019_08_14.tar.gz) | 119 | 22.3 | Boxes
[ssd_mobilenet_v3_small_coco](http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v3_small_coco_2019_08_14.tar.gz) | 43 | 15.6 | Boxes [ssd_mobilenet_v3_small_coco](http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v3_small_coco_2019_08_14.tar.gz) | 43 | 15.6 | Boxes
### Pixel4 Edge TPU models
Model name | Pixel 4 Edge TPU Latency (ms) | COCO mAP | Outputs
----------------------------------------------------------------------------------------------------------------------------------- | :------------------: | :------: | :-----:
[ssd_mobilenet_edgetpu_coco](https://storage.cloud.google.com/mobilenet_edgetpu/checkpoints/ssdlite_mobilenet_edgetpu_coco_quant.tar.gz) | 6.6 | 24.3 | Boxes
## Kitti-trained models ## Kitti-trained models
Model name | Speed (ms) | Pascal mAP@0.5 | Outputs Model name | Speed (ms) | Pascal mAP@0.5 | Outputs
......
...@@ -25,10 +25,14 @@ from object_detection.builders import dataset_builder ...@@ -25,10 +25,14 @@ from object_detection.builders import dataset_builder
from object_detection.builders import image_resizer_builder from object_detection.builders import image_resizer_builder
from object_detection.builders import model_builder from object_detection.builders import model_builder
from object_detection.builders import preprocessor_builder from object_detection.builders import preprocessor_builder
from object_detection.core import box_list
from object_detection.core import box_list_ops
from object_detection.core import keypoint_ops
from object_detection.core import preprocessor from object_detection.core import preprocessor
from object_detection.core import standard_fields as fields from object_detection.core import standard_fields as fields
from object_detection.data_decoders import tf_example_decoder from object_detection.data_decoders import tf_example_decoder
from object_detection.protos import eval_pb2 from object_detection.protos import eval_pb2
from object_detection.protos import image_resizer_pb2
from object_detection.protos import input_reader_pb2 from object_detection.protos import input_reader_pb2
from object_detection.protos import model_pb2 from object_detection.protos import model_pb2
from object_detection.protos import train_pb2 from object_detection.protos import train_pb2
...@@ -155,6 +159,30 @@ def transform_input_data(tensor_dict, ...@@ -155,6 +159,30 @@ def transform_input_data(tensor_dict,
image = out_tensor_dict[fields.InputDataFields.image] image = out_tensor_dict[fields.InputDataFields.image]
preprocessed_resized_image, true_image_shape = model_preprocess_fn( preprocessed_resized_image, true_image_shape = model_preprocess_fn(
tf.expand_dims(tf.cast(image, dtype=tf.float32), axis=0)) tf.expand_dims(tf.cast(image, dtype=tf.float32), axis=0))
preprocessed_shape = tf.shape(preprocessed_resized_image)
new_height, new_width = preprocessed_shape[1], preprocessed_shape[2]
im_box = tf.stack([
0.0, 0.0,
tf.to_float(new_height) / tf.to_float(true_image_shape[0, 0]),
tf.to_float(new_width) / tf.to_float(true_image_shape[0, 1])
])
if fields.InputDataFields.groundtruth_boxes in tensor_dict:
bboxes = out_tensor_dict[fields.InputDataFields.groundtruth_boxes]
boxlist = box_list.BoxList(bboxes)
realigned_bboxes = box_list_ops.change_coordinate_frame(boxlist, im_box)
out_tensor_dict[
fields.InputDataFields.groundtruth_boxes] = realigned_bboxes.get()
if fields.InputDataFields.groundtruth_keypoints in tensor_dict:
keypoints = out_tensor_dict[fields.InputDataFields.groundtruth_keypoints]
realigned_keypoints = keypoint_ops.change_coordinate_frame(keypoints,
im_box)
out_tensor_dict[
fields.InputDataFields.groundtruth_keypoints] = realigned_keypoints
if use_bfloat16: if use_bfloat16:
preprocessed_resized_image = tf.cast( preprocessed_resized_image = tf.cast(
preprocessed_resized_image, tf.bfloat16) preprocessed_resized_image, tf.bfloat16)
...@@ -655,6 +683,14 @@ def eval_input(eval_config, eval_input_config, model_config, ...@@ -655,6 +683,14 @@ def eval_input(eval_config, eval_input_config, model_config,
raise TypeError('The `model_config` must be a ' raise TypeError('The `model_config` must be a '
'model_pb2.DetectionModel.') 'model_pb2.DetectionModel.')
if eval_config.force_no_resize:
arch = model_config.WhichOneof('model')
arch_config = getattr(model_config, arch)
image_resizer_proto = image_resizer_pb2.ImageResizer()
image_resizer_proto.identity_resizer.CopyFrom(
image_resizer_pb2.IdentityResizer())
arch_config.image_resizer.CopyFrom(image_resizer_proto)
if model is None: if model is None:
model_preprocess_fn = INPUT_BUILDER_UTIL_MAP['model_build']( model_preprocess_fn = INPUT_BUILDER_UTIL_MAP['model_build'](
model_config, is_training=False).preprocess model_config, is_training=False).preprocess
......
...@@ -468,7 +468,7 @@ class InputsTest(test_case.TestCase, parameterized.TestCase): ...@@ -468,7 +468,7 @@ class InputsTest(test_case.TestCase, parameterized.TestCase):
replaced_string = inputs._replace_empty_string_with_random_number( replaced_string = inputs._replace_empty_string_with_random_number(
string_placeholder) string_placeholder)
test_string = 'hello world' test_string = b'hello world'
feed_dict = {string_placeholder: test_string} feed_dict = {string_placeholder: test_string}
with self.test_session() as sess: with self.test_session() as sess:
...@@ -493,7 +493,34 @@ class InputsTest(test_case.TestCase, parameterized.TestCase): ...@@ -493,7 +493,34 @@ class InputsTest(test_case.TestCase, parameterized.TestCase):
# Test whether out_string is a string which represents an integer. # Test whether out_string is a string which represents an integer.
int(out_string) # throws an error if out_string is not castable to int. int(out_string) # throws an error if out_string is not castable to int.
self.assertEqual(out_string, '2798129067578209328') self.assertEqual(out_string, b'2798129067578209328')
def test_force_no_resize(self):
"""Tests the functionality of force_no_reisze option."""
configs = _get_configs_for_model('ssd_inception_v2_pets')
configs['eval_config'].force_no_resize = True
eval_input_fn = inputs.create_eval_input_fn(
eval_config=configs['eval_config'],
eval_input_config=configs['eval_input_configs'][0],
model_config=configs['model']
)
train_input_fn = inputs.create_train_input_fn(
train_config=configs['train_config'],
train_input_config=configs['train_input_config'],
model_config=configs['model']
)
features_train, _ = _make_initializable_iterator(
train_input_fn()).get_next()
features_eval, _ = _make_initializable_iterator(
eval_input_fn()).get_next()
images_train, images_eval = features_train['image'], features_eval['image']
self.assertEqual([1, None, None, 3], images_eval.shape.as_list())
self.assertEqual([24, 300, 300, 3], images_train.shape.as_list())
class DataAugmentationFnTest(test_case.TestCase): class DataAugmentationFnTest(test_case.TestCase):
...@@ -646,6 +673,14 @@ def _fake_image_resizer_fn(image, mask): ...@@ -646,6 +673,14 @@ def _fake_image_resizer_fn(image, mask):
return (image, mask, tf.shape(image)) return (image, mask, tf.shape(image))
def _fake_resize50_preprocess_fn(image):
image = image[0]
image, shape = preprocessor.resize_to_range(
image, min_dimension=50, max_dimension=50, pad_to_max_dimension=True)
return tf.expand_dims(image, 0), tf.expand_dims(shape, axis=0)
class DataTransformationFnTest(test_case.TestCase): class DataTransformationFnTest(test_case.TestCase):
def test_combine_additional_channels_if_present(self): def test_combine_additional_channels_if_present(self):
...@@ -1014,6 +1049,37 @@ class DataTransformationFnTest(test_case.TestCase): ...@@ -1014,6 +1049,37 @@ class DataTransformationFnTest(test_case.TestCase):
self.assertAllEqual(augmented_tensor_dict[fields.InputDataFields.image], self.assertAllEqual(augmented_tensor_dict[fields.InputDataFields.image],
(np_image + 5) * 2) (np_image + 5) * 2)
def test_resize_with_padding(self):
tensor_dict = {
fields.InputDataFields.image:
tf.constant(np.random.rand(100, 50, 3).astype(np.float32)),
fields.InputDataFields.groundtruth_boxes:
tf.constant(np.array([[.5, .5, 1, 1], [.0, .0, .5, .5]],
np.float32)),
fields.InputDataFields.groundtruth_classes:
tf.constant(np.array([1, 2], np.int32)),
fields.InputDataFields.groundtruth_keypoints:
tf.constant([[0.1, 0.2], [0.3, 0.4]]),
}
num_classes = 3
input_transformation_fn = functools.partial(
inputs.transform_input_data,
model_preprocess_fn=_fake_resize50_preprocess_fn,
image_resizer_fn=_fake_image_resizer_fn,
num_classes=num_classes,)
with self.test_session() as sess:
transformed_inputs = sess.run(
input_transformation_fn(tensor_dict=tensor_dict))
self.assertAllClose(
transformed_inputs[fields.InputDataFields.groundtruth_boxes],
[[.5, .25, 1., .5], [.0, .0, .5, .25]])
self.assertAllClose(
transformed_inputs[fields.InputDataFields.groundtruth_keypoints],
[[[.1, .1], [.3, .2]]])
class PadInputDataToStaticShapesFnTest(test_case.TestCase): class PadInputDataToStaticShapesFnTest(test_case.TestCase):
......
...@@ -45,6 +45,7 @@ Example usage: ...@@ -45,6 +45,7 @@ Example usage:
import functools import functools
import os import os
import tensorflow as tf import tensorflow as tf
from tensorflow.contrib import framework as contrib_framework
from object_detection.builders import dataset_builder from object_detection.builders import dataset_builder
from object_detection.builders import graph_rewriter_builder from object_detection.builders import graph_rewriter_builder
...@@ -80,7 +81,7 @@ flags.DEFINE_boolean( ...@@ -80,7 +81,7 @@ flags.DEFINE_boolean(
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
@tf.contrib.framework.deprecated(None, 'Use object_detection/model_main.py.') @contrib_framework.deprecated(None, 'Use object_detection/model_main.py.')
def main(unused_argv): def main(unused_argv):
assert FLAGS.checkpoint_dir, '`checkpoint_dir` is missing.' assert FLAGS.checkpoint_dir, '`checkpoint_dir` is missing.'
assert FLAGS.eval_dir, '`eval_dir` is missing.' assert FLAGS.eval_dir, '`eval_dir` is missing.'
......
...@@ -45,6 +45,7 @@ import functools ...@@ -45,6 +45,7 @@ import functools
import json import json
import os import os
import tensorflow as tf import tensorflow as tf
from tensorflow.contrib import framework as contrib_framework
from object_detection.builders import dataset_builder from object_detection.builders import dataset_builder
from object_detection.builders import graph_rewriter_builder from object_detection.builders import graph_rewriter_builder
...@@ -84,7 +85,7 @@ flags.DEFINE_string('model_config_path', '', ...@@ -84,7 +85,7 @@ flags.DEFINE_string('model_config_path', '',
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
@tf.contrib.framework.deprecated(None, 'Use object_detection/model_main.py.') @contrib_framework.deprecated(None, 'Use object_detection/model_main.py.')
def main(_): def main(_):
assert FLAGS.train_dir, '`train_dir` is missing.' assert FLAGS.train_dir, '`train_dir` is missing.'
if FLAGS.task == 0: tf.gfile.MakeDirs(FLAGS.train_dir) if FLAGS.task == 0: tf.gfile.MakeDirs(FLAGS.train_dir)
......
...@@ -22,6 +22,7 @@ DetectionModel. ...@@ -22,6 +22,7 @@ DetectionModel.
import functools import functools
import tensorflow as tf import tensorflow as tf
from tensorflow.contrib import slim as contrib_slim
from object_detection.builders import optimizer_builder from object_detection.builders import optimizer_builder
from object_detection.builders import preprocessor_builder from object_detection.builders import preprocessor_builder
...@@ -32,7 +33,7 @@ from object_detection.utils import ops as util_ops ...@@ -32,7 +33,7 @@ from object_detection.utils import ops as util_ops
from object_detection.utils import variables_helper from object_detection.utils import variables_helper
from deployment import model_deploy from deployment import model_deploy
slim = tf.contrib.slim slim = contrib_slim
def create_input_queue(batch_size_per_clone, create_tensor_dict_fn, def create_input_queue(batch_size_per_clone, create_tensor_dict_fn,
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
import tensorflow as tf import tensorflow as tf
from google.protobuf import text_format from google.protobuf import text_format
from tensorflow.contrib import layers as contrib_layers
from object_detection.core import losses from object_detection.core import losses
from object_detection.core import model from object_detection.core import model
...@@ -89,10 +90,10 @@ class FakeDetectionModel(model.DetectionModel): ...@@ -89,10 +90,10 @@ class FakeDetectionModel(model.DetectionModel):
prediction_dict: a dictionary holding prediction tensors to be prediction_dict: a dictionary holding prediction tensors to be
passed to the Loss or Postprocess functions. passed to the Loss or Postprocess functions.
""" """
flattened_inputs = tf.contrib.layers.flatten(preprocessed_inputs) flattened_inputs = contrib_layers.flatten(preprocessed_inputs)
class_prediction = tf.contrib.layers.fully_connected( class_prediction = contrib_layers.fully_connected(flattened_inputs,
flattened_inputs, self._num_classes) self._num_classes)
box_prediction = tf.contrib.layers.fully_connected(flattened_inputs, 4) box_prediction = contrib_layers.fully_connected(flattened_inputs, 4)
return { return {
'class_predictions_with_background': tf.reshape( 'class_predictions_with_background': tf.reshape(
......
...@@ -95,6 +95,8 @@ configured in the meta architecture: ...@@ -95,6 +95,8 @@ configured in the meta architecture:
import abc import abc
import functools import functools
import tensorflow as tf import tensorflow as tf
from tensorflow.contrib import framework as contrib_framework
from tensorflow.contrib import slim as contrib_slim
from object_detection.anchor_generators import grid_anchor_generator from object_detection.anchor_generators import grid_anchor_generator
from object_detection.builders import box_predictor_builder from object_detection.builders import box_predictor_builder
...@@ -110,7 +112,7 @@ from object_detection.utils import ops ...@@ -110,7 +112,7 @@ from object_detection.utils import ops
from object_detection.utils import shape_utils from object_detection.utils import shape_utils
from object_detection.utils import variables_helper from object_detection.utils import variables_helper
slim = tf.contrib.slim slim = contrib_slim
_UNINITIALIZED_FEATURE_EXTRACTOR = '__uninitialized__' _UNINITIALIZED_FEATURE_EXTRACTOR = '__uninitialized__'
...@@ -2772,7 +2774,7 @@ class FasterRCNNMetaArch(model.DetectionModel): ...@@ -2772,7 +2774,7 @@ class FasterRCNNMetaArch(model.DetectionModel):
self.first_stage_feature_extractor_scope, self.first_stage_feature_extractor_scope,
self.second_stage_feature_extractor_scope self.second_stage_feature_extractor_scope
] ]
feature_extractor_variables = tf.contrib.framework.filter_variables( feature_extractor_variables = contrib_framework.filter_variables(
variables_to_restore, include_patterns=include_patterns) variables_to_restore, include_patterns=include_patterns)
return {var.op.name: var for var in feature_extractor_variables} return {var.op.name: var for var in feature_extractor_variables}
......
...@@ -21,6 +21,7 @@ import numpy as np ...@@ -21,6 +21,7 @@ import numpy as np
import tensorflow as tf import tensorflow as tf
from google.protobuf import text_format from google.protobuf import text_format
from tensorflow.contrib import slim as contrib_slim
from object_detection.anchor_generators import grid_anchor_generator from object_detection.anchor_generators import grid_anchor_generator
from object_detection.builders import box_predictor_builder from object_detection.builders import box_predictor_builder
from object_detection.builders import hyperparams_builder from object_detection.builders import hyperparams_builder
...@@ -37,7 +38,7 @@ from object_detection.utils import ops ...@@ -37,7 +38,7 @@ from object_detection.utils import ops
from object_detection.utils import test_case from object_detection.utils import test_case
from object_detection.utils import test_utils from object_detection.utils import test_utils
slim = tf.contrib.slim slim = contrib_slim
BOX_CODE_SIZE = 4 BOX_CODE_SIZE = 4
......
...@@ -19,6 +19,8 @@ models. ...@@ -19,6 +19,8 @@ models.
""" """
import abc import abc
import tensorflow as tf import tensorflow as tf
from tensorflow.contrib import slim as contrib_slim
from tensorflow.contrib import tpu as contrib_tpu
from object_detection.core import box_list from object_detection.core import box_list
from object_detection.core import box_list_ops from object_detection.core import box_list_ops
...@@ -31,7 +33,7 @@ from object_detection.utils import shape_utils ...@@ -31,7 +33,7 @@ from object_detection.utils import shape_utils
from object_detection.utils import variables_helper from object_detection.utils import variables_helper
from object_detection.utils import visualization_utils from object_detection.utils import visualization_utils
slim = tf.contrib.slim slim = contrib_slim
class SSDFeatureExtractor(object): class SSDFeatureExtractor(object):
...@@ -778,11 +780,18 @@ class SSDMetaArch(model.DetectionModel): ...@@ -778,11 +780,18 @@ class SSDMetaArch(model.DetectionModel):
detection_keypoints, 'raw_keypoint_locations') detection_keypoints, 'raw_keypoint_locations')
additional_fields[fields.BoxListFields.keypoints] = detection_keypoints additional_fields[fields.BoxListFields.keypoints] = detection_keypoints
with tf.init_scope():
if tf.executing_eagerly():
# soft device placement in eager mode will automatically handle
# outside compilation.
def _non_max_suppression_wrapper(kwargs):
return self._non_max_suppression_fn(**kwargs)
else:
def _non_max_suppression_wrapper(kwargs): def _non_max_suppression_wrapper(kwargs):
if self._nms_on_host: if self._nms_on_host:
# Note: NMS is not memory efficient on TPU. This force the NMS to run # Note: NMS is not memory efficient on TPU. This force the NMS
# outside of TPU. # to run outside of TPU.
return tf.contrib.tpu.outside_compilation( return contrib_tpu.outside_compilation(
lambda x: self._non_max_suppression_fn(**x), kwargs) lambda x: self._non_max_suppression_fn(**x), kwargs)
else: else:
return self._non_max_suppression_fn(**kwargs) return self._non_max_suppression_fn(**kwargs)
......
...@@ -19,13 +19,14 @@ from absl.testing import parameterized ...@@ -19,13 +19,14 @@ from absl.testing import parameterized
import numpy as np import numpy as np
import tensorflow as tf import tensorflow as tf
from tensorflow.contrib import slim as contrib_slim
from object_detection.meta_architectures import ssd_meta_arch from object_detection.meta_architectures import ssd_meta_arch
from object_detection.meta_architectures import ssd_meta_arch_test_lib from object_detection.meta_architectures import ssd_meta_arch_test_lib
from object_detection.protos import model_pb2 from object_detection.protos import model_pb2
from object_detection.utils import test_utils from object_detection.utils import test_utils
slim = tf.contrib.slim slim = contrib_slim
keras = tf.keras.layers keras = tf.keras.layers
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
import functools import functools
import tensorflow as tf import tensorflow as tf
from google.protobuf import text_format from google.protobuf import text_format
from tensorflow.contrib import slim as contrib_slim
from object_detection.builders import post_processing_builder from object_detection.builders import post_processing_builder
from object_detection.core import anchor_generator from object_detection.core import anchor_generator
...@@ -33,7 +34,7 @@ from object_detection.utils import ops ...@@ -33,7 +34,7 @@ from object_detection.utils import ops
from object_detection.utils import test_case from object_detection.utils import test_case
from object_detection.utils import test_utils from object_detection.utils import test_utils
slim = tf.contrib.slim slim = contrib_slim
keras = tf.keras.layers keras = tf.keras.layers
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment