"git@developer.sourcefind.cn:OpenDAS/torch-sparce.git" did not exist on "6a992437915f03ae1dc84e00e6b6f735429661ad"
Unverified Commit fd7b6887 authored by Jonathan Huang's avatar Jonathan Huang Committed by GitHub
Browse files

Merge pull request #3293 from pkulzc/master

Internal changes of object_detection 
parents f98ec55e 1efe98bb
...@@ -4,9 +4,156 @@ package( ...@@ -4,9 +4,156 @@ package(
default_visibility = ["//visibility:public"], default_visibility = ["//visibility:public"],
) )
load("//learning/brain/contrib/learn/tpu:tpu.bzl", "cloud_tpu_py_binaries")
licenses(["notice"]) licenses(["notice"])
# Apache 2.0 # Apache 2.0
exports_files(["LICENSE"])
py_library(
name = "inputs",
srcs = [
"inputs.py",
],
deps = [
"//tensorflow",
"//tensorflow/models/research/object_detection/builders:dataset_builder",
"//tensorflow/models/research/object_detection/builders:image_resizer_builder",
"//tensorflow/models/research/object_detection/builders:model_builder",
"//tensorflow/models/research/object_detection/builders:preprocessor_builder",
"//tensorflow/models/research/object_detection/protos:input_reader_py_pb2",
"//tensorflow/models/research/object_detection/protos:model_py_pb2",
"//tensorflow/models/research/object_detection/protos:train_py_pb2",
"//tensorflow/models/research/object_detection/utils:config_util",
"//tensorflow/models/research/object_detection/utils:dataset_util",
"//tensorflow/models/research/object_detection/utils:ops",
],
)
py_test(
name = "inputs_test",
srcs = [
"inputs_test.py",
],
data = [
"//tensorflow/models/research/object_detection/data:pet_label_map.pbtxt",
"//tensorflow/models/research/object_detection/samples/configs:faster_rcnn_resnet50_pets.config",
"//tensorflow/models/research/object_detection/samples/configs:ssd_inception_v2_pets.config",
"//tensorflow/models/research/object_detection/test_data:pets_examples.record",
],
deps = [
":inputs",
"//tensorflow",
"//tensorflow/models/research/object_detection/core:standard_fields",
"//tensorflow/models/research/object_detection/utils:config_util",
],
)
py_binary(
name = "model",
srcs = [
"model.py",
],
deps = [
":inputs",
":model_hparams",
"//tensorflow",
"//tensorflow/models/research/object_detection:eval_util",
"//tensorflow/models/research/object_detection/builders:model_builder",
"//tensorflow/models/research/object_detection/builders:optimizer_builder",
"//tensorflow/models/research/object_detection/metrics:coco_evaluation",
"//tensorflow/models/research/object_detection/utils:config_util",
"//tensorflow/models/research/object_detection/utils:label_map_util",
"//tensorflow/models/research/object_detection/utils:ops",
"//tensorflow/models/research/object_detection/utils:shape_utils",
"//tensorflow/models/research/object_detection/utils:variables_helper",
"//tensorflow/models/research/object_detection/utils:visualization_utils",
],
)
py_library(
name = "model_hparams",
srcs = [
"model_hparams.py",
],
deps = [
"//tensorflow",
],
)
py_test(
name = "model_test",
timeout = "long",
srcs = [
"model_test.py",
],
data = [
"//tensorflow/models/research/object_detection/data:pet_label_map.pbtxt",
"//tensorflow/models/research/object_detection/samples/configs:faster_rcnn_resnet50_pets.config",
"//tensorflow/models/research/object_detection/samples/configs:ssd_inception_v2_pets.config",
"//tensorflow/models/research/object_detection/test_data:pets_examples.record",
],
deps = [
":inputs",
":model",
":model_hparams",
":model_test_util",
"//mock",
"//tensorflow",
"//tensorflow/models/research/object_detection/core:standard_fields",
"//tensorflow/models/research/object_detection/data_decoders:tf_example_decoder",
"//tensorflow/models/research/object_detection/utils:config_util",
"//tensorflow/models/research/object_detection/utils:ops",
],
)
MODEL_TPU_DEPS = [
":inputs",
":model",
":model_hparams",
"//tensorflow",
"//tensorflow/models/research/object_detection:eval_util",
"//tensorflow/models/research/object_detection/builders:model_builder",
"//tensorflow/models/research/object_detection/builders:optimizer_builder",
"//tensorflow/models/research/object_detection/metrics:coco_evaluation",
"//tensorflow/models/research/object_detection/utils:config_util",
"//tensorflow/models/research/object_detection/utils:label_map_util",
"//tensorflow/models/research/object_detection/utils:ops",
"//tensorflow/models/research/object_detection/utils:variables_helper",
"//tensorflow/models/research/object_detection/utils:visualization_utils",
]
cloud_tpu_py_binaries(
name = "model_tpu",
srcs = [
"model_tpu.py",
],
main = "model_tpu.py",
deps = MODEL_TPU_DEPS,
)
py_library(
name = "model_tpu_lib",
srcs = [
"model_tpu.py",
],
deps = MODEL_TPU_DEPS,
)
py_library(
name = "model_test_util",
srcs = [
"model_test_util.py",
],
deps = [
":model",
":model_hparams",
"//tensorflow",
],
)
py_binary( py_binary(
name = "train", name = "train",
srcs = [ srcs = [
...@@ -15,9 +162,10 @@ py_binary( ...@@ -15,9 +162,10 @@ py_binary(
deps = [ deps = [
":trainer", ":trainer",
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/builders:input_reader_builder", "//tensorflow/models/research/object_detection/builders:dataset_builder",
"//tensorflow_models/object_detection/builders:model_builder", "//tensorflow/models/research/object_detection/builders:model_builder",
"//tensorflow_models/object_detection/utils:config_util", "//tensorflow/models/research/object_detection/utils:config_util",
"//tensorflow/models/research/object_detection/utils:dataset_util",
], ],
) )
...@@ -26,14 +174,14 @@ py_library( ...@@ -26,14 +174,14 @@ py_library(
srcs = ["trainer.py"], srcs = ["trainer.py"],
deps = [ deps = [
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/builders:optimizer_builder", "//tensorflow/models/research/object_detection/builders:optimizer_builder",
"//tensorflow_models/object_detection/builders:preprocessor_builder", "//tensorflow/models/research/object_detection/builders:preprocessor_builder",
"//tensorflow_models/object_detection/core:batcher", "//tensorflow/models/research/object_detection/core:batcher",
"//tensorflow_models/object_detection/core:preprocessor", "//tensorflow/models/research/object_detection/core:preprocessor",
"//tensorflow_models/object_detection/core:standard_fields", "//tensorflow/models/research/object_detection/core:standard_fields",
"//tensorflow_models/object_detection/utils:ops", "//tensorflow/models/research/object_detection/utils:ops",
"//tensorflow_models/object_detection/utils:variables_helper", "//tensorflow/models/research/object_detection/utils:variables_helper",
"//tensorflow_models/slim:model_deploy", "//third_party/tensorflow_models/slim:model_deploy",
], ],
) )
...@@ -43,10 +191,10 @@ py_test( ...@@ -43,10 +191,10 @@ py_test(
deps = [ deps = [
":trainer", ":trainer",
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/core:losses", "//tensorflow/models/research/object_detection/core:losses",
"//tensorflow_models/object_detection/core:model", "//tensorflow/models/research/object_detection/core:model",
"//tensorflow_models/object_detection/core:standard_fields", "//tensorflow/models/research/object_detection/core:standard_fields",
"//tensorflow_models/object_detection/protos:train_py_pb2", "//tensorflow/models/research/object_detection/protos:train_py_pb2",
], ],
) )
...@@ -57,13 +205,13 @@ py_library( ...@@ -57,13 +205,13 @@ py_library(
], ],
deps = [ deps = [
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/core:box_list", "//tensorflow/models/research/object_detection/core:box_list",
"//tensorflow_models/object_detection/core:box_list_ops", "//tensorflow/models/research/object_detection/core:box_list_ops",
"//tensorflow_models/object_detection/core:keypoint_ops", "//tensorflow/models/research/object_detection/core:keypoint_ops",
"//tensorflow_models/object_detection/core:standard_fields", "//tensorflow/models/research/object_detection/core:standard_fields",
"//tensorflow_models/object_detection/utils:label_map_util", "//tensorflow/models/research/object_detection/utils:label_map_util",
"//tensorflow_models/object_detection/utils:ops", "//tensorflow/models/research/object_detection/utils:ops",
"//tensorflow_models/object_detection/utils:visualization_utils", "//tensorflow/models/research/object_detection/utils:visualization_utils",
], ],
) )
...@@ -72,11 +220,12 @@ py_library( ...@@ -72,11 +220,12 @@ py_library(
srcs = ["evaluator.py"], srcs = ["evaluator.py"],
deps = [ deps = [
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection:eval_util", "//tensorflow/models/research/object_detection:eval_util",
"//tensorflow_models/object_detection/core:prefetcher", "//tensorflow/models/research/object_detection/core:prefetcher",
"//tensorflow_models/object_detection/core:standard_fields", "//tensorflow/models/research/object_detection/core:standard_fields",
"//tensorflow_models/object_detection/protos:eval_py_pb2", "//tensorflow/models/research/object_detection/metrics:coco_evaluation",
"//tensorflow_models/object_detection/utils:object_detection_evaluation", "//tensorflow/models/research/object_detection/protos:eval_py_pb2",
"//tensorflow/models/research/object_detection/utils:object_detection_evaluation",
], ],
) )
...@@ -88,10 +237,11 @@ py_binary( ...@@ -88,10 +237,11 @@ py_binary(
deps = [ deps = [
":evaluator", ":evaluator",
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/builders:input_reader_builder", "//tensorflow/models/research/object_detection/builders:dataset_builder",
"//tensorflow_models/object_detection/builders:model_builder", "//tensorflow/models/research/object_detection/builders:model_builder",
"//tensorflow_models/object_detection/utils:config_util", "//tensorflow/models/research/object_detection/utils:config_util",
"//tensorflow_models/object_detection/utils:label_map_util", "//tensorflow/models/research/object_detection/utils:dataset_util",
"//tensorflow/models/research/object_detection/utils:label_map_util",
], ],
) )
...@@ -103,9 +253,9 @@ py_library( ...@@ -103,9 +253,9 @@ py_library(
deps = [ deps = [
"//tensorflow", "//tensorflow",
"//tensorflow/python/tools:freeze_graph_lib", "//tensorflow/python/tools:freeze_graph_lib",
"//tensorflow_models/object_detection/builders:model_builder", "//tensorflow/models/research/object_detection/builders:model_builder",
"//tensorflow_models/object_detection/core:standard_fields", "//tensorflow/models/research/object_detection/core:standard_fields",
"//tensorflow_models/object_detection/data_decoders:tf_example_decoder", "//tensorflow/models/research/object_detection/data_decoders:tf_example_decoder",
], ],
) )
...@@ -117,9 +267,9 @@ py_test( ...@@ -117,9 +267,9 @@ py_test(
deps = [ deps = [
":exporter", ":exporter",
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/builders:model_builder", "//tensorflow/models/research/object_detection/builders:model_builder",
"//tensorflow_models/object_detection/core:model", "//tensorflow/models/research/object_detection/core:model",
"//tensorflow_models/object_detection/protos:pipeline_py_pb2", "//tensorflow/models/research/object_detection/protos:pipeline_py_pb2",
], ],
) )
...@@ -131,6 +281,6 @@ py_binary( ...@@ -131,6 +281,6 @@ py_binary(
deps = [ deps = [
":exporter", ":exporter",
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/protos:pipeline_py_pb2", "//tensorflow/models/research/object_detection/protos:pipeline_py_pb2",
], ],
) )
...@@ -32,6 +32,8 @@ https://scholar.googleusercontent.com/scholar.bib?q=info:l291WsrB-hQJ:scholar.go ...@@ -32,6 +32,8 @@ https://scholar.googleusercontent.com/scholar.bib?q=info:l291WsrB-hQJ:scholar.go
* Derek Chow, github: [derekjchow](https://github.com/derekjchow) * Derek Chow, github: [derekjchow](https://github.com/derekjchow)
* Chen Sun, github: [jesu9](https://github.com/jesu9) * Chen Sun, github: [jesu9](https://github.com/jesu9)
* Menglong Zhu, github: [dreamdragon](https://github.com/dreamdragon) * Menglong Zhu, github: [dreamdragon](https://github.com/dreamdragon)
* Alireza Fathi, github: [afathi3](https://github.com/afathi3)
* Zhichao Lu, github: [pkulzc](https://github.com/pkulzc)
## Table of contents ## Table of contents
...@@ -67,6 +69,8 @@ Extras: ...@@ -67,6 +69,8 @@ Extras:
Supported object detection evaluation protocols</a><br> Supported object detection evaluation protocols</a><br>
* <a href='g3doc/oid_inference_and_evaluation.md'> * <a href='g3doc/oid_inference_and_evaluation.md'>
Inference and evaluation on the Open Images dataset</a><br> Inference and evaluation on the Open Images dataset</a><br>
* <a href='g3doc/instance_segmentation.md'>
Run an instance segmentation model
## Getting Help ## Getting Help
...@@ -75,7 +79,7 @@ API, create a new question on [StackOverflow](https://stackoverflow.com/) with ...@@ -75,7 +79,7 @@ API, create a new question on [StackOverflow](https://stackoverflow.com/) with
the tags "tensorflow" and "object-detection". the tags "tensorflow" and "object-detection".
Please report bugs (actually broken code, not usage questions) to the Please report bugs (actually broken code, not usage questions) to the
tensorflow/models Github tensorflow/models GitHub
[issue tracker](https://github.com/tensorflow/models/issues), prefixing the [issue tracker](https://github.com/tensorflow/models/issues), prefixing the
issue name with "object_detection". issue name with "object_detection".
...@@ -83,6 +87,15 @@ issue name with "object_detection". ...@@ -83,6 +87,15 @@ issue name with "object_detection".
## Release information ## Release information
### February 9, 2018
We now support instance segmentation!! In this API update we support a number of instance segmentation models similar to those discussed in the [Mask R-CNN paper](https://arxiv.org/abs/1703.06870). For further details refer to
[our slides](http://presentations.cocodataset.org/Places17-GMRI.pdf) from the 2017 Coco + Places Workshop.
Refer to the section on [Running an Instance Segmentation Model](g3doc/instance_segmentation.md) for instructions on how to configure a model
that predicts masks in addition to object bounding boxes.
<b>Thanks to contributors</b>: Alireza Fathi, Zhichao Lu, Vivek Rathod, Ronny Votel, Jonathan Huang
### November 17, 2017 ### November 17, 2017
As a part of the Open Images V3 release we have released: As a part of the Open Images V3 release we have released:
......
...@@ -14,9 +14,9 @@ py_library( ...@@ -14,9 +14,9 @@ py_library(
], ],
deps = [ deps = [
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/core:anchor_generator", "//tensorflow/models/research/object_detection/core:anchor_generator",
"//tensorflow_models/object_detection/core:box_list", "//tensorflow/models/research/object_detection/core:box_list",
"//tensorflow_models/object_detection/utils:ops", "//tensorflow/models/research/object_detection/utils:ops",
], ],
) )
...@@ -28,6 +28,7 @@ py_test( ...@@ -28,6 +28,7 @@ py_test(
deps = [ deps = [
":grid_anchor_generator", ":grid_anchor_generator",
"//tensorflow", "//tensorflow",
"//tensorflow/models/research/object_detection/utils:test_case",
], ],
) )
...@@ -39,8 +40,8 @@ py_library( ...@@ -39,8 +40,8 @@ py_library(
deps = [ deps = [
":grid_anchor_generator", ":grid_anchor_generator",
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/core:anchor_generator", "//tensorflow/models/research/object_detection/core:anchor_generator",
"//tensorflow_models/object_detection/core:box_list_ops", "//tensorflow/models/research/object_detection/core:box_list_ops",
], ],
) )
...@@ -51,6 +52,32 @@ py_test( ...@@ -51,6 +52,32 @@ py_test(
], ],
deps = [ deps = [
":multiple_grid_anchor_generator", ":multiple_grid_anchor_generator",
"//third_party/py/numpy", "//numpy",
"//tensorflow/models/research/object_detection/utils:test_case",
],
)
py_library(
name = "multiscale_grid_anchor_generator",
srcs = [
"multiscale_grid_anchor_generator.py",
],
deps = [
":grid_anchor_generator",
"//tensorflow",
"//tensorflow/models/research/object_detection/core:anchor_generator",
"//tensorflow/models/research/object_detection/core:box_list_ops",
],
)
py_test(
name = "multiscale_grid_anchor_generator_test",
srcs = [
"multiscale_grid_anchor_generator_test.py",
],
deps = [
":multiscale_grid_anchor_generator",
"//numpy",
"//tensorflow/models/research/object_detection/utils:test_case",
], ],
) )
...@@ -42,25 +42,27 @@ class GridAnchorGenerator(anchor_generator.AnchorGenerator): ...@@ -42,25 +42,27 @@ class GridAnchorGenerator(anchor_generator.AnchorGenerator):
scales: a list of (float) scales, default=(0.5, 1.0, 2.0) scales: a list of (float) scales, default=(0.5, 1.0, 2.0)
aspect_ratios: a list of (float) aspect ratios, default=(0.5, 1.0, 2.0) aspect_ratios: a list of (float) aspect ratios, default=(0.5, 1.0, 2.0)
base_anchor_size: base anchor size as height, width ( base_anchor_size: base anchor size as height, width (
(length-2 float32 list, default=[256, 256]) (length-2 float32 list or tensor, default=[256, 256])
anchor_stride: difference in centers between base anchors for adjacent anchor_stride: difference in centers between base anchors for adjacent
grid positions (length-2 float32 list, default=[16, 16]) grid positions (length-2 float32 list or tensor,
default=[16, 16])
anchor_offset: center of the anchor with scale and aspect ratio 1 for the anchor_offset: center of the anchor with scale and aspect ratio 1 for the
upper left element of the grid, this should be zero for upper left element of the grid, this should be zero for
feature networks with only VALID padding and even receptive feature networks with only VALID padding and even receptive
field size, but may need additional calculation if other field size, but may need additional calculation if other
padding is used (length-2 float32 tensor, default=[0, 0]) padding is used (length-2 float32 list or tensor,
default=[0, 0])
""" """
# Handle argument defaults # Handle argument defaults
if base_anchor_size is None: if base_anchor_size is None:
base_anchor_size = [256, 256] base_anchor_size = [256, 256]
base_anchor_size = tf.constant(base_anchor_size, tf.float32) base_anchor_size = tf.to_float(tf.convert_to_tensor(base_anchor_size))
if anchor_stride is None: if anchor_stride is None:
anchor_stride = [16, 16] anchor_stride = [16, 16]
anchor_stride = tf.constant(anchor_stride, dtype=tf.float32) anchor_stride = tf.to_float(tf.convert_to_tensor(anchor_stride))
if anchor_offset is None: if anchor_offset is None:
anchor_offset = [0, 0] anchor_offset = [0, 0]
anchor_offset = tf.constant(anchor_offset, dtype=tf.float32) anchor_offset = tf.to_float(tf.convert_to_tensor(anchor_offset))
self._scales = scales self._scales = scales
self._aspect_ratios = aspect_ratios self._aspect_ratios = aspect_ratios
...@@ -91,7 +93,11 @@ class GridAnchorGenerator(anchor_generator.AnchorGenerator): ...@@ -91,7 +93,11 @@ class GridAnchorGenerator(anchor_generator.AnchorGenerator):
allowed. allowed.
Returns: Returns:
boxes: a BoxList holding a collection of N anchor boxes boxes: a BoxList holding a collection of N anchor boxes. Additionally
this BoxList also holds a `feature_map_index` field which is set to 0
for each anchor; this field exists for interchangeability reasons with
the MultipleGridAnchorGenerator (see the docstring for the corresponding
`_generate` function in multiple_grid_anchor_generator.py)
Raises: Raises:
ValueError: if feature_map_shape_list, box_specs_list do not have the same ValueError: if feature_map_shape_list, box_specs_list do not have the same
length. length.
...@@ -109,13 +115,20 @@ class GridAnchorGenerator(anchor_generator.AnchorGenerator): ...@@ -109,13 +115,20 @@ class GridAnchorGenerator(anchor_generator.AnchorGenerator):
self._aspect_ratios) self._aspect_ratios)
scales_grid = tf.reshape(scales_grid, [-1]) scales_grid = tf.reshape(scales_grid, [-1])
aspect_ratios_grid = tf.reshape(aspect_ratios_grid, [-1]) aspect_ratios_grid = tf.reshape(aspect_ratios_grid, [-1])
return tile_anchors(grid_height, anchors = tile_anchors(grid_height,
grid_width, grid_width,
scales_grid, scales_grid,
aspect_ratios_grid, aspect_ratios_grid,
self._base_anchor_size, self._base_anchor_size,
self._anchor_stride, self._anchor_stride,
self._anchor_offset) self._anchor_offset)
num_anchors = anchors.num_boxes_static()
if num_anchors is None:
num_anchors = anchors.num_boxes()
anchor_indices = tf.zeros([num_anchors])
anchors.add_field('feature_map_index', anchor_indices)
return anchors
def tile_anchors(grid_height, def tile_anchors(grid_height,
......
...@@ -14,62 +14,90 @@ ...@@ -14,62 +14,90 @@
# ============================================================================== # ==============================================================================
"""Tests for object_detection.grid_anchor_generator.""" """Tests for object_detection.grid_anchor_generator."""
import numpy as np
import tensorflow as tf import tensorflow as tf
from object_detection.anchor_generators import grid_anchor_generator from object_detection.anchor_generators import grid_anchor_generator
from object_detection.utils import test_case
class GridAnchorGeneratorTest(tf.test.TestCase): class GridAnchorGeneratorTest(test_case.TestCase):
def test_construct_single_anchor(self): def test_construct_single_anchor(self):
"""Builds a 1x1 anchor grid to test the size of the output boxes.""" """Builds a 1x1 anchor grid to test the size of the output boxes."""
scales = [0.5, 1.0, 2.0] def graph_fn():
aspect_ratios = [0.25, 1.0, 4.0] scales = [0.5, 1.0, 2.0]
anchor_offset = [7, -3] aspect_ratios = [0.25, 1.0, 4.0]
anchor_offset = [7, -3]
anchor_generator = grid_anchor_generator.GridAnchorGenerator(
scales, aspect_ratios, anchor_offset=anchor_offset)
anchors = anchor_generator.generate(feature_map_shape_list=[(1, 1)])
anchor_corners = anchors.get()
return (anchor_corners,)
exp_anchor_corners = [[-121, -35, 135, 29], [-249, -67, 263, 61], exp_anchor_corners = [[-121, -35, 135, 29], [-249, -67, 263, 61],
[-505, -131, 519, 125], [-57, -67, 71, 61], [-505, -131, 519, 125], [-57, -67, 71, 61],
[-121, -131, 135, 125], [-249, -259, 263, 253], [-121, -131, 135, 125], [-249, -259, 263, 253],
[-25, -131, 39, 125], [-57, -259, 71, 253], [-25, -131, 39, 125], [-57, -259, 71, 253],
[-121, -515, 135, 509]] [-121, -515, 135, 509]]
anchor_corners_out = self.execute(graph_fn, [])
anchor_generator = grid_anchor_generator.GridAnchorGenerator( self.assertAllClose(anchor_corners_out, exp_anchor_corners)
scales, aspect_ratios,
anchor_offset=anchor_offset)
anchors = anchor_generator.generate(feature_map_shape_list=[(1, 1)])
anchor_corners = anchors.get()
with self.test_session():
anchor_corners_out = anchor_corners.eval()
self.assertAllClose(anchor_corners_out, exp_anchor_corners)
def test_construct_anchor_grid(self): def test_construct_anchor_grid(self):
base_anchor_size = [10, 10] def graph_fn():
anchor_stride = [19, 19] base_anchor_size = [10, 10]
anchor_offset = [0, 0] anchor_stride = [19, 19]
scales = [0.5, 1.0, 2.0] anchor_offset = [0, 0]
aspect_ratios = [1.0] scales = [0.5, 1.0, 2.0]
aspect_ratios = [1.0]
anchor_generator = grid_anchor_generator.GridAnchorGenerator(
scales,
aspect_ratios,
base_anchor_size=base_anchor_size,
anchor_stride=anchor_stride,
anchor_offset=anchor_offset)
anchors = anchor_generator.generate(feature_map_shape_list=[(2, 2)])
anchor_corners = anchors.get()
return (anchor_corners,)
exp_anchor_corners = [[-2.5, -2.5, 2.5, 2.5], [-5., -5., 5., 5.], exp_anchor_corners = [[-2.5, -2.5, 2.5, 2.5], [-5., -5., 5., 5.],
[-10., -10., 10., 10.], [-2.5, 16.5, 2.5, 21.5], [-10., -10., 10., 10.], [-2.5, 16.5, 2.5, 21.5],
[-5., 14., 5, 24], [-10., 9., 10, 29], [-5., 14., 5, 24], [-10., 9., 10, 29],
[16.5, -2.5, 21.5, 2.5], [14., -5., 24, 5], [16.5, -2.5, 21.5, 2.5], [14., -5., 24, 5],
[9., -10., 29, 10], [16.5, 16.5, 21.5, 21.5], [9., -10., 29, 10], [16.5, 16.5, 21.5, 21.5],
[14., 14., 24, 24], [9., 9., 29, 29]] [14., 14., 24, 24], [9., 9., 29, 29]]
anchor_corners_out = self.execute(graph_fn, [])
self.assertAllClose(anchor_corners_out, exp_anchor_corners)
anchor_generator = grid_anchor_generator.GridAnchorGenerator( def test_construct_anchor_grid_with_dynamic_feature_map_shapes(self):
scales, def graph_fn(feature_map_height, feature_map_width):
aspect_ratios, base_anchor_size = [10, 10]
base_anchor_size=base_anchor_size, anchor_stride = [19, 19]
anchor_stride=anchor_stride, anchor_offset = [0, 0]
anchor_offset=anchor_offset) scales = [0.5, 1.0, 2.0]
aspect_ratios = [1.0]
anchor_generator = grid_anchor_generator.GridAnchorGenerator(
scales,
aspect_ratios,
base_anchor_size=base_anchor_size,
anchor_stride=anchor_stride,
anchor_offset=anchor_offset)
anchors = anchor_generator.generate(feature_map_shape_list=[(2, 2)]) anchors = anchor_generator.generate(
anchor_corners = anchors.get() feature_map_shape_list=[(feature_map_height, feature_map_width)])
anchor_corners = anchors.get()
return (anchor_corners,)
with self.test_session(): exp_anchor_corners = [[-2.5, -2.5, 2.5, 2.5], [-5., -5., 5., 5.],
anchor_corners_out = anchor_corners.eval() [-10., -10., 10., 10.], [-2.5, 16.5, 2.5, 21.5],
self.assertAllClose(anchor_corners_out, exp_anchor_corners) [-5., 14., 5, 24], [-10., 9., 10, 29],
[16.5, -2.5, 21.5, 2.5], [14., -5., 24, 5],
[9., -10., 29, 10], [16.5, 16.5, 21.5, 21.5],
[14., 14., 24, 24], [9., 9., 29, 29]]
anchor_corners_out = self.execute_cpu(graph_fn,
[np.array(2, dtype=np.int32),
np.array(2, dtype=np.int32)])
self.assertAllClose(anchor_corners_out, exp_anchor_corners)
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -165,7 +165,10 @@ class MultipleGridAnchorGenerator(anchor_generator.AnchorGenerator): ...@@ -165,7 +165,10 @@ class MultipleGridAnchorGenerator(anchor_generator.AnchorGenerator):
grid. grid.
Returns: Returns:
boxes: a BoxList holding a collection of N anchor boxes boxes: a BoxList holding a collection of N anchor boxes. Additionally
this BoxList also holds a `feature_map_index` field which, for each
anchor, stores the index of the corresponding feature map which was used
to generate it.
Raises: Raises:
ValueError: if feature_map_shape_list, box_specs_list do not have the same ValueError: if feature_map_shape_list, box_specs_list do not have the same
length. length.
...@@ -208,6 +211,7 @@ class MultipleGridAnchorGenerator(anchor_generator.AnchorGenerator): ...@@ -208,6 +211,7 @@ class MultipleGridAnchorGenerator(anchor_generator.AnchorGenerator):
raise ValueError('%s must be a list of pairs.' % arg_name) raise ValueError('%s must be a list of pairs.' % arg_name)
anchor_grid_list = [] anchor_grid_list = []
anchor_indices_list = []
min_im_shape = tf.minimum(im_height, im_width) min_im_shape = tf.minimum(im_height, im_width)
scale_height = min_im_shape / im_height scale_height = min_im_shape / im_height
scale_width = min_im_shape / im_width scale_width = min_im_shape / im_width
...@@ -215,32 +219,40 @@ class MultipleGridAnchorGenerator(anchor_generator.AnchorGenerator): ...@@ -215,32 +219,40 @@ class MultipleGridAnchorGenerator(anchor_generator.AnchorGenerator):
scale_height * self._base_anchor_size[0], scale_height * self._base_anchor_size[0],
scale_width * self._base_anchor_size[1] scale_width * self._base_anchor_size[1]
] ]
for grid_size, scales, aspect_ratios, stride, offset in zip( for feature_map_index, (
feature_map_shape_list, self._scales, self._aspect_ratios, grid_size, scales, aspect_ratios, stride, offset) in enumerate(
anchor_strides, anchor_offsets): zip(feature_map_shape_list, self._scales, self._aspect_ratios,
anchor_grid_list.append( anchor_strides, anchor_offsets)):
grid_anchor_generator.tile_anchors( tiled_anchors = grid_anchor_generator.tile_anchors(
grid_height=grid_size[0], grid_height=grid_size[0],
grid_width=grid_size[1], grid_width=grid_size[1],
scales=scales, scales=scales,
aspect_ratios=aspect_ratios, aspect_ratios=aspect_ratios,
base_anchor_size=base_anchor_size, base_anchor_size=base_anchor_size,
anchor_stride=stride, anchor_stride=stride,
anchor_offset=offset)) anchor_offset=offset)
anchor_grid_list.append(tiled_anchors)
num_anchors_in_layer = tiled_anchors.num_boxes_static()
if num_anchors_in_layer is None:
num_anchors_in_layer = tiled_anchors.num_boxes()
anchor_indices_list.append(
feature_map_index * tf.ones([num_anchors_in_layer]))
concatenated_anchors = box_list_ops.concatenate(anchor_grid_list) concatenated_anchors = box_list_ops.concatenate(anchor_grid_list)
anchor_indices = tf.concat(anchor_indices_list, 0)
num_anchors = concatenated_anchors.num_boxes_static() num_anchors = concatenated_anchors.num_boxes_static()
if num_anchors is None: if num_anchors is None:
num_anchors = concatenated_anchors.num_boxes() num_anchors = concatenated_anchors.num_boxes()
if self._clip_window is not None: if self._clip_window is not None:
concatenated_anchors = box_list_ops.clip_to_window( concatenated_anchors = box_list_ops.clip_to_window(
concatenated_anchors, self._clip_window, filter_nonoverlapping=False) concatenated_anchors, self._clip_window, filter_nonoverlapping=False)
# TODO(jonathanhuang): make reshape an option for the clip_to_window op # TODO: make reshape an option for the clip_to_window op
concatenated_anchors.set( concatenated_anchors.set(
tf.reshape(concatenated_anchors.get(), [num_anchors, 4])) tf.reshape(concatenated_anchors.get(), [num_anchors, 4]))
stddevs_tensor = 0.01 * tf.ones( stddevs_tensor = 0.01 * tf.ones(
[num_anchors, 4], dtype=tf.float32, name='stddevs') [num_anchors, 4], dtype=tf.float32, name='stddevs')
concatenated_anchors.add_field('stddev', stddevs_tensor) concatenated_anchors.add_field('stddev', stddevs_tensor)
concatenated_anchors.add_field('feature_map_index', anchor_indices)
return concatenated_anchors return concatenated_anchors
......
...@@ -20,35 +20,45 @@ import numpy as np ...@@ -20,35 +20,45 @@ import numpy as np
import tensorflow as tf import tensorflow as tf
from object_detection.anchor_generators import multiple_grid_anchor_generator as ag from object_detection.anchor_generators import multiple_grid_anchor_generator as ag
from object_detection.utils import test_case
class MultipleGridAnchorGeneratorTest(tf.test.TestCase): class MultipleGridAnchorGeneratorTest(test_case.TestCase):
def test_construct_single_anchor_grid(self): def test_construct_single_anchor_grid(self):
"""Builds a 1x1 anchor grid to test the size of the output boxes.""" """Builds a 1x1 anchor grid to test the size of the output boxes."""
def graph_fn():
box_specs_list = [[(.5, .25), (1.0, .25), (2.0, .25),
(.5, 1.0), (1.0, 1.0), (2.0, 1.0),
(.5, 4.0), (1.0, 4.0), (2.0, 4.0)]]
anchor_generator = ag.MultipleGridAnchorGenerator(
box_specs_list,
base_anchor_size=tf.constant([256, 256], dtype=tf.float32),
anchor_strides=[(16, 16)],
anchor_offsets=[(7, -3)])
anchors = anchor_generator.generate(feature_map_shape_list=[(1, 1)])
return anchors.get()
exp_anchor_corners = [[-121, -35, 135, 29], [-249, -67, 263, 61], exp_anchor_corners = [[-121, -35, 135, 29], [-249, -67, 263, 61],
[-505, -131, 519, 125], [-57, -67, 71, 61], [-505, -131, 519, 125], [-57, -67, 71, 61],
[-121, -131, 135, 125], [-249, -259, 263, 253], [-121, -131, 135, 125], [-249, -259, 263, 253],
[-25, -131, 39, 125], [-57, -259, 71, 253], [-25, -131, 39, 125], [-57, -259, 71, 253],
[-121, -515, 135, 509]] [-121, -515, 135, 509]]
box_specs_list = [[(.5, .25), (1.0, .25), (2.0, .25), anchor_corners_out = self.execute(graph_fn, [])
(.5, 1.0), (1.0, 1.0), (2.0, 1.0), self.assertAllClose(anchor_corners_out, exp_anchor_corners)
(.5, 4.0), (1.0, 4.0), (2.0, 4.0)]]
anchor_generator = ag.MultipleGridAnchorGenerator(
box_specs_list,
base_anchor_size=tf.constant([256, 256], dtype=tf.float32),
anchor_strides=[(16, 16)],
anchor_offsets=[(7, -3)])
anchors = anchor_generator.generate(feature_map_shape_list=[(1, 1)])
anchor_corners = anchors.get()
with self.test_session():
anchor_corners_out = anchor_corners.eval()
self.assertAllClose(anchor_corners_out, exp_anchor_corners)
def test_construct_anchor_grid(self): def test_construct_anchor_grid(self):
box_specs_list = [[(0.5, 1.0), (1.0, 1.0), (2.0, 1.0)]] def graph_fn():
box_specs_list = [[(0.5, 1.0), (1.0, 1.0), (2.0, 1.0)]]
anchor_generator = ag.MultipleGridAnchorGenerator(
box_specs_list,
base_anchor_size=tf.constant([10, 10], dtype=tf.float32),
anchor_strides=[(19, 19)],
anchor_offsets=[(0, 0)])
anchors = anchor_generator.generate(feature_map_shape_list=[(2, 2)])
return anchors.get()
exp_anchor_corners = [[-2.5, -2.5, 2.5, 2.5], [-5., -5., 5., 5.], exp_anchor_corners = [[-2.5, -2.5, 2.5, 2.5], [-5., -5., 5., 5.],
[-10., -10., 10., 10.], [-2.5, 16.5, 2.5, 21.5], [-10., -10., 10., 10.], [-2.5, 16.5, 2.5, 21.5],
[-5., 14., 5, 24], [-10., 9., 10, 29], [-5., 14., 5, 24], [-10., 9., 10, 29],
...@@ -56,55 +66,74 @@ class MultipleGridAnchorGeneratorTest(tf.test.TestCase): ...@@ -56,55 +66,74 @@ class MultipleGridAnchorGeneratorTest(tf.test.TestCase):
[9., -10., 29, 10], [16.5, 16.5, 21.5, 21.5], [9., -10., 29, 10], [16.5, 16.5, 21.5, 21.5],
[14., 14., 24, 24], [9., 9., 29, 29]] [14., 14., 24, 24], [9., 9., 29, 29]]
anchor_generator = ag.MultipleGridAnchorGenerator( anchor_corners_out = self.execute(graph_fn, [])
box_specs_list, self.assertAllClose(anchor_corners_out, exp_anchor_corners)
base_anchor_size=tf.constant([10, 10], dtype=tf.float32),
anchor_strides=[(19, 19)],
anchor_offsets=[(0, 0)])
anchors = anchor_generator.generate(feature_map_shape_list=[(2, 2)])
anchor_corners = anchors.get()
with self.test_session():
anchor_corners_out = anchor_corners.eval()
self.assertAllClose(anchor_corners_out, exp_anchor_corners)
def test_construct_anchor_grid_non_square(self): def test_construct_anchor_grid_non_square(self):
box_specs_list = [[(1.0, 1.0)]]
def graph_fn():
box_specs_list = [[(1.0, 1.0)]]
anchor_generator = ag.MultipleGridAnchorGenerator(
box_specs_list, base_anchor_size=tf.constant([1, 1],
dtype=tf.float32))
anchors = anchor_generator.generate(feature_map_shape_list=[(tf.constant(
1, dtype=tf.int32), tf.constant(2, dtype=tf.int32))])
return anchors.get()
exp_anchor_corners = [[0., -0.25, 1., 0.75], [0., 0.25, 1., 1.25]] exp_anchor_corners = [[0., -0.25, 1., 0.75], [0., 0.25, 1., 1.25]]
anchor_corners_out = self.execute(graph_fn, [])
self.assertAllClose(anchor_corners_out, exp_anchor_corners)
anchor_generator = ag.MultipleGridAnchorGenerator( def test_construct_dynamic_size_anchor_grid(self):
box_specs_list, base_anchor_size=tf.constant([1, 1], dtype=tf.float32))
anchors = anchor_generator.generate(feature_map_shape_list=[(tf.constant(
1, dtype=tf.int32), tf.constant(2, dtype=tf.int32))])
anchor_corners = anchors.get()
with self.test_session(): def graph_fn(height, width):
anchor_corners_out = anchor_corners.eval() box_specs_list = [[(1.0, 1.0)]]
self.assertAllClose(anchor_corners_out, exp_anchor_corners) anchor_generator = ag.MultipleGridAnchorGenerator(
box_specs_list, base_anchor_size=tf.constant([1, 1],
dtype=tf.float32))
anchors = anchor_generator.generate(feature_map_shape_list=[(height,
width)])
return anchors.get()
def test_construct_anchor_grid_normalized(self): exp_anchor_corners = [[0., -0.25, 1., 0.75], [0., 0.25, 1., 1.25]]
box_specs_list = [[(1.0, 1.0)]]
exp_anchor_corners = [[0., 0., 1., 0.5], [0., 0.5, 1., 1.]] anchor_corners_out = self.execute_cpu(graph_fn,
[np.array(1, dtype=np.int32),
np.array(2, dtype=np.int32)])
self.assertAllClose(anchor_corners_out, exp_anchor_corners)
anchor_generator = ag.MultipleGridAnchorGenerator( def test_construct_anchor_grid_normalized(self):
box_specs_list, base_anchor_size=tf.constant([1, 1], dtype=tf.float32)) def graph_fn():
anchors = anchor_generator.generate( box_specs_list = [[(1.0, 1.0)]]
feature_map_shape_list=[(tf.constant(1, dtype=tf.int32), tf.constant(
2, dtype=tf.int32))], anchor_generator = ag.MultipleGridAnchorGenerator(
im_height=320, box_specs_list, base_anchor_size=tf.constant([1, 1],
im_width=640) dtype=tf.float32))
anchor_corners = anchors.get() anchors = anchor_generator.generate(
feature_map_shape_list=[(tf.constant(1, dtype=tf.int32), tf.constant(
2, dtype=tf.int32))],
im_height=320,
im_width=640)
return anchors.get()
with self.test_session(): exp_anchor_corners = [[0., 0., 1., 0.5], [0., 0.5, 1., 1.]]
anchor_corners_out = anchor_corners.eval() anchor_corners_out = self.execute(graph_fn, [])
self.assertAllClose(anchor_corners_out, exp_anchor_corners) self.assertAllClose(anchor_corners_out, exp_anchor_corners)
def test_construct_multiple_grids(self): def test_construct_multiple_grids(self):
box_specs_list = [[(1.0, 1.0), (2.0, 1.0), (1.0, 0.5)],
[(1.0, 1.0), (1.0, 0.5)]]
def graph_fn():
box_specs_list = [[(1.0, 1.0), (2.0, 1.0), (1.0, 0.5)],
[(1.0, 1.0), (1.0, 0.5)]]
anchor_generator = ag.MultipleGridAnchorGenerator(
box_specs_list,
base_anchor_size=tf.constant([1.0, 1.0], dtype=tf.float32),
anchor_strides=[(.25, .25), (.5, .5)],
anchor_offsets=[(.125, .125), (.25, .25)])
anchors = anchor_generator.generate(feature_map_shape_list=[(4, 4),
(2, 2)])
return anchors.get()
# height and width of box with .5 aspect ratio # height and width of box with .5 aspect ratio
h = np.sqrt(2) h = np.sqrt(2)
w = 1.0/np.sqrt(2) w = 1.0/np.sqrt(2)
...@@ -121,26 +150,27 @@ class MultipleGridAnchorGeneratorTest(tf.test.TestCase): ...@@ -121,26 +150,27 @@ class MultipleGridAnchorGeneratorTest(tf.test.TestCase):
[.125-1.0, .125-1.0, .125+1.0, .125+1.0], [.125-1.0, .125-1.0, .125+1.0, .125+1.0],
[.125-.5*h, .125-.5*w, .125+.5*h, .125+.5*w],] [.125-.5*h, .125-.5*w, .125+.5*h, .125+.5*w],]
anchor_generator = ag.MultipleGridAnchorGenerator( anchor_corners_out = self.execute(graph_fn, [])
box_specs_list, self.assertEquals(anchor_corners_out.shape, (56, 4))
base_anchor_size=tf.constant([1.0, 1.0], dtype=tf.float32), big_grid_corners = anchor_corners_out[0:3, :]
anchor_strides=[(.25, .25), (.5, .5)], small_grid_corners = anchor_corners_out[48:, :]
anchor_offsets=[(.125, .125), (.25, .25)]) self.assertAllClose(small_grid_corners, exp_small_grid_corners)
anchors = anchor_generator.generate(feature_map_shape_list=[(4, 4), (2, 2)]) self.assertAllClose(big_grid_corners, exp_big_grid_corners)
anchor_corners = anchors.get()
with self.test_session():
anchor_corners_out = anchor_corners.eval()
self.assertEquals(anchor_corners_out.shape, (56, 4))
big_grid_corners = anchor_corners_out[0:3, :]
small_grid_corners = anchor_corners_out[48:, :]
self.assertAllClose(small_grid_corners, exp_small_grid_corners)
self.assertAllClose(big_grid_corners, exp_big_grid_corners)
def test_construct_multiple_grids_with_clipping(self): def test_construct_multiple_grids_with_clipping(self):
box_specs_list = [[(1.0, 1.0), (2.0, 1.0), (1.0, 0.5)],
[(1.0, 1.0), (1.0, 0.5)]]
def graph_fn():
box_specs_list = [[(1.0, 1.0), (2.0, 1.0), (1.0, 0.5)],
[(1.0, 1.0), (1.0, 0.5)]]
clip_window = tf.constant([0, 0, 1, 1], dtype=tf.float32)
anchor_generator = ag.MultipleGridAnchorGenerator(
box_specs_list,
base_anchor_size=tf.constant([1.0, 1.0], dtype=tf.float32),
clip_window=clip_window)
anchors = anchor_generator.generate(feature_map_shape_list=[(4, 4),
(2, 2)])
return anchors.get()
# height and width of box with .5 aspect ratio # height and width of box with .5 aspect ratio
h = np.sqrt(2) h = np.sqrt(2)
w = 1.0/np.sqrt(2) w = 1.0/np.sqrt(2)
...@@ -153,18 +183,9 @@ class MultipleGridAnchorGeneratorTest(tf.test.TestCase): ...@@ -153,18 +183,9 @@ class MultipleGridAnchorGeneratorTest(tf.test.TestCase):
[.25, .25, 1, 1], [.25, .25, 1, 1],
[.75-.5*h, .75-.5*w, 1, 1]] [.75-.5*h, .75-.5*w, 1, 1]]
clip_window = tf.constant([0, 0, 1, 1], dtype=tf.float32) anchor_corners_out = self.execute(graph_fn, [])
anchor_generator = ag.MultipleGridAnchorGenerator( small_grid_corners = anchor_corners_out[48:, :]
box_specs_list, self.assertAllClose(small_grid_corners, exp_small_grid_corners)
base_anchor_size=tf.constant([1.0, 1.0], dtype=tf.float32),
clip_window=clip_window)
anchors = anchor_generator.generate(feature_map_shape_list=[(4, 4), (2, 2)])
anchor_corners = anchors.get()
with self.test_session():
anchor_corners_out = anchor_corners.eval()
small_grid_corners = anchor_corners_out[48:, :]
self.assertAllClose(small_grid_corners, exp_small_grid_corners)
def test_invalid_box_specs(self): def test_invalid_box_specs(self):
# not all box specs are pairs # not all box specs are pairs
...@@ -229,38 +250,39 @@ class MultipleGridAnchorGeneratorTest(tf.test.TestCase): ...@@ -229,38 +250,39 @@ class MultipleGridAnchorGeneratorTest(tf.test.TestCase):
anchor_generator.generate(feature_map_shape_list=[(4), (2, 2)]) anchor_generator.generate(feature_map_shape_list=[(4), (2, 2)])
class CreateSSDAnchorsTest(tf.test.TestCase): class CreateSSDAnchorsTest(test_case.TestCase):
def test_create_ssd_anchors_returns_correct_shape(self): def test_create_ssd_anchors_returns_correct_shape(self):
anchor_generator = ag.create_ssd_anchors(
num_layers=6, def graph_fn1():
min_scale=0.2, anchor_generator = ag.create_ssd_anchors(
max_scale=0.95, num_layers=6,
aspect_ratios=(1.0, 2.0, 3.0, 1.0 / 2, 1.0 / 3), min_scale=0.2,
reduce_boxes_in_lowest_layer=True) max_scale=0.95,
aspect_ratios=(1.0, 2.0, 3.0, 1.0 / 2, 1.0 / 3),
feature_map_shape_list = [(38, 38), (19, 19), (10, 10), reduce_boxes_in_lowest_layer=True)
(5, 5), (3, 3), (1, 1)]
anchors = anchor_generator.generate( feature_map_shape_list = [(38, 38), (19, 19), (10, 10),
feature_map_shape_list=feature_map_shape_list) (5, 5), (3, 3), (1, 1)]
anchor_corners = anchors.get() anchors = anchor_generator.generate(
with self.test_session(): feature_map_shape_list=feature_map_shape_list)
anchor_corners_out = anchor_corners.eval() return anchors.get()
self.assertEquals(anchor_corners_out.shape, (7308, 4)) anchor_corners_out = self.execute(graph_fn1, [])
self.assertEquals(anchor_corners_out.shape, (7308, 4))
anchor_generator = ag.create_ssd_anchors(
num_layers=6, min_scale=0.2, max_scale=0.95, def graph_fn2():
aspect_ratios=(1.0, 2.0, 3.0, 1.0/2, 1.0/3), anchor_generator = ag.create_ssd_anchors(
reduce_boxes_in_lowest_layer=False) num_layers=6, min_scale=0.2, max_scale=0.95,
aspect_ratios=(1.0, 2.0, 3.0, 1.0/2, 1.0/3),
feature_map_shape_list = [(38, 38), (19, 19), (10, 10), reduce_boxes_in_lowest_layer=False)
(5, 5), (3, 3), (1, 1)]
anchors = anchor_generator.generate( feature_map_shape_list = [(38, 38), (19, 19), (10, 10),
feature_map_shape_list=feature_map_shape_list) (5, 5), (3, 3), (1, 1)]
anchor_corners = anchors.get() anchors = anchor_generator.generate(
with self.test_session(): feature_map_shape_list=feature_map_shape_list)
anchor_corners_out = anchor_corners.eval() return anchors.get()
self.assertEquals(anchor_corners_out.shape, (11640, 4)) anchor_corners_out = self.execute(graph_fn2, [])
self.assertEquals(anchor_corners_out.shape, (11640, 4))
if __name__ == '__main__': if __name__ == '__main__':
......
# 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.
# ==============================================================================
"""Generates grid anchors on the fly corresponding to multiple CNN layers.
Generates grid anchors on the fly corresponding to multiple CNN layers as
described in:
"Focal Loss for Dense Object Detection" (https://arxiv.org/abs/1708.02002)
T.-Y. Lin, P. Goyal, R. Girshick, K. He, P. Dollar
"""
from object_detection.anchor_generators import grid_anchor_generator
from object_detection.core import box_list_ops
class MultiscaleGridAnchorGenerator(object):
"""Generate a grid of anchors for multiple CNN layers of different scale."""
def __init__(self, min_level, max_level, anchor_scale, aspect_ratios,
scales_per_octave):
"""Constructs a MultiscaleGridAnchorGenerator.
To construct anchors, at multiple scale resolutions, one must provide a
the minimum level and maximum levels on a scale pyramid. To define the size
of anchor, the anchor scale is provided to decide the size relatively to the
stride of the corresponding feature map. The generator allows one pixel
location on feature map maps to multiple anchors, that have different aspect
ratios and intermediate scales.
Args:
min_level: minimum level in feature pyramid.
max_level: maximum level in feature pyramid.
anchor_scale: anchor scale and feature stride define the size of the base
anchor on an image. For example, given a feature pyramid with strides
[2^3, ..., 2^7] and anchor scale 4. The base anchor size is
4 * [2^3, ..., 2^7].
aspect_ratios: list or tuple of (float) aspect ratios to place on each
grid point.
scales_per_octave: integer number of intermediate scales per scale octave.
"""
self._anchor_grid_info = []
self._aspect_ratios = aspect_ratios
self._scales_per_octave = scales_per_octave
for level in range(min_level, max_level + 1):
anchor_stride = [2**level, 2**level]
scales = []
aspects = []
for scale in range(scales_per_octave):
scales.append(2**(float(scale) / scales_per_octave))
for aspect_ratio in aspect_ratios:
aspects.append(aspect_ratio)
base_anchor_size = [2**level * anchor_scale, 2**level * anchor_scale]
self._anchor_grid_info.append({
'level': level,
'info': [scales, aspects, base_anchor_size, anchor_stride]
})
def name_scope(self):
return 'MultiscaleGridAnchorGenerator'
def num_anchors_per_location(self):
"""Returns the number of anchors per spatial location.
Returns:
a list of integers, one for each expected feature map to be passed to
the Generate function.
"""
return len(self._anchor_grid_info) * [
len(self._aspect_ratios) * self._scales_per_octave]
def generate(self, feature_map_shape_list, im_height, im_width):
"""Generates a collection of bounding boxes to be used as anchors.
Currently we require the input image shape to be statically defined. That
is, im_height and im_width should be integers rather than tensors.
Args:
feature_map_shape_list: list of pairs of convnet layer resolutions in the
format [(height_0, width_0), (height_1, width_1), ...]. For example,
setting feature_map_shape_list=[(8, 8), (7, 7)] asks for anchors that
correspond to an 8x8 layer followed by a 7x7 layer.
im_height: the height of the image to generate the grid for.
im_width: the width of the image to generate the grid for.
Returns:
boxes: a BoxList holding a collection of N anchor boxes
Raises:
ValueError: if im_height and im_width are not integers.
"""
if not isinstance(im_height, int) or not isinstance(im_width, int):
raise ValueError('MultiscaleGridAnchorGenerator currently requires '
'input image shape to be statically defined.')
anchor_grid_list = []
for feat_shape, grid_info in zip(feature_map_shape_list,
self._anchor_grid_info):
# TODO check the feature_map_shape_list is consistent with
# self._anchor_grid_info
level = grid_info['level']
stride = 2**level
scales, aspect_ratios, base_anchor_size, anchor_stride = grid_info['info']
feat_h = feat_shape[0]
feat_w = feat_shape[1]
anchor_offset = [0, 0]
if im_height % 2.0**level == 0:
anchor_offset[0] = stride / 2.0
if im_width % 2.0**level == 0:
anchor_offset[1] = stride / 2.0
ag = grid_anchor_generator.GridAnchorGenerator(
scales,
aspect_ratios,
base_anchor_size=base_anchor_size,
anchor_stride=anchor_stride,
anchor_offset=anchor_offset)
anchor_grid_list.append(
ag.generate(feature_map_shape_list=[(feat_h, feat_w)]))
concatenated_anchors = box_list_ops.concatenate(anchor_grid_list)
return concatenated_anchors
# 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.
# ==============================================================================
"""Tests for anchor_generators.multiscale_grid_anchor_generator_test.py."""
import numpy as np
import tensorflow as tf
from object_detection.anchor_generators import multiscale_grid_anchor_generator as mg
from object_detection.utils import test_case
class MultiscaleGridAnchorGeneratorTest(test_case.TestCase):
def test_construct_single_anchor(self):
min_level = 5
max_level = 5
anchor_scale = 4.0
aspect_ratios = [1.0]
scales_per_octave = 1
im_height = 64
im_width = 64
feature_map_shape_list = [(2, 2)]
exp_anchor_corners = [[-48, -48, 80, 80],
[-48, -16, 80, 112],
[-16, -48, 112, 80],
[-16, -16, 112, 112]]
anchor_generator = mg.MultiscaleGridAnchorGenerator(
min_level, max_level, anchor_scale, aspect_ratios, scales_per_octave)
anchors = anchor_generator.generate(feature_map_shape_list,
im_height, im_width)
anchor_corners = anchors.get()
with self.test_session():
anchor_corners_out = anchor_corners.eval()
self.assertAllClose(anchor_corners_out, exp_anchor_corners)
def test_num_anchors_per_location(self):
min_level = 5
max_level = 6
anchor_scale = 4.0
aspect_ratios = [1.0, 2.0]
scales_per_octave = 3
anchor_generator = mg.MultiscaleGridAnchorGenerator(
min_level, max_level, anchor_scale, aspect_ratios, scales_per_octave)
self.assertEqual(anchor_generator.num_anchors_per_location(), [6, 6])
def test_construct_single_anchor_fails_with_tensor_image_size(self):
min_level = 5
max_level = 5
anchor_scale = 4.0
aspect_ratios = [1.0]
scales_per_octave = 1
im_height = tf.constant(64)
im_width = tf.constant(64)
feature_map_shape_list = [(2, 2)]
anchor_generator = mg.MultiscaleGridAnchorGenerator(
min_level, max_level, anchor_scale, aspect_ratios, scales_per_octave)
with self.assertRaises(ValueError):
anchor_generator.generate(feature_map_shape_list, im_height, im_width)
def test_construct_single_anchor_with_odd_input_dimension(self):
def graph_fn():
min_level = 5
max_level = 5
anchor_scale = 4.0
aspect_ratios = [1.0]
scales_per_octave = 1
im_height = 65
im_width = 65
feature_map_shape_list = [(3, 3)]
anchor_generator = mg.MultiscaleGridAnchorGenerator(
min_level, max_level, anchor_scale, aspect_ratios, scales_per_octave)
anchors = anchor_generator.generate(feature_map_shape_list, im_height,
im_width)
anchor_corners = anchors.get()
return (anchor_corners,)
anchor_corners_out = self.execute(graph_fn, [])
exp_anchor_corners = [[-64, -64, 64, 64],
[-64, -32, 64, 96],
[-64, 0, 64, 128],
[-32, -64, 96, 64],
[-32, -32, 96, 96],
[-32, 0, 96, 128],
[0, -64, 128, 64],
[0, -32, 128, 96],
[0, 0, 128, 128]]
self.assertAllClose(anchor_corners_out, exp_anchor_corners)
def test_construct_single_anchor_on_two_feature_maps(self):
def graph_fn():
min_level = 5
max_level = 6
anchor_scale = 4.0
aspect_ratios = [1.0]
scales_per_octave = 1
im_height = 64
im_width = 64
feature_map_shape_list = [(2, 2), (1, 1)]
anchor_generator = mg.MultiscaleGridAnchorGenerator(
min_level, max_level, anchor_scale, aspect_ratios, scales_per_octave)
anchors = anchor_generator.generate(feature_map_shape_list, im_height,
im_width)
anchor_corners = anchors.get()
return (anchor_corners,)
anchor_corners_out = self.execute(graph_fn, [])
exp_anchor_corners = [[-48, -48, 80, 80],
[-48, -16, 80, 112],
[-16, -48, 112, 80],
[-16, -16, 112, 112],
[-96, -96, 160, 160]]
self.assertAllClose(anchor_corners_out, exp_anchor_corners)
def test_construct_single_anchor_with_two_scales_per_octave(self):
def graph_fn():
min_level = 6
max_level = 6
anchor_scale = 4.0
aspect_ratios = [1.0]
scales_per_octave = 2
im_height = 64
im_width = 64
feature_map_shape_list = [(1, 1), (1, 1)]
anchor_generator = mg.MultiscaleGridAnchorGenerator(
min_level, max_level, anchor_scale, aspect_ratios, scales_per_octave)
anchors = anchor_generator.generate(feature_map_shape_list, im_height,
im_width)
anchor_corners = anchors.get()
return (anchor_corners,)
# There are 4 set of anchors in this configuration. The order is:
# [[2**0.0 intermediate scale + 1.0 aspect],
# [2**0.5 intermediate scale + 1.0 aspect]]
exp_anchor_corners = [[-96., -96., 160., 160.],
[-149.0193, -149.0193, 213.0193, 213.0193]]
anchor_corners_out = self.execute(graph_fn, [])
self.assertAllClose(anchor_corners_out, exp_anchor_corners)
def test_construct_single_anchor_with_two_scales_per_octave_and_aspect(self):
def graph_fn():
min_level = 6
max_level = 6
anchor_scale = 4.0
aspect_ratios = [1.0, 2.0]
scales_per_octave = 2
im_height = 64
im_width = 64
feature_map_shape_list = [(1, 1), (1, 1), (1, 1), (1, 1)]
anchor_generator = mg.MultiscaleGridAnchorGenerator(
min_level, max_level, anchor_scale, aspect_ratios, scales_per_octave)
anchors = anchor_generator.generate(feature_map_shape_list, im_height,
im_width)
anchor_corners = anchors.get()
return anchor_corners
# There are 4 set of anchors in this configuration. The order is:
# [[2**0.0 intermediate scale + 1.0 aspect],
# [2**0.5 intermediate scale + 1.0 aspect],
# [2**0.0 intermediate scale + 2.0 aspect],
# [2**0.5 intermediate scale + 2.0 aspect]]
exp_anchor_corners = [[-96., -96., 160., 160.],
[-149.0193, -149.0193, 213.0193, 213.0193],
[-58.50967, -149.0193, 122.50967, 213.0193],
[-96., -224., 160., 288.]]
anchor_corners_out = self.execute(graph_fn, [])
self.assertAllClose(anchor_corners_out, exp_anchor_corners)
def test_construct_single_anchors_on_feature_maps_with_dynamic_shape(self):
def graph_fn(feature_map1_height, feature_map1_width, feature_map2_height,
feature_map2_width):
min_level = 5
max_level = 6
anchor_scale = 4.0
aspect_ratios = [1.0]
scales_per_octave = 1
im_height = 64
im_width = 64
feature_map_shape_list = [(feature_map1_height, feature_map1_width),
(feature_map2_height, feature_map2_width)]
anchor_generator = mg.MultiscaleGridAnchorGenerator(
min_level, max_level, anchor_scale, aspect_ratios, scales_per_octave)
anchors = anchor_generator.generate(feature_map_shape_list, im_height,
im_width)
anchor_corners = anchors.get()
return (anchor_corners,)
anchor_corners_out = self.execute_cpu(graph_fn, [
np.array(2, dtype=np.int32),
np.array(2, dtype=np.int32),
np.array(1, dtype=np.int32),
np.array(1, dtype=np.int32)
])
exp_anchor_corners = [[-48, -48, 80, 80],
[-48, -16, 80, 112],
[-16, -48, 112, 80],
[-16, -16, 112, 112],
[-96, -96, 160, 160]]
self.assertAllClose(anchor_corners_out, exp_anchor_corners)
if __name__ == '__main__':
tf.test.main()
...@@ -13,8 +13,8 @@ py_library( ...@@ -13,8 +13,8 @@ py_library(
"faster_rcnn_box_coder.py", "faster_rcnn_box_coder.py",
], ],
deps = [ deps = [
"//tensorflow_models/object_detection/core:box_coder", "//tensorflow/models/research/object_detection/core:box_coder",
"//tensorflow_models/object_detection/core:box_list", "//tensorflow/models/research/object_detection/core:box_list",
], ],
) )
...@@ -26,7 +26,7 @@ py_test( ...@@ -26,7 +26,7 @@ py_test(
deps = [ deps = [
":faster_rcnn_box_coder", ":faster_rcnn_box_coder",
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/core:box_list", "//tensorflow/models/research/object_detection/core:box_list",
], ],
) )
...@@ -36,9 +36,9 @@ py_library( ...@@ -36,9 +36,9 @@ py_library(
"keypoint_box_coder.py", "keypoint_box_coder.py",
], ],
deps = [ deps = [
"//tensorflow_models/object_detection/core:box_coder", "//tensorflow/models/research/object_detection/core:box_coder",
"//tensorflow_models/object_detection/core:box_list", "//tensorflow/models/research/object_detection/core:box_list",
"//tensorflow_models/object_detection/core:standard_fields", "//tensorflow/models/research/object_detection/core:standard_fields",
], ],
) )
...@@ -50,8 +50,8 @@ py_test( ...@@ -50,8 +50,8 @@ py_test(
deps = [ deps = [
":keypoint_box_coder", ":keypoint_box_coder",
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/core:box_list", "//tensorflow/models/research/object_detection/core:box_list",
"//tensorflow_models/object_detection/core:standard_fields", "//tensorflow/models/research/object_detection/core:standard_fields",
], ],
) )
...@@ -61,8 +61,8 @@ py_library( ...@@ -61,8 +61,8 @@ py_library(
"mean_stddev_box_coder.py", "mean_stddev_box_coder.py",
], ],
deps = [ deps = [
"//tensorflow_models/object_detection/core:box_coder", "//tensorflow/models/research/object_detection/core:box_coder",
"//tensorflow_models/object_detection/core:box_list", "//tensorflow/models/research/object_detection/core:box_list",
], ],
) )
...@@ -74,7 +74,7 @@ py_test( ...@@ -74,7 +74,7 @@ py_test(
deps = [ deps = [
":mean_stddev_box_coder", ":mean_stddev_box_coder",
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/core:box_list", "//tensorflow/models/research/object_detection/core:box_list",
], ],
) )
...@@ -84,8 +84,8 @@ py_library( ...@@ -84,8 +84,8 @@ py_library(
"square_box_coder.py", "square_box_coder.py",
], ],
deps = [ deps = [
"//tensorflow_models/object_detection/core:box_coder", "//tensorflow/models/research/object_detection/core:box_coder",
"//tensorflow_models/object_detection/core:box_list", "//tensorflow/models/research/object_detection/core:box_list",
], ],
) )
...@@ -97,6 +97,6 @@ py_test( ...@@ -97,6 +97,6 @@ py_test(
deps = [ deps = [
":square_box_coder", ":square_box_coder",
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/core:box_list", "//tensorflow/models/research/object_detection/core:box_list",
], ],
) )
...@@ -20,18 +20,20 @@ py_library( ...@@ -20,18 +20,20 @@ py_library(
":matcher_builder", ":matcher_builder",
":post_processing_builder", ":post_processing_builder",
":region_similarity_calculator_builder", ":region_similarity_calculator_builder",
"//tensorflow_models/object_detection/core:box_predictor", "//tensorflow/models/research/object_detection/core:box_predictor",
"//tensorflow_models/object_detection/meta_architectures:faster_rcnn_meta_arch", "//tensorflow/models/research/object_detection/meta_architectures:faster_rcnn_meta_arch",
"//tensorflow_models/object_detection/meta_architectures:rfcn_meta_arch", "//tensorflow/models/research/object_detection/meta_architectures:rfcn_meta_arch",
"//tensorflow_models/object_detection/meta_architectures:ssd_meta_arch", "//tensorflow/models/research/object_detection/meta_architectures:ssd_meta_arch",
"//tensorflow_models/object_detection/models:embedded_ssd_mobilenet_v1_feature_extractor", "//tensorflow/models/research/object_detection/models:embedded_ssd_mobilenet_v1_feature_extractor",
"//tensorflow_models/object_detection/models:faster_rcnn_inception_resnet_v2_feature_extractor", "//tensorflow/models/research/object_detection/models:faster_rcnn_inception_resnet_v2_feature_extractor",
"//tensorflow_models/object_detection/models:faster_rcnn_inception_v2_feature_extractor", "//tensorflow/models/research/object_detection/models:faster_rcnn_inception_v2_feature_extractor",
"//tensorflow_models/object_detection/models:faster_rcnn_resnet_v1_feature_extractor", "//tensorflow/models/research/object_detection/models:faster_rcnn_nas_feature_extractor",
"//tensorflow_models/object_detection/models:ssd_inception_v2_feature_extractor", "//tensorflow/models/research/object_detection/models:faster_rcnn_resnet_v1_feature_extractor",
"//tensorflow_models/object_detection/models:ssd_inception_v3_feature_extractor", "//tensorflow/models/research/object_detection/models:ssd_inception_v2_feature_extractor",
"//tensorflow_models/object_detection/models:ssd_mobilenet_v1_feature_extractor", "//tensorflow/models/research/object_detection/models:ssd_inception_v3_feature_extractor",
"//tensorflow_models/object_detection/protos:model_py_pb2", "//tensorflow/models/research/object_detection/models:ssd_mobilenet_v1_feature_extractor",
"//tensorflow/models/research/object_detection/models:ssd_resnet_v1_fpn_feature_extractor",
"//tensorflow/models/research/object_detection/protos:model_py_pb2",
], ],
) )
...@@ -41,15 +43,18 @@ py_test( ...@@ -41,15 +43,18 @@ py_test(
deps = [ deps = [
":model_builder", ":model_builder",
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/meta_architectures:faster_rcnn_meta_arch", "//tensorflow/models/research/object_detection/meta_architectures:faster_rcnn_meta_arch",
"//tensorflow_models/object_detection/meta_architectures:ssd_meta_arch", "//tensorflow/models/research/object_detection/meta_architectures:ssd_meta_arch",
"//tensorflow_models/object_detection/models:faster_rcnn_inception_resnet_v2_feature_extractor", "//tensorflow/models/research/object_detection/models:embedded_ssd_mobilenet_v1_feature_extractor",
"//tensorflow_models/object_detection/models:faster_rcnn_inception_v2_feature_extractor", "//tensorflow/models/research/object_detection/models:faster_rcnn_inception_resnet_v2_feature_extractor",
"//tensorflow_models/object_detection/models:faster_rcnn_resnet_v1_feature_extractor", "//tensorflow/models/research/object_detection/models:faster_rcnn_inception_v2_feature_extractor",
"//tensorflow_models/object_detection/models:ssd_inception_v2_feature_extractor", "//tensorflow/models/research/object_detection/models:faster_rcnn_nas_feature_extractor",
"//tensorflow_models/object_detection/models:ssd_inception_v3_feature_extractor", "//tensorflow/models/research/object_detection/models:faster_rcnn_resnet_v1_feature_extractor",
"//tensorflow_models/object_detection/models:ssd_mobilenet_v1_feature_extractor", "//tensorflow/models/research/object_detection/models:ssd_inception_v2_feature_extractor",
"//tensorflow_models/object_detection/protos:model_py_pb2", "//tensorflow/models/research/object_detection/models:ssd_inception_v3_feature_extractor",
"//tensorflow/models/research/object_detection/models:ssd_mobilenet_v1_feature_extractor",
"//tensorflow/models/research/object_detection/models:ssd_resnet_v1_fpn_feature_extractor",
"//tensorflow/models/research/object_detection/protos:model_py_pb2",
], ],
) )
...@@ -57,9 +62,9 @@ py_library( ...@@ -57,9 +62,9 @@ py_library(
name = "matcher_builder", name = "matcher_builder",
srcs = ["matcher_builder.py"], srcs = ["matcher_builder.py"],
deps = [ deps = [
"//tensorflow_models/object_detection/matchers:argmax_matcher", "//tensorflow/models/research/object_detection/matchers:argmax_matcher",
"//tensorflow_models/object_detection/matchers:bipartite_matcher", "//tensorflow/models/research/object_detection/matchers:bipartite_matcher",
"//tensorflow_models/object_detection/protos:matcher_py_pb2", "//tensorflow/models/research/object_detection/protos:matcher_py_pb2",
], ],
) )
...@@ -68,9 +73,9 @@ py_test( ...@@ -68,9 +73,9 @@ py_test(
srcs = ["matcher_builder_test.py"], srcs = ["matcher_builder_test.py"],
deps = [ deps = [
":matcher_builder", ":matcher_builder",
"//tensorflow_models/object_detection/matchers:argmax_matcher", "//tensorflow/models/research/object_detection/matchers:argmax_matcher",
"//tensorflow_models/object_detection/matchers:bipartite_matcher", "//tensorflow/models/research/object_detection/matchers:bipartite_matcher",
"//tensorflow_models/object_detection/protos:matcher_py_pb2", "//tensorflow/models/research/object_detection/protos:matcher_py_pb2",
], ],
) )
...@@ -78,11 +83,11 @@ py_library( ...@@ -78,11 +83,11 @@ py_library(
name = "box_coder_builder", name = "box_coder_builder",
srcs = ["box_coder_builder.py"], srcs = ["box_coder_builder.py"],
deps = [ deps = [
"//tensorflow_models/object_detection/box_coders:faster_rcnn_box_coder", "//tensorflow/models/research/object_detection/box_coders:faster_rcnn_box_coder",
"//tensorflow_models/object_detection/box_coders:keypoint_box_coder", "//tensorflow/models/research/object_detection/box_coders:keypoint_box_coder",
"//tensorflow_models/object_detection/box_coders:mean_stddev_box_coder", "//tensorflow/models/research/object_detection/box_coders:mean_stddev_box_coder",
"//tensorflow_models/object_detection/box_coders:square_box_coder", "//tensorflow/models/research/object_detection/box_coders:square_box_coder",
"//tensorflow_models/object_detection/protos:box_coder_py_pb2", "//tensorflow/models/research/object_detection/protos:box_coder_py_pb2",
], ],
) )
...@@ -92,11 +97,11 @@ py_test( ...@@ -92,11 +97,11 @@ py_test(
deps = [ deps = [
":box_coder_builder", ":box_coder_builder",
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/box_coders:faster_rcnn_box_coder", "//tensorflow/models/research/object_detection/box_coders:faster_rcnn_box_coder",
"//tensorflow_models/object_detection/box_coders:keypoint_box_coder", "//tensorflow/models/research/object_detection/box_coders:keypoint_box_coder",
"//tensorflow_models/object_detection/box_coders:mean_stddev_box_coder", "//tensorflow/models/research/object_detection/box_coders:mean_stddev_box_coder",
"//tensorflow_models/object_detection/box_coders:square_box_coder", "//tensorflow/models/research/object_detection/box_coders:square_box_coder",
"//tensorflow_models/object_detection/protos:box_coder_py_pb2", "//tensorflow/models/research/object_detection/protos:box_coder_py_pb2",
], ],
) )
...@@ -104,9 +109,10 @@ py_library( ...@@ -104,9 +109,10 @@ py_library(
name = "anchor_generator_builder", name = "anchor_generator_builder",
srcs = ["anchor_generator_builder.py"], srcs = ["anchor_generator_builder.py"],
deps = [ deps = [
"//tensorflow_models/object_detection/anchor_generators:grid_anchor_generator", "//tensorflow/models/research/object_detection/anchor_generators:grid_anchor_generator",
"//tensorflow_models/object_detection/anchor_generators:multiple_grid_anchor_generator", "//tensorflow/models/research/object_detection/anchor_generators:multiple_grid_anchor_generator",
"//tensorflow_models/object_detection/protos:anchor_generator_py_pb2", "//tensorflow/models/research/object_detection/anchor_generators:multiscale_grid_anchor_generator",
"//tensorflow/models/research/object_detection/protos:anchor_generator_py_pb2",
], ],
) )
...@@ -116,9 +122,35 @@ py_test( ...@@ -116,9 +122,35 @@ py_test(
deps = [ deps = [
":anchor_generator_builder", ":anchor_generator_builder",
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/anchor_generators:grid_anchor_generator", "//tensorflow/models/research/object_detection/anchor_generators:grid_anchor_generator",
"//tensorflow_models/object_detection/anchor_generators:multiple_grid_anchor_generator", "//tensorflow/models/research/object_detection/anchor_generators:multiple_grid_anchor_generator",
"//tensorflow_models/object_detection/protos:anchor_generator_py_pb2", "//tensorflow/models/research/object_detection/anchor_generators:multiscale_grid_anchor_generator",
"//tensorflow/models/research/object_detection/protos:anchor_generator_py_pb2",
],
)
py_library(
name = "dataset_builder",
srcs = ["dataset_builder.py"],
deps = [
"//tensorflow",
"//tensorflow/models/research/object_detection/data_decoders:tf_example_decoder",
"//tensorflow/models/research/object_detection/protos:input_reader_py_pb2",
"//tensorflow/models/research/object_detection/utils:dataset_util",
],
)
py_test(
name = "dataset_builder_test",
srcs = [
"dataset_builder_test.py",
],
deps = [
":dataset_builder",
"//tensorflow",
"//tensorflow/models/research/object_detection/core:standard_fields",
"//tensorflow/models/research/object_detection/protos:input_reader_py_pb2",
"//tensorflow/models/research/object_detection/utils:dataset_util",
], ],
) )
...@@ -127,8 +159,8 @@ py_library( ...@@ -127,8 +159,8 @@ py_library(
srcs = ["input_reader_builder.py"], srcs = ["input_reader_builder.py"],
deps = [ deps = [
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/data_decoders:tf_example_decoder", "//tensorflow/models/research/object_detection/data_decoders:tf_example_decoder",
"//tensorflow_models/object_detection/protos:input_reader_py_pb2", "//tensorflow/models/research/object_detection/protos:input_reader_py_pb2",
], ],
) )
...@@ -140,8 +172,8 @@ py_test( ...@@ -140,8 +172,8 @@ py_test(
deps = [ deps = [
":input_reader_builder", ":input_reader_builder",
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/core:standard_fields", "//tensorflow/models/research/object_detection/core:standard_fields",
"//tensorflow_models/object_detection/protos:input_reader_py_pb2", "//tensorflow/models/research/object_detection/protos:input_reader_py_pb2",
], ],
) )
...@@ -149,8 +181,8 @@ py_library( ...@@ -149,8 +181,8 @@ py_library(
name = "losses_builder", name = "losses_builder",
srcs = ["losses_builder.py"], srcs = ["losses_builder.py"],
deps = [ deps = [
"//tensorflow_models/object_detection/core:losses", "//tensorflow/models/research/object_detection/core:losses",
"//tensorflow_models/object_detection/protos:losses_py_pb2", "//tensorflow/models/research/object_detection/protos:losses_py_pb2",
], ],
) )
...@@ -159,8 +191,8 @@ py_test( ...@@ -159,8 +191,8 @@ py_test(
srcs = ["losses_builder_test.py"], srcs = ["losses_builder_test.py"],
deps = [ deps = [
":losses_builder", ":losses_builder",
"//tensorflow_models/object_detection/core:losses", "//tensorflow/models/research/object_detection/core:losses",
"//tensorflow_models/object_detection/protos:losses_py_pb2", "//tensorflow/models/research/object_detection/protos:losses_py_pb2",
], ],
) )
...@@ -169,7 +201,7 @@ py_library( ...@@ -169,7 +201,7 @@ py_library(
srcs = ["optimizer_builder.py"], srcs = ["optimizer_builder.py"],
deps = [ deps = [
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/utils:learning_schedules", "//tensorflow/models/research/object_detection/utils:learning_schedules",
], ],
) )
...@@ -179,7 +211,7 @@ py_test( ...@@ -179,7 +211,7 @@ py_test(
deps = [ deps = [
":optimizer_builder", ":optimizer_builder",
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/protos:optimizer_py_pb2", "//tensorflow/models/research/object_detection/protos:optimizer_py_pb2",
], ],
) )
...@@ -188,8 +220,8 @@ py_library( ...@@ -188,8 +220,8 @@ py_library(
srcs = ["post_processing_builder.py"], srcs = ["post_processing_builder.py"],
deps = [ deps = [
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/core:post_processing", "//tensorflow/models/research/object_detection/core:post_processing",
"//tensorflow_models/object_detection/protos:post_processing_py_pb2", "//tensorflow/models/research/object_detection/protos:post_processing_py_pb2",
], ],
) )
...@@ -199,7 +231,7 @@ py_test( ...@@ -199,7 +231,7 @@ py_test(
deps = [ deps = [
":post_processing_builder", ":post_processing_builder",
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/protos:post_processing_py_pb2", "//tensorflow/models/research/object_detection/protos:post_processing_py_pb2",
], ],
) )
...@@ -207,7 +239,7 @@ py_library( ...@@ -207,7 +239,7 @@ py_library(
name = "hyperparams_builder", name = "hyperparams_builder",
srcs = ["hyperparams_builder.py"], srcs = ["hyperparams_builder.py"],
deps = [ deps = [
"//tensorflow_models/object_detection/protos:hyperparams_py_pb2", "//tensorflow/models/research/object_detection/protos:hyperparams_py_pb2",
], ],
) )
...@@ -217,7 +249,7 @@ py_test( ...@@ -217,7 +249,7 @@ py_test(
deps = [ deps = [
":hyperparams_builder", ":hyperparams_builder",
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/protos:hyperparams_py_pb2", "//tensorflow/models/research/object_detection/protos:hyperparams_py_pb2",
], ],
) )
...@@ -226,8 +258,8 @@ py_library( ...@@ -226,8 +258,8 @@ py_library(
srcs = ["box_predictor_builder.py"], srcs = ["box_predictor_builder.py"],
deps = [ deps = [
":hyperparams_builder", ":hyperparams_builder",
"//tensorflow_models/object_detection/core:box_predictor", "//tensorflow/models/research/object_detection/core:box_predictor",
"//tensorflow_models/object_detection/protos:box_predictor_py_pb2", "//tensorflow/models/research/object_detection/protos:box_predictor_py_pb2",
], ],
) )
...@@ -238,8 +270,8 @@ py_test( ...@@ -238,8 +270,8 @@ py_test(
":box_predictor_builder", ":box_predictor_builder",
":hyperparams_builder", ":hyperparams_builder",
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/protos:box_predictor_py_pb2", "//tensorflow/models/research/object_detection/protos:box_predictor_py_pb2",
"//tensorflow_models/object_detection/protos:hyperparams_py_pb2", "//tensorflow/models/research/object_detection/protos:hyperparams_py_pb2",
], ],
) )
...@@ -247,8 +279,8 @@ py_library( ...@@ -247,8 +279,8 @@ py_library(
name = "region_similarity_calculator_builder", name = "region_similarity_calculator_builder",
srcs = ["region_similarity_calculator_builder.py"], srcs = ["region_similarity_calculator_builder.py"],
deps = [ deps = [
"//tensorflow_models/object_detection/core:region_similarity_calculator", "//tensorflow/models/research/object_detection/core:region_similarity_calculator",
"//tensorflow_models/object_detection/protos:region_similarity_calculator_py_pb2", "//tensorflow/models/research/object_detection/protos:region_similarity_calculator_py_pb2",
], ],
) )
...@@ -266,8 +298,8 @@ py_library( ...@@ -266,8 +298,8 @@ py_library(
srcs = ["preprocessor_builder.py"], srcs = ["preprocessor_builder.py"],
deps = [ deps = [
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/core:preprocessor", "//tensorflow/models/research/object_detection/core:preprocessor",
"//tensorflow_models/object_detection/protos:preprocessor_py_pb2", "//tensorflow/models/research/object_detection/protos:preprocessor_py_pb2",
], ],
) )
...@@ -279,8 +311,8 @@ py_test( ...@@ -279,8 +311,8 @@ py_test(
deps = [ deps = [
":preprocessor_builder", ":preprocessor_builder",
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/core:preprocessor", "//tensorflow/models/research/object_detection/core:preprocessor",
"//tensorflow_models/object_detection/protos:preprocessor_py_pb2", "//tensorflow/models/research/object_detection/protos:preprocessor_py_pb2",
], ],
) )
...@@ -289,8 +321,8 @@ py_library( ...@@ -289,8 +321,8 @@ py_library(
srcs = ["image_resizer_builder.py"], srcs = ["image_resizer_builder.py"],
deps = [ deps = [
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/core:preprocessor", "//tensorflow/models/research/object_detection/core:preprocessor",
"//tensorflow_models/object_detection/protos:image_resizer_py_pb2", "//tensorflow/models/research/object_detection/protos:image_resizer_py_pb2",
], ],
) )
...@@ -300,6 +332,6 @@ py_test( ...@@ -300,6 +332,6 @@ py_test(
deps = [ deps = [
":image_resizer_builder", ":image_resizer_builder",
"//tensorflow", "//tensorflow",
"//tensorflow_models/object_detection/protos:image_resizer_py_pb2", "//tensorflow/models/research/object_detection/protos:image_resizer_py_pb2",
], ],
) )
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
from object_detection.anchor_generators import grid_anchor_generator from object_detection.anchor_generators import grid_anchor_generator
from object_detection.anchor_generators import multiple_grid_anchor_generator from object_detection.anchor_generators import multiple_grid_anchor_generator
from object_detection.anchor_generators import multiscale_grid_anchor_generator
from object_detection.protos import anchor_generator_pb2 from object_detection.protos import anchor_generator_pb2
...@@ -78,5 +79,15 @@ def build(anchor_generator_config): ...@@ -78,5 +79,15 @@ def build(anchor_generator_config):
anchor_offsets=anchor_offsets, anchor_offsets=anchor_offsets,
reduce_boxes_in_lowest_layer=( reduce_boxes_in_lowest_layer=(
ssd_anchor_generator_config.reduce_boxes_in_lowest_layer)) ssd_anchor_generator_config.reduce_boxes_in_lowest_layer))
elif anchor_generator_config.WhichOneof(
'anchor_generator_oneof') == 'multiscale_anchor_generator':
cfg = anchor_generator_config.multiscale_anchor_generator
return multiscale_grid_anchor_generator.MultiscaleGridAnchorGenerator(
cfg.min_level,
cfg.max_level,
cfg.anchor_scale,
[float(aspect_ratio) for aspect_ratio in cfg.aspect_ratios],
cfg.scales_per_octave
)
else: else:
raise ValueError('Empty anchor generator.') raise ValueError('Empty anchor generator.')
...@@ -22,6 +22,7 @@ import tensorflow as tf ...@@ -22,6 +22,7 @@ import tensorflow as tf
from google.protobuf import text_format from google.protobuf import text_format
from object_detection.anchor_generators import grid_anchor_generator from object_detection.anchor_generators import grid_anchor_generator
from object_detection.anchor_generators import multiple_grid_anchor_generator from object_detection.anchor_generators import multiple_grid_anchor_generator
from object_detection.anchor_generators import multiscale_grid_anchor_generator
from object_detection.builders import anchor_generator_builder from object_detection.builders import anchor_generator_builder
from object_detection.protos import anchor_generator_pb2 from object_detection.protos import anchor_generator_pb2
...@@ -252,6 +253,31 @@ class AnchorGeneratorBuilderTest(tf.test.TestCase): ...@@ -252,6 +253,31 @@ class AnchorGeneratorBuilderTest(tf.test.TestCase):
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
anchor_generator_builder.build(anchor_generator_proto) anchor_generator_builder.build(anchor_generator_proto)
def test_build_multiscale_anchor_generator_custom_aspect_ratios(self):
anchor_generator_text_proto = """
multiscale_anchor_generator {
aspect_ratios: [1.0]
}
"""
anchor_generator_proto = anchor_generator_pb2.AnchorGenerator()
text_format.Merge(anchor_generator_text_proto, anchor_generator_proto)
anchor_generator_object = anchor_generator_builder.build(
anchor_generator_proto)
self.assertTrue(isinstance(anchor_generator_object,
multiscale_grid_anchor_generator.
MultiscaleGridAnchorGenerator))
print anchor_generator_object._anchor_grid_info
for level, anchor_grid_info in zip(
range(3, 8), anchor_generator_object._anchor_grid_info):
self.assertEqual(set(anchor_grid_info.keys()), set(['level', 'info']))
self.assertTrue(level, anchor_grid_info['level'])
self.assertEqual(len(anchor_grid_info['info']), 4)
self.assertAllClose(anchor_grid_info['info'][0], [2**0, 2**0.5])
self.assertTrue(anchor_grid_info['info'][1], 1.0)
self.assertAllClose(anchor_grid_info['info'][2],
[4.0 * 2**level, 4.0 * 2**level])
self.assertAllClose(anchor_grid_info['info'][3], [2**level, 2**level])
if __name__ == '__main__': if __name__ == '__main__':
tf.test.main() tf.test.main()
...@@ -64,6 +64,26 @@ def build(argscope_fn, box_predictor_config, is_training, num_classes): ...@@ -64,6 +64,26 @@ def build(argscope_fn, box_predictor_config, is_training, num_classes):
kernel_size=conv_box_predictor.kernel_size, kernel_size=conv_box_predictor.kernel_size,
box_code_size=conv_box_predictor.box_code_size, box_code_size=conv_box_predictor.box_code_size,
apply_sigmoid_to_scores=conv_box_predictor.apply_sigmoid_to_scores, apply_sigmoid_to_scores=conv_box_predictor.apply_sigmoid_to_scores,
class_prediction_bias_init=(conv_box_predictor.
class_prediction_bias_init),
use_depthwise=conv_box_predictor.use_depthwise
)
return box_predictor_object
if box_predictor_oneof == 'weight_shared_convolutional_box_predictor':
conv_box_predictor = (box_predictor_config.
weight_shared_convolutional_box_predictor)
conv_hyperparams = argscope_fn(conv_box_predictor.conv_hyperparams,
is_training)
box_predictor_object = box_predictor.WeightSharedConvolutionalBoxPredictor(
is_training=is_training,
num_classes=num_classes,
conv_hyperparams=conv_hyperparams,
depth=conv_box_predictor.depth,
num_layers_before_predictor=(conv_box_predictor.
num_layers_before_predictor),
kernel_size=conv_box_predictor.kernel_size,
box_code_size=conv_box_predictor.box_code_size,
class_prediction_bias_init=conv_box_predictor.class_prediction_bias_init class_prediction_bias_init=conv_box_predictor.class_prediction_bias_init
) )
return box_predictor_object return box_predictor_object
...@@ -85,8 +105,12 @@ def build(argscope_fn, box_predictor_config, is_training, num_classes): ...@@ -85,8 +105,12 @@ def build(argscope_fn, box_predictor_config, is_training, num_classes):
box_code_size=mask_rcnn_box_predictor.box_code_size, box_code_size=mask_rcnn_box_predictor.box_code_size,
conv_hyperparams=conv_hyperparams, conv_hyperparams=conv_hyperparams,
predict_instance_masks=mask_rcnn_box_predictor.predict_instance_masks, predict_instance_masks=mask_rcnn_box_predictor.predict_instance_masks,
mask_prediction_conv_depth=(mask_rcnn_box_predictor. mask_height=mask_rcnn_box_predictor.mask_height,
mask_prediction_conv_depth), mask_width=mask_rcnn_box_predictor.mask_width,
mask_prediction_num_conv_layers=(
mask_rcnn_box_predictor.mask_prediction_num_conv_layers),
mask_prediction_conv_depth=(
mask_rcnn_box_predictor.mask_prediction_conv_depth),
predict_keypoints=mask_rcnn_box_predictor.predict_keypoints) predict_keypoints=mask_rcnn_box_predictor.predict_keypoints)
return box_predictor_object return box_predictor_object
......
...@@ -83,6 +83,7 @@ class ConvolutionalBoxPredictorBuilderTest(tf.test.TestCase): ...@@ -83,6 +83,7 @@ class ConvolutionalBoxPredictorBuilderTest(tf.test.TestCase):
box_code_size: 3 box_code_size: 3
apply_sigmoid_to_scores: true apply_sigmoid_to_scores: true
class_prediction_bias_init: 4.0 class_prediction_bias_init: 4.0
use_depthwise: true
} }
""" """
conv_hyperparams_text_proto = """ conv_hyperparams_text_proto = """
...@@ -118,6 +119,7 @@ class ConvolutionalBoxPredictorBuilderTest(tf.test.TestCase): ...@@ -118,6 +119,7 @@ class ConvolutionalBoxPredictorBuilderTest(tf.test.TestCase):
self.assertAlmostEqual(box_predictor._class_prediction_bias_init, 4.0) self.assertAlmostEqual(box_predictor._class_prediction_bias_init, 4.0)
self.assertEqual(box_predictor.num_classes, 10) self.assertEqual(box_predictor.num_classes, 10)
self.assertFalse(box_predictor._is_training) self.assertFalse(box_predictor._is_training)
self.assertTrue(box_predictor._use_depthwise)
def test_construct_default_conv_box_predictor(self): def test_construct_default_conv_box_predictor(self):
box_predictor_text_proto = """ box_predictor_text_proto = """
...@@ -148,6 +150,121 @@ class ConvolutionalBoxPredictorBuilderTest(tf.test.TestCase): ...@@ -148,6 +150,121 @@ class ConvolutionalBoxPredictorBuilderTest(tf.test.TestCase):
self.assertFalse(box_predictor._apply_sigmoid_to_scores) self.assertFalse(box_predictor._apply_sigmoid_to_scores)
self.assertEqual(box_predictor.num_classes, 90) self.assertEqual(box_predictor.num_classes, 90)
self.assertTrue(box_predictor._is_training) self.assertTrue(box_predictor._is_training)
self.assertFalse(box_predictor._use_depthwise)
class WeightSharedConvolutionalBoxPredictorBuilderTest(tf.test.TestCase):
def test_box_predictor_calls_conv_argscope_fn(self):
conv_hyperparams_text_proto = """
regularizer {
l1_regularizer {
weight: 0.0003
}
}
initializer {
truncated_normal_initializer {
mean: 0.0
stddev: 0.3
}
}
activation: RELU_6
"""
hyperparams_proto = hyperparams_pb2.Hyperparams()
text_format.Merge(conv_hyperparams_text_proto, hyperparams_proto)
def mock_conv_argscope_builder(conv_hyperparams_arg, is_training):
return (conv_hyperparams_arg, is_training)
box_predictor_proto = box_predictor_pb2.BoxPredictor()
(box_predictor_proto.weight_shared_convolutional_box_predictor
.conv_hyperparams.CopyFrom(hyperparams_proto))
box_predictor = box_predictor_builder.build(
argscope_fn=mock_conv_argscope_builder,
box_predictor_config=box_predictor_proto,
is_training=False,
num_classes=10)
(conv_hyperparams_actual, is_training) = box_predictor._conv_hyperparams
self.assertAlmostEqual((hyperparams_proto.regularizer.
l1_regularizer.weight),
(conv_hyperparams_actual.regularizer.l1_regularizer.
weight))
self.assertAlmostEqual((hyperparams_proto.initializer.
truncated_normal_initializer.stddev),
(conv_hyperparams_actual.initializer.
truncated_normal_initializer.stddev))
self.assertAlmostEqual((hyperparams_proto.initializer.
truncated_normal_initializer.mean),
(conv_hyperparams_actual.initializer.
truncated_normal_initializer.mean))
self.assertEqual(hyperparams_proto.activation,
conv_hyperparams_actual.activation)
self.assertFalse(is_training)
def test_construct_non_default_conv_box_predictor(self):
box_predictor_text_proto = """
weight_shared_convolutional_box_predictor {
depth: 2
num_layers_before_predictor: 2
kernel_size: 7
box_code_size: 3
class_prediction_bias_init: 4.0
}
"""
conv_hyperparams_text_proto = """
regularizer {
l1_regularizer {
}
}
initializer {
truncated_normal_initializer {
}
}
"""
hyperparams_proto = hyperparams_pb2.Hyperparams()
text_format.Merge(conv_hyperparams_text_proto, hyperparams_proto)
def mock_conv_argscope_builder(conv_hyperparams_arg, is_training):
return (conv_hyperparams_arg, is_training)
box_predictor_proto = box_predictor_pb2.BoxPredictor()
text_format.Merge(box_predictor_text_proto, box_predictor_proto)
(box_predictor_proto.weight_shared_convolutional_box_predictor.
conv_hyperparams.CopyFrom(hyperparams_proto))
box_predictor = box_predictor_builder.build(
argscope_fn=mock_conv_argscope_builder,
box_predictor_config=box_predictor_proto,
is_training=False,
num_classes=10)
self.assertEqual(box_predictor._depth, 2)
self.assertEqual(box_predictor._num_layers_before_predictor, 2)
self.assertAlmostEqual(box_predictor._class_prediction_bias_init, 4.0)
self.assertEqual(box_predictor.num_classes, 10)
self.assertFalse(box_predictor._is_training)
def test_construct_default_conv_box_predictor(self):
box_predictor_text_proto = """
weight_shared_convolutional_box_predictor {
conv_hyperparams {
regularizer {
l1_regularizer {
}
}
initializer {
truncated_normal_initializer {
}
}
}
}"""
box_predictor_proto = box_predictor_pb2.BoxPredictor()
text_format.Merge(box_predictor_text_proto, box_predictor_proto)
box_predictor = box_predictor_builder.build(
argscope_fn=hyperparams_builder.build,
box_predictor_config=box_predictor_proto,
is_training=True,
num_classes=90)
self.assertEqual(box_predictor._depth, 0)
self.assertEqual(box_predictor._num_layers_before_predictor, 0)
self.assertEqual(box_predictor.num_classes, 90)
self.assertTrue(box_predictor._is_training)
class MaskRCNNBoxPredictorBuilderTest(tf.test.TestCase): class MaskRCNNBoxPredictorBuilderTest(tf.test.TestCase):
...@@ -247,6 +364,8 @@ class MaskRCNNBoxPredictorBuilderTest(tf.test.TestCase): ...@@ -247,6 +364,8 @@ class MaskRCNNBoxPredictorBuilderTest(tf.test.TestCase):
hyperparams_pb2.Hyperparams.CONV) hyperparams_pb2.Hyperparams.CONV)
box_predictor_proto.mask_rcnn_box_predictor.predict_instance_masks = True box_predictor_proto.mask_rcnn_box_predictor.predict_instance_masks = True
box_predictor_proto.mask_rcnn_box_predictor.mask_prediction_conv_depth = 512 box_predictor_proto.mask_rcnn_box_predictor.mask_prediction_conv_depth = 512
box_predictor_proto.mask_rcnn_box_predictor.mask_height = 16
box_predictor_proto.mask_rcnn_box_predictor.mask_width = 16
mock_argscope_fn = mock.Mock(return_value='arg_scope') mock_argscope_fn = mock.Mock(return_value='arg_scope')
box_predictor = box_predictor_builder.build( box_predictor = box_predictor_builder.build(
argscope_fn=mock_argscope_fn, argscope_fn=mock_argscope_fn,
......
# 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.
# ==============================================================================
"""tf.data.Dataset builder.
Creates data sources for DetectionModels from an InputReader config. See
input_reader.proto for options.
Note: If users wishes to also use their own InputReaders with the Object
Detection configuration framework, they should define their own builder function
that wraps the build function.
"""
import tensorflow as tf
from object_detection.core import standard_fields as fields
from object_detection.data_decoders import tf_example_decoder
from object_detection.protos import input_reader_pb2
from object_detection.utils import dataset_util
def _get_padding_shapes(dataset, max_num_boxes, num_classes,
spatial_image_shape):
"""Returns shapes to pad dataset tensors to before batching.
Args:
dataset: tf.data.Dataset object.
max_num_boxes: Max number of groundtruth boxes needed to computes shapes for
padding.
num_classes: Number of classes in the dataset needed to compute shapes for
padding.
spatial_image_shape: A list of two integers of the form [height, width]
containing expected spatial shape of the imaage.
Returns:
A dictionary keyed by fields.InputDataFields containing padding shapes for
tensors in the dataset.
"""
height, width = spatial_image_shape
padding_shapes = {
fields.InputDataFields.image: [height, width, 3],
fields.InputDataFields.source_id: [],
fields.InputDataFields.filename: [],
fields.InputDataFields.key: [],
fields.InputDataFields.groundtruth_difficult: [max_num_boxes],
fields.InputDataFields.groundtruth_boxes: [max_num_boxes, 4],
fields.InputDataFields.groundtruth_classes: [
max_num_boxes, num_classes
],
fields.InputDataFields.groundtruth_instance_masks: [max_num_boxes, height,
width],
fields.InputDataFields.groundtruth_is_crowd: [max_num_boxes],
fields.InputDataFields.groundtruth_group_of: [max_num_boxes],
fields.InputDataFields.groundtruth_area: [max_num_boxes],
fields.InputDataFields.groundtruth_weights: [max_num_boxes],
fields.InputDataFields.num_groundtruth_boxes: [],
fields.InputDataFields.groundtruth_label_types: [max_num_boxes],
fields.InputDataFields.groundtruth_label_scores: [max_num_boxes],
fields.InputDataFields.true_image_shape: [3]
}
if fields.InputDataFields.groundtruth_keypoints in dataset.output_shapes:
tensor_shape = dataset.output_shapes[fields.InputDataFields.
groundtruth_keypoints]
padding_shape = [max_num_boxes, tensor_shape[1].value,
tensor_shape[2].value]
padding_shapes[fields.InputDataFields.groundtruth_keypoints] = padding_shape
if (fields.InputDataFields.groundtruth_keypoint_visibilities
in dataset.output_shapes):
tensor_shape = dataset.output_shapes[fields.InputDataFields.
groundtruth_keypoint_visibilities]
padding_shape = [max_num_boxes, tensor_shape[1].value]
padding_shapes[fields.InputDataFields.
groundtruth_keypoint_visibilities] = padding_shape
return {tensor_key: padding_shapes[tensor_key]
for tensor_key, _ in dataset.output_shapes.items()}
def build(input_reader_config, transform_input_data_fn=None, num_workers=1,
worker_index=0, batch_size=1, max_num_boxes=None, num_classes=None,
spatial_image_shape=None):
"""Builds a tf.data.Dataset.
Builds a tf.data.Dataset by applying the `transform_input_data_fn` on all
records. Optionally, if `batch_size` > 1 and `max_num_boxes`, `num_classes`
and `spatial_image_shape` are not None, returns a padded batched
tf.data.Dataset.
Args:
input_reader_config: A input_reader_pb2.InputReader object.
transform_input_data_fn: Function to apply to all records, or None if
no extra decoding is required.
num_workers: Number of workers (tpu shard).
worker_index: Id for the current worker (tpu shard).
batch_size: Batch size. If not None, returns a padded batch dataset.
max_num_boxes: Max number of groundtruth boxes needed to computes shapes for
padding. This is only used if batch_size is greater than 1.
num_classes: Number of classes in the dataset needed to compute shapes for
padding. This is only used if batch_size is greater than 1.
spatial_image_shape: a list of two integers of the form [height, width]
containing expected spatial shape of the image after applying
transform_input_data_fn. This is needed to compute shapes for padding and
only used if batch_size is greater than 1.
Returns:
A tf.data.Dataset based on the input_reader_config.
Raises:
ValueError: On invalid input reader proto.
ValueError: If no input paths are specified.
ValueError: If batch_size > 1 and any of (max_num_boxes, num_classes,
spatial_image_shape) is None.
"""
if not isinstance(input_reader_config, input_reader_pb2.InputReader):
raise ValueError('input_reader_config not of type '
'input_reader_pb2.InputReader.')
if input_reader_config.WhichOneof('input_reader') == 'tf_record_input_reader':
config = input_reader_config.tf_record_input_reader
if not config.input_path:
raise ValueError('At least one input path must be specified in '
'`input_reader_config`.')
label_map_proto_file = None
if input_reader_config.HasField('label_map_path'):
label_map_proto_file = input_reader_config.label_map_path
decoder = tf_example_decoder.TfExampleDecoder(
load_instance_masks=input_reader_config.load_instance_masks,
instance_mask_type=input_reader_config.mask_type,
label_map_proto_file=label_map_proto_file)
def process_fn(value):
processed = decoder.decode(value)
if transform_input_data_fn is not None:
return transform_input_data_fn(processed)
return processed
dataset = dataset_util.read_dataset(
tf.data.TFRecordDataset, process_fn, config.input_path[:],
input_reader_config, num_workers, worker_index)
if batch_size > 1:
if num_classes is None:
raise ValueError('`num_classes` must be set when batch_size > 1.')
if max_num_boxes is None:
raise ValueError('`max_num_boxes` must be set when batch_size > 1.')
if spatial_image_shape is None:
raise ValueError('`spatial_image_shape` must be set when batch_size > '
'1 .')
padding_shapes = _get_padding_shapes(dataset, max_num_boxes, num_classes,
spatial_image_shape)
dataset = dataset.apply(
tf.contrib.data.padded_batch_and_drop_remainder(batch_size,
padding_shapes))
return dataset
raise ValueError('Unsupported input_reader_config.')
# 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.
# ==============================================================================
"""Tests for dataset_builder."""
import os
import numpy as np
import tensorflow as tf
from google.protobuf import text_format
from tensorflow.core.example import example_pb2
from tensorflow.core.example import feature_pb2
from object_detection.builders import dataset_builder
from object_detection.core import standard_fields as fields
from object_detection.protos import input_reader_pb2
from object_detection.utils import dataset_util
class DatasetBuilderTest(tf.test.TestCase):
def create_tf_record(self):
path = os.path.join(self.get_temp_dir(), 'tfrecord')
writer = tf.python_io.TFRecordWriter(path)
image_tensor = np.random.randint(255, size=(4, 5, 3)).astype(np.uint8)
flat_mask = (4 * 5) * [1.0]
with self.test_session():
encoded_jpeg = tf.image.encode_jpeg(tf.constant(image_tensor)).eval()
example = example_pb2.Example(
features=feature_pb2.Features(
feature={
'image/encoded':
feature_pb2.Feature(
bytes_list=feature_pb2.BytesList(value=[encoded_jpeg])),
'image/format':
feature_pb2.Feature(
bytes_list=feature_pb2.BytesList(
value=['jpeg'.encode('utf-8')])),
'image/height':
feature_pb2.Feature(
int64_list=feature_pb2.Int64List(value=[4])),
'image/width':
feature_pb2.Feature(
int64_list=feature_pb2.Int64List(value=[5])),
'image/object/bbox/xmin':
feature_pb2.Feature(
float_list=feature_pb2.FloatList(value=[0.0])),
'image/object/bbox/xmax':
feature_pb2.Feature(
float_list=feature_pb2.FloatList(value=[1.0])),
'image/object/bbox/ymin':
feature_pb2.Feature(
float_list=feature_pb2.FloatList(value=[0.0])),
'image/object/bbox/ymax':
feature_pb2.Feature(
float_list=feature_pb2.FloatList(value=[1.0])),
'image/object/class/label':
feature_pb2.Feature(
int64_list=feature_pb2.Int64List(value=[2])),
'image/object/mask':
feature_pb2.Feature(
float_list=feature_pb2.FloatList(value=flat_mask)),
}))
writer.write(example.SerializeToString())
writer.close()
return path
def test_build_tf_record_input_reader(self):
tf_record_path = self.create_tf_record()
input_reader_text_proto = """
shuffle: false
num_readers: 1
tf_record_input_reader {{
input_path: '{0}'
}}
""".format(tf_record_path)
input_reader_proto = input_reader_pb2.InputReader()
text_format.Merge(input_reader_text_proto, input_reader_proto)
tensor_dict = dataset_util.make_initializable_iterator(
dataset_builder.build(input_reader_proto)).get_next()
sv = tf.train.Supervisor(logdir=self.get_temp_dir())
with sv.prepare_or_wait_for_session() as sess:
sv.start_queue_runners(sess)
output_dict = sess.run(tensor_dict)
self.assertTrue(
fields.InputDataFields.groundtruth_instance_masks not in output_dict)
self.assertEquals((4, 5, 3),
output_dict[fields.InputDataFields.image].shape)
self.assertEquals([2],
output_dict[fields.InputDataFields.groundtruth_classes])
self.assertEquals(
(1, 4), output_dict[fields.InputDataFields.groundtruth_boxes].shape)
self.assertAllEqual(
[0.0, 0.0, 1.0, 1.0],
output_dict[fields.InputDataFields.groundtruth_boxes][0])
def test_build_tf_record_input_reader_and_load_instance_masks(self):
tf_record_path = self.create_tf_record()
input_reader_text_proto = """
shuffle: false
num_readers: 1
load_instance_masks: true
tf_record_input_reader {{
input_path: '{0}'
}}
""".format(tf_record_path)
input_reader_proto = input_reader_pb2.InputReader()
text_format.Merge(input_reader_text_proto, input_reader_proto)
tensor_dict = dataset_util.make_initializable_iterator(
dataset_builder.build(input_reader_proto)).get_next()
sv = tf.train.Supervisor(logdir=self.get_temp_dir())
with sv.prepare_or_wait_for_session() as sess:
sv.start_queue_runners(sess)
output_dict = sess.run(tensor_dict)
self.assertAllEqual(
(1, 4, 5),
output_dict[fields.InputDataFields.groundtruth_instance_masks].shape)
def test_build_tf_record_input_reader_with_batch_size_two(self):
tf_record_path = self.create_tf_record()
input_reader_text_proto = """
shuffle: false
num_readers: 1
tf_record_input_reader {{
input_path: '{0}'
}}
""".format(tf_record_path)
input_reader_proto = input_reader_pb2.InputReader()
text_format.Merge(input_reader_text_proto, input_reader_proto)
def one_hot_class_encoding_fn(tensor_dict):
tensor_dict[fields.InputDataFields.groundtruth_classes] = tf.one_hot(
tensor_dict[fields.InputDataFields.groundtruth_classes] - 1, depth=3)
return tensor_dict
tensor_dict = dataset_util.make_initializable_iterator(
dataset_builder.build(
input_reader_proto,
transform_input_data_fn=one_hot_class_encoding_fn,
batch_size=2,
max_num_boxes=2,
num_classes=3,
spatial_image_shape=[4, 5])).get_next()
sv = tf.train.Supervisor(logdir=self.get_temp_dir())
with sv.prepare_or_wait_for_session() as sess:
sv.start_queue_runners(sess)
output_dict = sess.run(tensor_dict)
self.assertAllEqual([2, 4, 5, 3],
output_dict[fields.InputDataFields.image].shape)
self.assertAllEqual([2, 2, 3],
output_dict[fields.InputDataFields.groundtruth_classes].
shape)
self.assertAllEqual([2, 2, 4],
output_dict[fields.InputDataFields.groundtruth_boxes].
shape)
self.assertAllEqual(
[[[0.0, 0.0, 1.0, 1.0],
[0.0, 0.0, 0.0, 0.0]],
[[0.0, 0.0, 1.0, 1.0],
[0.0, 0.0, 0.0, 0.0]]],
output_dict[fields.InputDataFields.groundtruth_boxes])
def test_build_tf_record_input_reader_with_batch_size_two_and_masks(self):
tf_record_path = self.create_tf_record()
input_reader_text_proto = """
shuffle: false
num_readers: 1
load_instance_masks: true
tf_record_input_reader {{
input_path: '{0}'
}}
""".format(tf_record_path)
input_reader_proto = input_reader_pb2.InputReader()
text_format.Merge(input_reader_text_proto, input_reader_proto)
def one_hot_class_encoding_fn(tensor_dict):
tensor_dict[fields.InputDataFields.groundtruth_classes] = tf.one_hot(
tensor_dict[fields.InputDataFields.groundtruth_classes] - 1, depth=3)
return tensor_dict
tensor_dict = dataset_util.make_initializable_iterator(
dataset_builder.build(
input_reader_proto,
transform_input_data_fn=one_hot_class_encoding_fn,
batch_size=2,
max_num_boxes=2,
num_classes=3,
spatial_image_shape=[4, 5])).get_next()
sv = tf.train.Supervisor(logdir=self.get_temp_dir())
with sv.prepare_or_wait_for_session() as sess:
sv.start_queue_runners(sess)
output_dict = sess.run(tensor_dict)
self.assertAllEqual(
[2, 2, 4, 5],
output_dict[fields.InputDataFields.groundtruth_instance_masks].shape)
def test_raises_error_with_no_input_paths(self):
input_reader_text_proto = """
shuffle: false
num_readers: 1
load_instance_masks: true
"""
input_reader_proto = input_reader_pb2.InputReader()
text_format.Merge(input_reader_text_proto, input_reader_proto)
with self.assertRaises(ValueError):
dataset_builder.build(input_reader_proto)
if __name__ == '__main__':
tf.test.main()
...@@ -79,18 +79,32 @@ def build(image_resizer_config): ...@@ -79,18 +79,32 @@ def build(image_resizer_config):
keep_aspect_ratio_config.max_dimension): keep_aspect_ratio_config.max_dimension):
raise ValueError('min_dimension > max_dimension') raise ValueError('min_dimension > max_dimension')
method = _tf_resize_method(keep_aspect_ratio_config.resize_method) method = _tf_resize_method(keep_aspect_ratio_config.resize_method)
return functools.partial( image_resizer_fn = functools.partial(
preprocessor.resize_to_range, preprocessor.resize_to_range,
min_dimension=keep_aspect_ratio_config.min_dimension, min_dimension=keep_aspect_ratio_config.min_dimension,
max_dimension=keep_aspect_ratio_config.max_dimension, max_dimension=keep_aspect_ratio_config.max_dimension,
method=method) method=method,
if image_resizer_config.WhichOneof( pad_to_max_dimension=keep_aspect_ratio_config.pad_to_max_dimension)
if not keep_aspect_ratio_config.convert_to_grayscale:
return image_resizer_fn
elif image_resizer_config.WhichOneof(
'image_resizer_oneof') == 'fixed_shape_resizer': 'image_resizer_oneof') == 'fixed_shape_resizer':
fixed_shape_resizer_config = image_resizer_config.fixed_shape_resizer fixed_shape_resizer_config = image_resizer_config.fixed_shape_resizer
method = _tf_resize_method(fixed_shape_resizer_config.resize_method) method = _tf_resize_method(fixed_shape_resizer_config.resize_method)
return functools.partial( image_resizer_fn = functools.partial(
preprocessor.resize_image, preprocessor.resize_image,
new_height=fixed_shape_resizer_config.height, new_height=fixed_shape_resizer_config.height,
new_width=fixed_shape_resizer_config.width, new_width=fixed_shape_resizer_config.width,
method=method) method=method)
raise ValueError('Invalid image resizer option.') if not fixed_shape_resizer_config.convert_to_grayscale:
return image_resizer_fn
else:
raise ValueError('Invalid image resizer option.')
def grayscale_image_resizer(image):
[resized_image, resized_image_shape] = image_resizer_fn(image)
grayscale_image = preprocessor.rgb_to_gray(resized_image)
grayscale_image_shape = tf.concat([resized_image_shape[:-1], [1]], 0)
return [grayscale_image, grayscale_image_shape]
return functools.partial(grayscale_image_resizer)
...@@ -29,11 +29,11 @@ class ImageResizerBuilderTest(tf.test.TestCase): ...@@ -29,11 +29,11 @@ class ImageResizerBuilderTest(tf.test.TestCase):
image_resizer_fn = image_resizer_builder.build(image_resizer_config) image_resizer_fn = image_resizer_builder.build(image_resizer_config)
images = tf.to_float( images = tf.to_float(
tf.random_uniform(input_shape, minval=0, maxval=255, dtype=tf.int32)) tf.random_uniform(input_shape, minval=0, maxval=255, dtype=tf.int32))
resized_images = image_resizer_fn(images) resized_images, _ = image_resizer_fn(images)
with self.test_session() as sess: with self.test_session() as sess:
return sess.run(resized_images).shape return sess.run(resized_images).shape
def test_built_keep_aspect_ratio_resizer_returns_expected_shape(self): def test_build_keep_aspect_ratio_resizer_returns_expected_shape(self):
image_resizer_text_proto = """ image_resizer_text_proto = """
keep_aspect_ratio_resizer { keep_aspect_ratio_resizer {
min_dimension: 10 min_dimension: 10
...@@ -46,6 +46,20 @@ class ImageResizerBuilderTest(tf.test.TestCase): ...@@ -46,6 +46,20 @@ class ImageResizerBuilderTest(tf.test.TestCase):
input_shape, image_resizer_text_proto) input_shape, image_resizer_text_proto)
self.assertEqual(output_shape, expected_output_shape) self.assertEqual(output_shape, expected_output_shape)
def test_build_keep_aspect_ratio_resizer_with_padding(self):
image_resizer_text_proto = """
keep_aspect_ratio_resizer {
min_dimension: 10
max_dimension: 20
pad_to_max_dimension: true
}
"""
input_shape = (50, 25, 3)
expected_output_shape = (20, 20, 3)
output_shape = self._shape_of_resized_random_image_given_text_proto(
input_shape, image_resizer_text_proto)
self.assertEqual(output_shape, expected_output_shape)
def test_built_fixed_shape_resizer_returns_expected_shape(self): def test_built_fixed_shape_resizer_returns_expected_shape(self):
image_resizer_text_proto = """ image_resizer_text_proto = """
fixed_shape_resizer { fixed_shape_resizer {
...@@ -69,7 +83,7 @@ class ImageResizerBuilderTest(tf.test.TestCase): ...@@ -69,7 +83,7 @@ class ImageResizerBuilderTest(tf.test.TestCase):
text_format.Merge(text_proto, image_resizer_config) text_format.Merge(text_proto, image_resizer_config)
image_resizer_fn = image_resizer_builder.build(image_resizer_config) image_resizer_fn = image_resizer_builder.build(image_resizer_config)
image_placeholder = tf.placeholder(tf.uint8, [1, None, None, 3]) image_placeholder = tf.placeholder(tf.uint8, [1, None, None, 3])
resized_image = image_resizer_fn(image_placeholder) resized_image, _ = image_resizer_fn(image_placeholder)
with self.test_session() as sess: with self.test_session() as sess:
return sess.run(resized_image, feed_dict={image_placeholder: image}) return sess.run(resized_image, feed_dict={image_placeholder: image})
......
...@@ -69,6 +69,7 @@ def build(input_reader_config): ...@@ -69,6 +69,7 @@ def build(input_reader_config):
label_map_proto_file = input_reader_config.label_map_path label_map_proto_file = input_reader_config.label_map_path
decoder = tf_example_decoder.TfExampleDecoder( decoder = tf_example_decoder.TfExampleDecoder(
load_instance_masks=input_reader_config.load_instance_masks, load_instance_masks=input_reader_config.load_instance_masks,
instance_mask_type=input_reader_config.mask_type,
label_map_proto_file=label_map_proto_file) label_map_proto_file=label_map_proto_file)
return decoder.decode(string_tensor) return decoder.decode(string_tensor)
......
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