Commit b0ccdb11 authored by Shixin Luo's avatar Shixin Luo
Browse files

resolve conflict with master

parents e61588cd 1611a8c5
......@@ -24,6 +24,7 @@ from typing import Any, Callable, Dict, Optional
# Import libraries
import tensorflow as tf
from official.modeling import tf_utils
from official.vision.beta.modeling.backbones import factory
from official.vision.beta.modeling.layers import nn_blocks
......@@ -130,7 +131,7 @@ class RevNet(tf.keras.Model):
block_repeats=spec[2],
batch_norm_first=(i != 0), # Only skip on first block
name='revblock_group_{}'.format(i + 2))
endpoints[i + 2] = x
endpoints[str(i + 2)] = x
self._output_specs = {l: endpoints[l].get_shape() for l in endpoints}
......@@ -202,3 +203,25 @@ class RevNet(tf.keras.Model):
def output_specs(self) -> Dict[int, tf.TensorShape]:
"""A dict of {level: TensorShape} pairs for the model output."""
return self._output_specs
@factory.register_backbone_builder('revnet')
def build_revnet(
input_specs: tf.keras.layers.InputSpec,
model_config,
l2_regularizer: tf.keras.regularizers.Regularizer = None) -> tf.keras.Model:
"""Builds ResNet 3d backbone from a config."""
backbone_type = model_config.backbone.type
backbone_cfg = model_config.backbone.get()
norm_activation_config = model_config.norm_activation
assert backbone_type == 'revnet', (f'Inconsistent backbone type '
f'{backbone_type}')
return RevNet(
model_id=backbone_cfg.model_id,
input_specs=input_specs,
activation=norm_activation_config.activation,
use_sync_bn=norm_activation_config.use_sync_bn,
norm_momentum=norm_activation_config.norm_momentum,
norm_epsilon=norm_activation_config.norm_epsilon,
kernel_regularizer=l2_regularizer)
......@@ -40,16 +40,16 @@ class RevNetTest(parameterized.TestCase, tf.test.TestCase):
self.assertAllEqual(
[1, input_size / 2**2, input_size / 2**2, 128 * endpoint_filter_scale],
endpoints[2].shape.as_list())
endpoints['2'].shape.as_list())
self.assertAllEqual(
[1, input_size / 2**3, input_size / 2**3, 256 * endpoint_filter_scale],
endpoints[3].shape.as_list())
endpoints['3'].shape.as_list())
self.assertAllEqual(
[1, input_size / 2**4, input_size / 2**4, 512 * endpoint_filter_scale],
endpoints[4].shape.as_list())
endpoints['4'].shape.as_list())
self.assertAllEqual(
[1, input_size / 2**5, input_size / 2**5, 832 * endpoint_filter_scale],
endpoints[5].shape.as_list())
endpoints['5'].shape.as_list())
@parameterized.parameters(1, 3, 4)
def test_input_specs(self, input_dim):
......
......@@ -25,6 +25,7 @@ import math
from absl import logging
import tensorflow as tf
from official.modeling import tf_utils
from official.vision.beta.modeling.backbones import factory
from official.vision.beta.modeling.layers import nn_blocks
from official.vision.beta.ops import spatial_transform_ops
......@@ -349,7 +350,7 @@ class SpineNet(tf.keras.Model):
block_spec.level > self._max_level):
raise ValueError('Output level is out of range [{}, {}]'.format(
self._min_level, self._max_level))
endpoints[block_spec.level] = x
endpoints[str(block_spec.level)] = x
return endpoints
......@@ -365,14 +366,14 @@ class SpineNet(tf.keras.Model):
kernel_initializer=self._kernel_initializer,
kernel_regularizer=self._kernel_regularizer,
bias_regularizer=self._bias_regularizer)(
net[level])
net[str(level)])
x = self._norm(
axis=self._bn_axis,
momentum=self._norm_momentum,
epsilon=self._norm_epsilon)(
x)
x = tf_utils.get_activation(self._activation_fn)(x)
endpoints[level] = x
endpoints[str(level)] = x
return endpoints
def _resample_with_alpha(self,
......@@ -476,3 +477,36 @@ class SpineNet(tf.keras.Model):
def output_specs(self):
"""A dict of {level: TensorShape} pairs for the model output."""
return self._output_specs
@factory.register_backbone_builder('spinenet')
def build_spinenet(
input_specs: tf.keras.layers.InputSpec,
model_config,
l2_regularizer: tf.keras.regularizers.Regularizer = None) -> tf.keras.Model:
"""Builds ResNet 3d backbone from a config."""
backbone_type = model_config.backbone.type
backbone_cfg = model_config.backbone.get()
norm_activation_config = model_config.norm_activation
assert backbone_type == 'spinenet', (f'Inconsistent backbone type '
f'{backbone_type}')
model_id = backbone_cfg.model_id
if model_id not in SCALING_MAP:
raise ValueError(
'SpineNet-{} is not a valid architecture.'.format(model_id))
scaling_params = SCALING_MAP[model_id]
return SpineNet(
input_specs=input_specs,
min_level=model_config.min_level,
max_level=model_config.max_level,
endpoints_num_filters=scaling_params['endpoints_num_filters'],
resample_alpha=scaling_params['resample_alpha'],
block_repeats=scaling_params['block_repeats'],
filter_size_scale=scaling_params['filter_size_scale'],
kernel_regularizer=l2_regularizer,
activation=norm_activation_config.activation,
use_sync_bn=norm_activation_config.use_sync_bn,
norm_momentum=norm_activation_config.norm_momentum,
norm_epsilon=norm_activation_config.norm_epsilon)
......@@ -54,10 +54,10 @@ class SpineNetTest(parameterized.TestCase, tf.test.TestCase):
endpoints = model(inputs)
for l in range(min_level, max_level + 1):
self.assertIn(l, endpoints.keys())
self.assertIn(str(l), endpoints.keys())
self.assertAllEqual(
[1, input_size / 2**l, input_size / 2**l, endpoints_num_filters],
endpoints[l].shape.as_list())
endpoints[str(l)].shape.as_list())
def test_serialize_deserialize(self):
# Create a network object that sets all of its config options.
......
......@@ -98,30 +98,30 @@ class FPN(tf.keras.Model):
# Get input feature pyramid from backbone.
inputs = self._build_input_pyramid(input_specs, min_level)
backbone_max_level = min(max(inputs.keys()), max_level)
backbone_max_level = min(int(max(inputs.keys())), max_level)
# Build lateral connections.
feats_lateral = {}
for level in range(min_level, backbone_max_level + 1):
feats_lateral[level] = conv2d(
feats_lateral[str(level)] = conv2d(
filters=num_filters,
kernel_size=1,
padding='same',
kernel_initializer=kernel_initializer,
kernel_regularizer=kernel_regularizer,
bias_regularizer=bias_regularizer)(
inputs[level])
inputs[str(level)])
# Build top-down path.
feats = {backbone_max_level: feats_lateral[backbone_max_level]}
feats = {str(backbone_max_level): feats_lateral[str(backbone_max_level)]}
for level in range(backbone_max_level - 1, min_level - 1, -1):
feats[level] = spatial_transform_ops.nearest_upsampling(
feats[level + 1], 2) + feats_lateral[level]
feats[str(level)] = spatial_transform_ops.nearest_upsampling(
feats[str(level + 1)], 2) + feats_lateral[str(level)]
# TODO(xianzhi): consider to remove bias in conv2d.
# Build post-hoc 3x3 convolution kernel.
for level in range(min_level, backbone_max_level + 1):
feats[level] = conv2d(
feats[str(level)] = conv2d(
filters=num_filters,
strides=1,
kernel_size=3,
......@@ -129,15 +129,15 @@ class FPN(tf.keras.Model):
kernel_initializer=kernel_initializer,
kernel_regularizer=kernel_regularizer,
bias_regularizer=bias_regularizer)(
feats[level])
feats[str(level)])
# TODO(xianzhi): consider to remove bias in conv2d.
# Build coarser FPN levels introduced for RetinaNet.
for level in range(backbone_max_level + 1, max_level + 1):
feats_in = feats[level - 1]
feats_in = feats[str(level - 1)]
if level > backbone_max_level + 1:
feats_in = activation_fn(feats_in)
feats[level] = conv2d(
feats[str(level)] = conv2d(
filters=num_filters,
strides=2,
kernel_size=3,
......@@ -149,12 +149,12 @@ class FPN(tf.keras.Model):
# Apply batch norm layers.
for level in range(min_level, max_level + 1):
feats[level] = norm(
feats[str(level)] = norm(
axis=bn_axis, momentum=norm_momentum, epsilon=norm_epsilon)(
feats[level])
feats[str(level)])
self._output_specs = {
level: feats[level].get_shape()
str(level): feats[str(level)].get_shape()
for level in range(min_level, max_level + 1)
}
......@@ -162,7 +162,7 @@ class FPN(tf.keras.Model):
def _build_input_pyramid(self, input_specs, min_level):
assert isinstance(input_specs, dict)
if min(input_specs.keys()) > min_level:
if min(input_specs.keys()) > str(min_level):
raise ValueError(
'Backbone min level should be less or equal to FPN min level')
......
......@@ -47,10 +47,10 @@ class FPNTest(parameterized.TestCase, tf.test.TestCase):
feats = network(endpoints)
for level in range(min_level, max_level + 1):
self.assertIn(level, feats)
self.assertIn(str(level), feats)
self.assertAllEqual(
[1, input_size // 2**level, input_size // 2**level, 256],
feats[level].shape.as_list())
feats[str(level)].shape.as_list())
def test_serialize_deserialize(self):
# Create a network object that sets all of its config options.
......
......@@ -21,11 +21,11 @@ from official.vision.beta.configs import image_classification as classification_
from official.vision.beta.configs import maskrcnn as maskrcnn_cfg
from official.vision.beta.configs import retinanet as retinanet_cfg
from official.vision.beta.configs import video_classification as video_classification_cfg
from official.vision.beta.modeling import backbones
from official.vision.beta.modeling import classification_model
from official.vision.beta.modeling import maskrcnn_model
from official.vision.beta.modeling import retinanet_model
from official.vision.beta.modeling import video_classification_model
from official.vision.beta.modeling.backbones import factory as backbone_factory
from official.vision.beta.modeling.decoders import factory as decoder_factory
from official.vision.beta.modeling.heads import dense_prediction_heads
from official.vision.beta.modeling.heads import instance_heads
......@@ -41,7 +41,7 @@ def build_classification_model(
model_config: classification_cfg.ImageClassificationModel,
l2_regularizer: tf.keras.regularizers.Regularizer = None):
"""Builds the classification model."""
backbone = backbone_factory.build_backbone(
backbone = backbones.factory.build_backbone(
input_specs=input_specs,
model_config=model_config,
l2_regularizer=l2_regularizer)
......@@ -64,7 +64,7 @@ def build_maskrcnn(input_specs: tf.keras.layers.InputSpec,
model_config: maskrcnn_cfg.MaskRCNN,
l2_regularizer: tf.keras.regularizers.Regularizer = None):
"""Builds Mask R-CNN model."""
backbone = backbone_factory.build_backbone(
backbone = backbones.factory.build_backbone(
input_specs=input_specs,
model_config=model_config,
l2_regularizer=l2_regularizer)
......@@ -193,7 +193,7 @@ def build_retinanet(input_specs: tf.keras.layers.InputSpec,
model_config: retinanet_cfg.RetinaNet,
l2_regularizer: tf.keras.regularizers.Regularizer = None):
"""Builds RetinaNet model."""
backbone = backbone_factory.build_backbone(
backbone = backbones.factory.build_backbone(
input_specs=input_specs,
model_config=model_config,
l2_regularizer=l2_regularizer)
......@@ -242,7 +242,7 @@ def build_video_classification_model(
num_classes: int,
l2_regularizer: tf.keras.regularizers.Regularizer = None):
"""Builds the video classification model."""
backbone = backbone_factory.build_backbone_3d(
backbone = backbones.factory.build_backbone(
input_specs=input_specs,
model_config=model_config,
l2_regularizer=l2_regularizer)
......
......@@ -210,18 +210,18 @@ class RetinaNetHead(tf.keras.layers.Layer):
Args:
features: a dict of tensors
- key: `int`, the level of the multilevel features.
- key: `str`, the level of the multilevel features.
- values: `Tensor`, the feature map tensors, whose shape is
[batch, height_l, width_l, channels].
Returns:
scores: a dict of tensors which includes scores of the predictions.
- key: `int`, the level of the multilevel predictions.
- key: `str`, the level of the multilevel predictions.
- values: `Tensor`, the box scores predicted from a particular feature
level, whose shape is
[batch, height_l, width_l, num_classes * num_anchors_per_location].
boxes: a dict of tensors which includes coordinates of the predictions.
- key: `int`, the level of the multilevel predictions.
- key: `str`, the level of the multilevel predictions.
- values: `Tensor`, the box scores predicted from a particular feature
level, whose shape is
[batch, height_l, width_l, 4 * num_anchors_per_location].
......@@ -231,7 +231,7 @@ class RetinaNetHead(tf.keras.layers.Layer):
for i, level in enumerate(
range(self._config_dict['min_level'],
self._config_dict['max_level'] + 1)):
this_level_features = features[level]
this_level_features = features[str(level)]
# class net.
x = this_level_features
......@@ -239,7 +239,7 @@ class RetinaNetHead(tf.keras.layers.Layer):
x = conv(x)
x = norm(x)
x = self._activation(x)
scores[level] = self._classifier(x)
scores[str(level)] = self._classifier(x)
# box net.
x = this_level_features
......@@ -247,7 +247,7 @@ class RetinaNetHead(tf.keras.layers.Layer):
x = conv(x)
x = norm(x)
x = self._activation(x)
boxes[level] = self._box_regressor(x)
boxes[str(level)] = self._box_regressor(x)
return scores, boxes
def get_config(self):
......@@ -433,13 +433,13 @@ class RPNHead(tf.keras.layers.Layer):
for i, level in enumerate(
range(self._config_dict['min_level'],
self._config_dict['max_level'] + 1)):
x = features[level]
x = features[str(level)]
for conv, norm in zip(self._convs, self._norms[i]):
x = conv(x)
x = norm(x)
x = self._activation(x)
scores[level] = self._classifier(x)
boxes[level] = self._box_regressor(x)
scores[str(level)] = self._classifier(x)
boxes[str(level)] = self._box_regressor(x)
return scores, boxes
def get_config(self):
......
......@@ -48,14 +48,14 @@ class RetinaNetHeadTest(parameterized.TestCase, tf.test.TestCase):
bias_regularizer=None,
)
features = {
3: np.random.rand(2, 128, 128, 16),
4: np.random.rand(2, 64, 64, 16),
'3': np.random.rand(2, 128, 128, 16),
'4': np.random.rand(2, 64, 64, 16),
}
scores, boxes = retinanet_head(features)
self.assertAllEqual(scores[3].numpy().shape, [2, 128, 128, 9])
self.assertAllEqual(scores[4].numpy().shape, [2, 64, 64, 9])
self.assertAllEqual(boxes[3].numpy().shape, [2, 128, 128, 12])
self.assertAllEqual(boxes[4].numpy().shape, [2, 64, 64, 12])
self.assertAllEqual(scores['3'].numpy().shape, [2, 128, 128, 9])
self.assertAllEqual(scores['4'].numpy().shape, [2, 64, 64, 9])
self.assertAllEqual(boxes['3'].numpy().shape, [2, 128, 128, 12])
self.assertAllEqual(boxes['4'].numpy().shape, [2, 64, 64, 12])
def test_serialize_deserialize(self):
retinanet_head = dense_prediction_heads.RetinaNetHead(
......@@ -104,14 +104,14 @@ class RpnHeadTest(parameterized.TestCase, tf.test.TestCase):
bias_regularizer=None,
)
features = {
3: np.random.rand(2, 128, 128, 16),
4: np.random.rand(2, 64, 64, 16),
'3': np.random.rand(2, 128, 128, 16),
'4': np.random.rand(2, 64, 64, 16),
}
scores, boxes = rpn_head(features)
self.assertAllEqual(scores[3].numpy().shape, [2, 128, 128, 3])
self.assertAllEqual(scores[4].numpy().shape, [2, 64, 64, 3])
self.assertAllEqual(boxes[3].numpy().shape, [2, 128, 128, 12])
self.assertAllEqual(boxes[4].numpy().shape, [2, 64, 64, 12])
self.assertAllEqual(scores['3'].numpy().shape, [2, 128, 128, 3])
self.assertAllEqual(scores['4'].numpy().shape, [2, 64, 64, 3])
self.assertAllEqual(boxes['3'].numpy().shape, [2, 128, 128, 12])
self.assertAllEqual(boxes['4'].numpy().shape, [2, 64, 64, 12])
def test_serialize_deserialize(self):
rpn_head = dense_prediction_heads.RPNHead(
......
# Copyright 2020 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 box_matcher.py."""
# Import libraries
import numpy as np
import tensorflow as tf
from official.vision.beta.modeling.layers import box_matcher
class BoxMatcherTest(tf.test.TestCase):
def test_box_matcher(self):
boxes_np = np.array(
[[
[0, 0, 1, 1],
[5, 0, 10, 5],
]])
boxes = tf.constant(boxes_np, dtype=tf.float32)
gt_boxes_np = np.array(
[[
[0, 0, 5, 5],
[0, 5, 5, 10],
[5, 0, 10, 5],
[5, 5, 10, 10],
]])
gt_boxes = tf.constant(gt_boxes_np, dtype=tf.float32)
gt_classes_np = np.array([[2, 10, 3, -1]])
gt_classes = tf.constant(gt_classes_np, dtype=tf.int32)
fg_threshold = 0.5
bg_thresh_hi = 0.2
bg_thresh_lo = 0.0
matcher = box_matcher.BoxMatcher(fg_threshold, bg_thresh_hi, bg_thresh_lo)
# Runs on TPU.
strategy = tf.distribute.experimental.TPUStrategy()
with strategy.scope():
(matched_gt_boxes_tpu, matched_gt_classes_tpu, matched_gt_indices_tpu,
positive_matches_tpu, negative_matches_tpu, ignored_matches_tpu) = (
matcher(boxes, gt_boxes, gt_classes))
# Runs on CPU.
(matched_gt_boxes_cpu, matched_gt_classes_cpu, matched_gt_indices_cpu,
positive_matches_cpu, negative_matches_cpu, ignored_matches_cpu) = (
matcher(boxes, gt_boxes, gt_classes))
# correctness
self.assertNDArrayNear(
matched_gt_boxes_cpu.numpy(),
[[[0, 0, 0, 0], [5, 0, 10, 5]]], 1e-4)
self.assertAllEqual(
matched_gt_classes_cpu.numpy(), [[0, 3]])
self.assertAllEqual(
matched_gt_indices_cpu.numpy(), [[-1, 2]])
self.assertAllEqual(
positive_matches_cpu.numpy(), [[False, True]])
self.assertAllEqual(
negative_matches_cpu.numpy(), [[True, False]])
self.assertAllEqual(
ignored_matches_cpu.numpy(), [[False, False]])
# consistency.
self.assertNDArrayNear(
matched_gt_boxes_cpu.numpy(), matched_gt_boxes_tpu.numpy(), 1e-4)
self.assertAllEqual(
matched_gt_classes_cpu.numpy(), matched_gt_classes_tpu.numpy())
self.assertAllEqual(
matched_gt_indices_cpu.numpy(), matched_gt_indices_tpu.numpy())
self.assertAllEqual(
positive_matches_cpu.numpy(), positive_matches_tpu.numpy())
self.assertAllEqual(
negative_matches_cpu.numpy(), negative_matches_tpu.numpy())
self.assertAllEqual(
ignored_matches_cpu.numpy(), ignored_matches_tpu.numpy())
def test_serialize_deserialize(self):
kwargs = dict(
foreground_iou_threshold=0.5,
background_iou_high_threshold=0.5,
background_iou_low_threshold=0.5,
)
matcher = box_matcher.BoxMatcher(**kwargs)
expected_config = dict(kwargs)
self.assertEqual(matcher.get_config(), expected_config)
new_matcher = box_matcher.BoxMatcher.from_config(matcher.get_config())
self.assertAllEqual(matcher.get_config(), new_matcher.get_config())
if __name__ == '__main__':
tf.test.main()
......@@ -563,24 +563,25 @@ class MultilevelDetectionGenerator(tf.keras.layers.Layer):
boxes = []
scores = []
levels = list(raw_boxes.keys())
min_level = min(levels)
max_level = max(levels)
min_level = int(min(levels))
max_level = int(max(levels))
for i in range(min_level, max_level + 1):
raw_boxes_i_shape = tf.shape(raw_boxes[i])
raw_boxes_i_shape = tf.shape(raw_boxes[str(i)])
batch_size = raw_boxes_i_shape[0]
num_anchors_per_locations = raw_boxes_i_shape[-1] // 4
num_classes = tf.shape(raw_scores[i])[-1] // num_anchors_per_locations
num_classes = tf.shape(
raw_scores[str(i)])[-1] // num_anchors_per_locations
# Applies score transformation and remove the implicit background class.
scores_i = tf.sigmoid(
tf.reshape(raw_scores[i], [batch_size, -1, num_classes]))
tf.reshape(raw_scores[str(i)], [batch_size, -1, num_classes]))
scores_i = tf.slice(scores_i, [0, 0, 1], [-1, -1, -1])
# Box decoding.
# The anchor boxes are shared for all data in a batch.
# One stage detector only supports class agnostic box regression.
anchor_boxes_i = tf.reshape(anchor_boxes[i], [batch_size, -1, 4])
raw_boxes_i = tf.reshape(raw_boxes[i], [batch_size, -1, 4])
anchor_boxes_i = tf.reshape(anchor_boxes[str(i)], [batch_size, -1, 4])
raw_boxes_i = tf.reshape(raw_boxes[str(i)], [batch_size, -1, 4])
boxes_i = box_ops.decode_boxes(raw_boxes_i, anchor_boxes_i)
# Box clipping.
......
......@@ -148,22 +148,25 @@ class MultilevelDetectionGeneratorTest(
np.random.rand(84, num_classes) - 0.5) * 3 # random 84x3 outputs.
box_outputs_all = np.random.rand(84, 4) # random 84 boxes.
class_outputs = {
4: tf.reshape(tf.convert_to_tensor(
cls_outputs_all[0:64], dtype=tf.float32),
[1, 8, 8, num_classes]),
5: tf.reshape(tf.convert_to_tensor(
cls_outputs_all[64:80], dtype=tf.float32),
[1, 4, 4, num_classes]),
6: tf.reshape(tf.convert_to_tensor(
cls_outputs_all[80:84], dtype=tf.float32),
[1, 2, 2, num_classes]),
'4':
tf.reshape(
tf.convert_to_tensor(cls_outputs_all[0:64], dtype=tf.float32),
[1, 8, 8, num_classes]),
'5':
tf.reshape(
tf.convert_to_tensor(cls_outputs_all[64:80], dtype=tf.float32),
[1, 4, 4, num_classes]),
'6':
tf.reshape(
tf.convert_to_tensor(cls_outputs_all[80:84], dtype=tf.float32),
[1, 2, 2, num_classes]),
}
box_outputs = {
4: tf.reshape(tf.convert_to_tensor(
'4': tf.reshape(tf.convert_to_tensor(
box_outputs_all[0:64], dtype=tf.float32), [1, 8, 8, 4]),
5: tf.reshape(tf.convert_to_tensor(
'5': tf.reshape(tf.convert_to_tensor(
box_outputs_all[64:80], dtype=tf.float32), [1, 4, 4, 4]),
6: tf.reshape(tf.convert_to_tensor(
'6': tf.reshape(tf.convert_to_tensor(
box_outputs_all[80:84], dtype=tf.float32), [1, 2, 2, 4]),
}
image_info = tf.constant([[[1000, 1000], [100, 100], [0.1, 0.1], [0, 0]]],
......
# Copyright 2020 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 mask_sampler.py."""
# Import libraries
import numpy as np
import tensorflow as tf
from official.vision.beta.modeling.layers import mask_sampler
class SampleAndCropForegroundMasksTest(tf.test.TestCase):
def test_sample_and_crop_foreground_masks(self):
candidate_rois_np = np.array(
[[[0, 0, 0.5, 0.5], [0.5, 0.5, 1, 1],
[2, 2, 4, 4], [1, 1, 5, 5]]])
candidate_rois = tf.constant(candidate_rois_np, dtype=tf.float32)
candidate_gt_boxes_np = np.array(
[[[0, 0, 0.6, 0.6], [0, 0, 0, 0],
[1, 1, 3, 3], [1, 1, 3, 3]]])
candidate_gt_boxes = tf.constant(candidate_gt_boxes_np, dtype=tf.float32)
candidate_gt_classes_np = np.array([[4, 0, 0, 2]])
candidate_gt_classes = tf.constant(
candidate_gt_classes_np, dtype=tf.float32)
candidate_gt_indices_np = np.array([[10, -1, -1, 20]])
candidate_gt_indices = tf.constant(
candidate_gt_indices_np, dtype=tf.int32)
gt_masks_np = np.random.rand(1, 100, 32, 32)
gt_masks = tf.constant(gt_masks_np, dtype=tf.float32)
num_mask_samples_per_image = 2
mask_target_size = 28
# Runs on TPU.
strategy = tf.distribute.experimental.TPUStrategy()
with strategy.scope():
foreground_rois, foreground_classes, cropped_foreground_masks = (
mask_sampler._sample_and_crop_foreground_masks(
candidate_rois, candidate_gt_boxes, candidate_gt_classes,
candidate_gt_indices, gt_masks, num_mask_samples_per_image,
mask_target_size))
foreground_rois_tpu = foreground_rois.numpy()
foreground_classes_tpu = foreground_classes.numpy()
cropped_foreground_masks_tpu = cropped_foreground_masks.numpy()
foreground_rois, foreground_classes, cropped_foreground_masks = (
mask_sampler._sample_and_crop_foreground_masks(
candidate_rois, candidate_gt_boxes, candidate_gt_classes,
candidate_gt_indices, gt_masks, num_mask_samples_per_image,
mask_target_size))
foreground_rois_cpu = foreground_rois.numpy()
foreground_classes_cpu = foreground_classes.numpy()
cropped_foreground_masks_cpu = cropped_foreground_masks.numpy()
# consistency.
self.assertAllEqual(foreground_rois_tpu.shape, foreground_rois_cpu.shape)
self.assertAllEqual(
foreground_classes_tpu.shape, foreground_classes_cpu.shape)
self.assertAllEqual(
cropped_foreground_masks_tpu.shape, cropped_foreground_masks_cpu.shape)
# correctnesss.
self.assertAllEqual(foreground_rois_tpu.shape, [1, 2, 4])
self.assertAllEqual(foreground_classes_tpu.shape, [1, 2])
self.assertAllEqual(cropped_foreground_masks_tpu.shape, [1, 2, 28, 28])
class MaskSamplerTest(tf.test.TestCase):
def test_mask_sampler(self):
candidate_rois_np = np.array(
[[[0, 0, 0.5, 0.5], [0.5, 0.5, 1, 1],
[2, 2, 4, 4], [1, 1, 5, 5]]])
candidate_rois = tf.constant(candidate_rois_np, dtype=tf.float32)
candidate_gt_boxes_np = np.array(
[[[0, 0, 0.6, 0.6], [0, 0, 0, 0],
[1, 1, 3, 3], [1, 1, 3, 3]]])
candidate_gt_boxes = tf.constant(candidate_gt_boxes_np, dtype=tf.float32)
candidate_gt_classes_np = np.array([[4, 0, 0, 2]])
candidate_gt_classes = tf.constant(
candidate_gt_classes_np, dtype=tf.float32)
candidate_gt_indices_np = np.array([[10, -1, -1, 20]])
candidate_gt_indices = tf.constant(
candidate_gt_indices_np, dtype=tf.int32)
gt_masks_np = np.random.rand(1, 100, 32, 32)
gt_masks = tf.constant(gt_masks_np, dtype=tf.float32)
sampler = mask_sampler.MaskSampler(28, 2)
foreground_rois, foreground_classes, cropped_foreground_masks = sampler(
candidate_rois, candidate_gt_boxes, candidate_gt_classes,
candidate_gt_indices, gt_masks)
# correctnesss.
self.assertAllEqual(foreground_rois.numpy().shape, [1, 2, 4])
self.assertAllEqual(foreground_classes.numpy().shape, [1, 2])
self.assertAllEqual(cropped_foreground_masks.numpy().shape, [1, 2, 28, 28])
def test_serialize_deserialize(self):
kwargs = dict(
mask_target_size=7,
num_sampled_masks=10,
)
sampler = mask_sampler.MaskSampler(**kwargs)
expected_config = dict(kwargs)
self.assertEqual(sampler.get_config(), expected_config)
new_sampler = mask_sampler.MaskSampler.from_config(
sampler.get_config())
self.assertAllEqual(sampler.get_config(), new_sampler.get_config())
if __name__ == '__main__':
tf.test.main()
......@@ -95,7 +95,7 @@ def _multilevel_propose_rois(raw_boxes,
roi_scores = []
image_shape = tf.expand_dims(image_shape, axis=1)
for level in sorted(raw_scores.keys()):
with tf.name_scope('level_%d' % level):
with tf.name_scope('level_%s' % level):
_, feature_h, feature_w, num_anchors_per_location = (
raw_scores[level].get_shape().as_list())
......
# Copyright 2020 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 roi_generator.py."""
# Import libraries
import numpy as np
import tensorflow as tf
from official.vision.beta.modeling.layers import roi_generator
class MultilevelProposeRoisTest(tf.test.TestCase):
def test_multilevel_propose_rois_single_level(self):
rpn_boxes_np = np.array(
[[[[0, 0, 10, 10], [0.01, 0.01, 9.9, 9.9]],
[[5, 5, 10, 10], [2, 2, 8, 8]]],
[[[2, 2, 4, 4], [3, 3, 6, 6]],
[[3.1, 3.1, 6.1, 6.1], [1, 1, 8, 8]]]])
rpn_boxes = {
2: tf.constant(rpn_boxes_np, dtype=tf.float32)
}
rpn_scores_np = np.array(
[[[[0.6], [0.9]], [[0.2], [0.3]]], [[[0.1], [0.8]], [[0.3], [0.5]]]])
rpn_scores = {
2: tf.constant(rpn_scores_np, dtype=tf.float32)
}
anchor_boxes_np = np.array(
[[[[0, 0, 10, 10], [0.01, 0.01, 9.9, 9.9]],
[[5, 5, 10, 10], [2, 2, 8, 8]]],
[[[2, 2, 4, 4], [3, 3, 6, 6]],
[[3.1, 3.1, 6.1, 6.1], [1, 1, 8, 8]]]])
anchor_boxes = {
2: tf.constant(anchor_boxes_np, dtype=tf.float32)
}
image_shape = tf.constant([[20, 20], [20, 20]], dtype=tf.int32)
selected_rois_np = np.array(
[[[0.01, 0.01, 9.9, 9.9], [2, 2, 8, 8], [5, 5, 10, 10], [0, 0, 0, 0]],
[[3, 3, 6, 6], [1, 1, 8, 8], [2, 2, 4, 4], [0, 0, 0, 0]]])
selected_roi_scores_np = np.array(
[[0.9, 0.3, 0.2, 0], [0.8, 0.5, 0.1, 0]])
# Runs on TPU.
strategy = tf.distribute.experimental.TPUStrategy()
with strategy.scope():
selected_rois_tpu, selected_roi_scores_tpu = (
roi_generator._multilevel_propose_rois(
rpn_boxes,
rpn_scores,
anchor_boxes=anchor_boxes,
image_shape=image_shape,
pre_nms_top_k=4,
pre_nms_score_threshold=0.0,
pre_nms_min_size_threshold=0.0,
nms_iou_threshold=0.5,
num_proposals=4,
use_batched_nms=False,
decode_boxes=False,
clip_boxes=False,
apply_sigmoid_to_score=False))
# Runs on CPU.
selected_rois_cpu, selected_roi_scores_cpu = (
roi_generator._multilevel_propose_rois(
rpn_boxes,
rpn_scores,
anchor_boxes=anchor_boxes,
image_shape=image_shape,
pre_nms_top_k=4,
pre_nms_score_threshold=0.0,
pre_nms_min_size_threshold=0.0,
nms_iou_threshold=0.5,
num_proposals=4,
use_batched_nms=False,
decode_boxes=False,
clip_boxes=False,
apply_sigmoid_to_score=False))
self.assertNDArrayNear(
selected_rois_tpu.numpy(), selected_rois_cpu.numpy(), 1e-5)
self.assertNDArrayNear(
selected_roi_scores_tpu.numpy(), selected_roi_scores_cpu.numpy(), 1e-5)
self.assertNDArrayNear(
selected_rois_tpu.numpy(), selected_rois_np, 1e-5)
self.assertNDArrayNear(
selected_roi_scores_tpu.numpy(), selected_roi_scores_np, 1e-5)
def test_multilevel_propose_rois_two_levels(self):
rpn_boxes_1_np = np.array(
[[[[0, 0, 10, 10], [0.01, 0.01, 9.99, 9.99]],
[[5, 5, 10, 10], [2, 2, 8, 8]]],
[[[2, 2, 2.5, 2.5], [3, 3, 6, 6]],
[[3.1, 3.1, 6.1, 6.1], [1, 1, 8, 8]]]])
rpn_boxes_2_np = np.array(
[[[[0, 0, 10.01, 10.01]]], [[[2, 2, 4.5, 4.5]]]])
rpn_boxes = {
2: tf.constant(rpn_boxes_1_np, dtype=tf.float32),
3: tf.constant(rpn_boxes_2_np, dtype=tf.float32),
}
rpn_scores_1_np = np.array(
[[[[0.6], [0.9]], [[0.2], [0.3]]], [[[0.1], [0.8]], [[0.3], [0.5]]]])
rpn_scores_2_np = np.array([[[[0.95]]], [[[0.99]]]])
rpn_scores = {
2: tf.constant(rpn_scores_1_np, dtype=tf.float32),
3: tf.constant(rpn_scores_2_np, dtype=tf.float32),
}
anchor_boxes_1_np = np.array(
[[[[0, 0, 10, 10], [0.01, 0.01, 9.99, 9.99]],
[[5, 5, 10, 10], [2, 2, 8, 8]]],
[[[2, 2, 2.5, 2.5], [3, 3, 6, 6]],
[[3.1, 3.1, 6.1, 6.1], [1, 1, 8, 8]]]])
anchor_boxes_2_np = np.array(
[[[[0, 0, 10.01, 10.01]]], [[[2, 2, 4.5, 4.5]]]])
anchor_boxes = {
2: tf.constant(anchor_boxes_1_np, dtype=tf.float32),
3: tf.constant(anchor_boxes_2_np, dtype=tf.float32),
}
image_shape = tf.constant([[20, 20], [20, 20]], dtype=tf.int32)
selected_rois_np = np.array(
[[[0, 0, 10.01, 10.01], [0.01, 0.01, 9.99, 9.99]],
[[2, 2, 4.5, 4.5], [3, 3, 6, 6]]])
selected_roi_scores_np = np.array([[0.95, 0.9], [0.99, 0.8]])
# Runs on TPU.
strategy = tf.distribute.experimental.TPUStrategy()
with strategy.scope():
selected_rois_tpu, selected_roi_scores_tpu = (
roi_generator._multilevel_propose_rois(
rpn_boxes,
rpn_scores,
anchor_boxes=anchor_boxes,
image_shape=image_shape,
pre_nms_top_k=4,
pre_nms_score_threshold=0.0,
pre_nms_min_size_threshold=0.0,
nms_iou_threshold=0.5,
num_proposals=2,
use_batched_nms=False,
decode_boxes=False,
clip_boxes=False,
apply_sigmoid_to_score=False))
# Runs on CPU.
selected_rois_cpu, selected_roi_scores_cpu = (
roi_generator._multilevel_propose_rois(
rpn_boxes,
rpn_scores,
anchor_boxes=anchor_boxes,
image_shape=image_shape,
pre_nms_top_k=4,
pre_nms_score_threshold=0.0,
pre_nms_min_size_threshold=0.0,
nms_iou_threshold=0.5,
num_proposals=2,
use_batched_nms=False,
decode_boxes=False,
clip_boxes=False,
apply_sigmoid_to_score=False))
self.assertNDArrayNear(
selected_rois_tpu.numpy(), selected_rois_cpu.numpy(), 1e-5)
self.assertNDArrayNear(
selected_roi_scores_tpu.numpy(), selected_roi_scores_cpu.numpy(), 1e-5)
self.assertNDArrayNear(
selected_rois_tpu.numpy(), selected_rois_np, 1e-5)
self.assertNDArrayNear(
selected_roi_scores_tpu.numpy(), selected_roi_scores_np, 1e-5)
class MultilevelROIGeneratorTest(tf.test.TestCase):
def test_serialize_deserialize(self):
kwargs = dict(
pre_nms_top_k=2000,
pre_nms_score_threshold=0.0,
pre_nms_min_size_threshold=0.0,
nms_iou_threshold=0.7,
num_proposals=1000,
test_pre_nms_top_k=1000,
test_pre_nms_score_threshold=0.0,
test_pre_nms_min_size_threshold=0.0,
test_nms_iou_threshold=0.7,
test_num_proposals=1000,
use_batched_nms=False,
)
generator = roi_generator.MultilevelROIGenerator(**kwargs)
expected_config = dict(kwargs)
self.assertEqual(generator.get_config(), expected_config)
new_generator = roi_generator.MultilevelROIGenerator.from_config(
generator.get_config())
self.assertAllEqual(generator.get_config(), new_generator.get_config())
......@@ -65,19 +65,19 @@ class RetinaNetModel(tf.keras.Model):
in the batch may be resized into different shapes before padding to the
fixed size.
anchor_boxes: a dict of tensors which includes multilevel anchors.
- key: `int`, the level of the multilevel predictions.
- key: `str`, the level of the multilevel predictions.
- values: `Tensor`, the anchor coordinates of a particular feature
level, whose shape is [height_l, width_l, num_anchors_per_location].
training: `bool`, indicating whether it is in training mode.
Returns:
scores: a dict of tensors which includes scores of the predictions.
- key: `int`, the level of the multilevel predictions.
- key: `str`, the level of the multilevel predictions.
- values: `Tensor`, the box scores predicted from a particular feature
level, whose shape is
[batch, height_l, width_l, num_classes * num_anchors_per_location].
boxes: a dict of tensors which includes coordinates of the predictions.
- key: `int`, the level of the multilevel predictions.
- key: `str`, the level of the multilevel predictions.
- values: `Tensor`, the box coordinates predicted from a particular
feature level, whose shape is
[batch, height_l, width_l, 4 * num_anchors_per_location].
......
......@@ -153,20 +153,20 @@ class RetinaNetTest(parameterized.TestCase, tf.test.TestCase):
cls_outputs = model_outputs['cls_outputs']
box_outputs = model_outputs['box_outputs']
for level in range(min_level, max_level + 1):
self.assertIn(level, cls_outputs)
self.assertIn(level, box_outputs)
self.assertIn(str(level), cls_outputs)
self.assertIn(str(level), box_outputs)
self.assertAllEqual([
2,
image_size[0] // 2**level,
image_size[1] // 2**level,
num_classes * num_anchors_per_location
], cls_outputs[level].numpy().shape)
], cls_outputs[str(level)].numpy().shape)
self.assertAllEqual([
2,
image_size[0] // 2**level,
image_size[1] // 2**level,
4 * num_anchors_per_location
], box_outputs[level].numpy().shape)
], box_outputs[str(level)].numpy().shape)
else:
self.assertIn('detection_boxes', model_outputs)
self.assertIn('detection_scores', model_outputs)
......@@ -220,3 +220,4 @@ class RetinaNetTest(parameterized.TestCase, tf.test.TestCase):
if __name__ == '__main__':
tf.test.main()
......@@ -17,13 +17,10 @@
import collections
# Import libraries
import tensorflow as tf
from official.vision.beta.ops.experimental import anchor_generator
from official.vision.detection.utils.object_detection import argmax_matcher
from official.vision import keras_cv
from official.vision.detection.utils.object_detection import balanced_positive_negative_sampler
from official.vision.detection.utils.object_detection import box_list
from official.vision.detection.utils.object_detection import faster_rcnn_box_coder
from official.vision.detection.utils.object_detection import region_similarity_calculator
from official.vision.detection.utils.object_detection import target_assigner
class Anchor(object):
......@@ -105,7 +102,7 @@ class Anchor(object):
feat_size_y = tf.cast(self.image_size[0] / 2 ** level, tf.int32)
feat_size_x = tf.cast(self.image_size[1] / 2 ** level, tf.int32)
steps = feat_size_y * feat_size_x * self.anchors_per_location
unpacked_labels[level] = tf.reshape(
unpacked_labels[str(level)] = tf.reshape(
labels[count:count + steps], [feat_size_y, feat_size_x, -1])
count += steps
return unpacked_labels
......@@ -135,18 +132,13 @@ class AnchorLabeler(object):
upper-bound threshold to assign negative labels for anchors. An anchor
with a score below the threshold is labeled negative.
"""
similarity_calc = region_similarity_calculator.IouSimilarity()
matcher = argmax_matcher.ArgMaxMatcher(
match_threshold,
unmatched_threshold=unmatched_threshold,
negatives_lower_than_unmatched=True,
self.similarity_calc = keras_cv.ops.IouSimilarity()
self.anchor_labeler = keras_cv.ops.AnchorLabeler()
self.matcher = keras_cv.ops.BoxMatcher(
positive_threshold=match_threshold,
negative_threshold=unmatched_threshold,
force_match_for_each_row=True)
box_coder = faster_rcnn_box_coder.FasterRcnnBoxCoder()
self._target_assigner = target_assigner.TargetAssigner(
similarity_calc, matcher, box_coder)
self._match_threshold = match_threshold
self._unmatched_threshold = unmatched_threshold
self.box_coder = faster_rcnn_box_coder.FasterRcnnBoxCoder()
def label_anchors(self, anchor_boxes, gt_boxes, gt_labels):
"""Labels anchors with ground truth inputs.
......@@ -177,30 +169,17 @@ class AnchorLabeler(object):
1.0 for positive matched anchors, and 0.0 for negative and ignored
anchors.
"""
gt_box_list = box_list.BoxList(gt_boxes)
flattened_anchor_boxes = []
for anchors in anchor_boxes.values():
flattened_anchor_boxes.append(tf.reshape(anchors, [-1, 4]))
flattened_anchor_boxes = tf.concat(flattened_anchor_boxes, axis=0)
similarity_matrix = self.similarity_calc(gt_boxes, flattened_anchor_boxes)
match_results = self.matcher(similarity_matrix)
cls_targets, box_targets, cls_weights, box_weights = self.anchor_labeler(
gt_boxes, gt_labels, match_results)
box_targets_list = box_list.BoxList(box_targets)
anchor_box_list = box_list.BoxList(flattened_anchor_boxes)
# The cls_weights, box_weights are not used.
(cls_targets, cls_weights, box_targets, box_weights,
matches) = self._target_assigner.assign(anchor_box_list, gt_box_list,
gt_labels)
# Labels definition in matches.match_results:
# (1) match_results[i]>=0, meaning that column i is matched with row
# match_results[i].
# (2) match_results[i]=-1, meaning that column i is not matched.
# (3) match_results[i]=-2, meaning that column i is ignored.
match_results = tf.expand_dims(matches.match_results, axis=1)
cls_targets = tf.cast(cls_targets, tf.int32)
cls_targets = tf.where(
tf.equal(match_results, -1), -tf.ones_like(cls_targets), cls_targets)
cls_targets = tf.where(
tf.equal(match_results, -2), -2 * tf.ones_like(cls_targets),
cls_targets)
box_targets = self.box_coder.encode(box_targets_list, anchor_box_list)
# Unpacks labels into multi-level representations.
cls_targets_dict = unpack_targets(cls_targets, anchor_boxes)
......@@ -285,19 +264,33 @@ class RpnAnchorLabeler(AnchorLabeler):
width_l represent the dimension of bounding box regression output at
l-th level.
"""
gt_box_list = box_list.BoxList(gt_boxes)
flattened_anchor_boxes = []
for anchors in anchor_boxes.values():
flattened_anchor_boxes.append(tf.reshape(anchors, [-1, 4]))
flattened_anchor_boxes = tf.concat(flattened_anchor_boxes, axis=0)
anchor_box_list = box_list.BoxList(flattened_anchor_boxes)
similarity_matrix = self.similarity_calc(gt_boxes, flattened_anchor_boxes)
match_results = self.matcher(similarity_matrix)
# cls_targets, cls_weights, box_weights are not used.
_, _, box_targets, _, matches = self._target_assigner.assign(
anchor_box_list, gt_box_list, gt_labels)
_, box_targets, _, _ = self.anchor_labeler(
gt_boxes, gt_labels, match_results)
box_targets_list = box_list.BoxList(box_targets)
anchor_box_list = box_list.BoxList(flattened_anchor_boxes)
box_targets = self.box_coder.encode(box_targets_list, anchor_box_list)
# Zero out the unmatched and ignored regression targets.
num_matches = match_results.shape.as_list()[0] or tf.shape(match_results)[0]
unmatched_ignored_box_targets = tf.zeros([num_matches, 4], dtype=tf.float32)
matched_anchors_mask = tf.greater_equal(match_results, 0)
# To broadcast matched_anchors_mask to the same shape as
# matched_reg_targets.
matched_anchors_mask = tf.tile(
tf.expand_dims(matched_anchors_mask, 1),
[1, tf.shape(box_targets)[1]])
box_targets = tf.where(matched_anchors_mask, box_targets,
unmatched_ignored_box_targets)
# score_targets contains the subsampled positive and negative anchors.
score_targets, _, _ = self._get_rpn_samples(matches.match_results)
score_targets, _, _ = self._get_rpn_samples(match_results)
# Unpacks labels.
score_targets_dict = unpack_targets(score_targets, anchor_boxes)
......@@ -316,9 +309,9 @@ def build_anchor_generator(min_level, max_level, num_scales, aspect_ratios,
scales.append(2**(scale / float(num_scales)))
for level in range(min_level, max_level + 1):
stride = 2**level
strides[level] = stride
anchor_sizes[level] = anchor_size * stride
anchor_gen = anchor_generator.AnchorGenerator(
strides[str(level)] = stride
anchor_sizes[str(level)] = anchor_size * stride
anchor_gen = keras_cv.ops.AnchorGenerator(
anchor_sizes=anchor_sizes,
scales=scales,
aspect_ratios=aspect_ratios,
......
......@@ -124,7 +124,7 @@ class AnchorTest(parameterized.TestCase, tf.test.TestCase):
# Uses the first anchors as ground truth. The ground truth should map to
# two anchors with two intermediate scales at the same location.
gt_boxes = anchor_boxes[3][0:1, 0, 0:4]
gt_boxes = anchor_boxes['3'][0:1, 0, 0:4]
gt_classes = tf.constant([[ground_truth_class_id]], dtype=tf.float32)
(cls_targets, box_targets, _,
box_weights) = anchor_labeler.label_anchors(
......@@ -137,7 +137,7 @@ class AnchorTest(parameterized.TestCase, tf.test.TestCase):
box_weights = box_weights.numpy()
anchor_locations = np.vstack(
np.where(cls_targets[min_level] > -1)).transpose()
np.where(cls_targets[str(min_level)] > -1)).transpose()
self.assertAllClose(expected_anchor_locations, anchor_locations)
# Two anchor boxes on min_level got matched to the gt_boxes.
self.assertAllClose(tf.reduce_sum(box_weights), 2)
......
# Copyright 2020 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 box_ops.py."""
# Import libraries
import numpy as np
import tensorflow as tf
from official.vision.beta.ops import box_ops
def _transform_boxes_on_tpu_and_cpu(transform_fn, boxes, *args):
# Runs on TPU.
strategy = tf.distribute.experimental.TPUStrategy()
with strategy.scope():
transformed_op_tpu = transform_fn(boxes, *args)
transfomred_boxes_tpu = tf.nest.map_structure(lambda x: x.numpy(),
transformed_op_tpu)
# Runs on CPU.
transfomred_op_cpu = transform_fn(boxes, *args)
transfomred_boxes_cpu = tf.nest.map_structure(lambda x: x.numpy(),
transfomred_op_cpu)
return transfomred_boxes_tpu, transfomred_boxes_cpu
class ConvertBoxesTest(tf.test.TestCase):
def testConvertBoxes(self):
# y1, x1, y2, x2.
boxes = np.array([[0, 0, 1, 2], [0.2, 0.1, 1.2, 1.1]])
# x1, y1, width, height
target = np.array([[0, 0, 2, 1], [0.1, 0.2, 1, 1]])
outboxes = box_ops.yxyx_to_xywh(boxes)
self.assertNDArrayNear(outboxes, target, 1e-7)
class JitterBoxesTest(tf.test.TestCase):
def testJitterBoxes(self):
boxes_data = [[0, 0, 1, 1], [0, 0.1, 1, 1.1], [0, 0.3, 1, 1.3],
[0, 0.5, 1, 1.5], [0, 0.7, 1, 1.7], [0, 1.9, 1, 1.9]]
boxes_np = np.array(boxes_data, dtype=np.float32)
max_size = max(
np.amax(boxes_np[:, 3] - boxes_np[:, 1]),
np.amax(boxes_np[:, 2] - boxes_np[:, 0]))
noise_scale = 0.025
boxes = tf.constant(boxes_np)
def jitter_fn(input_boxes, arg_noise_scale):
return box_ops.jitter_boxes(input_boxes, arg_noise_scale)
jittered_boxes_tpu, jittered_boxes_cpu = _transform_boxes_on_tpu_and_cpu(
jitter_fn, boxes, noise_scale)
# Test that the jittered box is within 10 stds from the inputs.
self.assertNDArrayNear(jittered_boxes_tpu, boxes_np,
noise_scale * max_size * 10)
self.assertNDArrayNear(jittered_boxes_cpu, boxes_np,
noise_scale * max_size * 10)
class NormalizeBoxesTest(tf.test.TestCase):
def testNormalizeBoxes1DWithImageShapeAsList(self):
boxes = tf.constant([10, 30, 40, 90], tf.float32)
image_shape = [50, 100]
normalized_boxes_tpu, normalized_boxes_cpu = (
_transform_boxes_on_tpu_and_cpu(
box_ops.normalize_boxes, boxes, image_shape))
self.assertNDArrayNear(normalized_boxes_tpu, normalized_boxes_cpu, 1e-5)
self.assertNDArrayNear(normalized_boxes_tpu, [0.2, 0.3, 0.8, 0.9], 1e-5)
def testNormalizeBoxes1DWithImageShapeAsTensor(self):
boxes = tf.constant([10, 30, 40, 90], tf.float32)
image_shape = tf.constant([50, 100], tf.int32)
normalized_boxes_tpu, normalized_boxes_cpu = (
_transform_boxes_on_tpu_and_cpu(
box_ops.normalize_boxes, boxes, image_shape))
self.assertNDArrayNear(normalized_boxes_tpu, normalized_boxes_cpu, 1e-5)
self.assertNDArrayNear(normalized_boxes_tpu, [0.2, 0.3, 0.8, 0.9], 1e-5)
def testNormalizeBoxes2DWithImageShapeAsList(self):
boxes = tf.constant([[10, 30, 40, 90], [30, 10, 40, 50]], tf.float32)
image_shape = [50, 100]
normalized_boxes_tpu, normalized_boxes_cpu = (
_transform_boxes_on_tpu_and_cpu(
box_ops.normalize_boxes, boxes, image_shape))
self.assertNDArrayNear(normalized_boxes_tpu, normalized_boxes_cpu, 1e-5)
self.assertNDArrayNear(normalized_boxes_tpu,
[[0.2, 0.3, 0.8, 0.9], [0.6, 0.1, 0.8, 0.5]], 1e-5)
def testNormalizeBoxes2DWithImageShapeAsVector(self):
boxes = tf.constant([[10, 30, 40, 90], [30, 10, 40, 50]], tf.float32)
image_shape = tf.constant([50, 100], dtype=tf.int32)
normalized_boxes_tpu, normalized_boxes_cpu = (
_transform_boxes_on_tpu_and_cpu(
box_ops.normalize_boxes, boxes, image_shape))
self.assertNDArrayNear(normalized_boxes_tpu, normalized_boxes_cpu, 1e-5)
self.assertNDArrayNear(normalized_boxes_tpu,
[[0.2, 0.3, 0.8, 0.9], [0.6, 0.1, 0.8, 0.5]], 1e-5)
def testNormalizeBoxes2DWithImageShapeAsBroadcastableTensor(self):
boxes = tf.constant([[10, 30, 40, 90], [30, 10, 40, 50]], tf.float32)
image_shape = tf.constant([[50, 100]], tf.int32)
normalized_boxes_tpu, normalized_boxes_cpu = (
_transform_boxes_on_tpu_and_cpu(
box_ops.normalize_boxes, boxes, image_shape))
self.assertNDArrayNear(normalized_boxes_tpu, normalized_boxes_cpu, 1e-5)
self.assertNDArrayNear(normalized_boxes_tpu,
[[0.2, 0.3, 0.8, 0.9], [0.6, 0.1, 0.8, 0.5]], 1e-5)
def testNormalizeBoxes2DWithImageShapeAsSameShapeTensor(self):
boxes = tf.constant([[10, 30, 40, 90], [30, 10, 40, 50]], tf.float32)
image_shape = tf.constant([[50, 100], [50, 100]], tf.int32)
normalized_boxes_tpu, normalized_boxes_cpu = (
_transform_boxes_on_tpu_and_cpu(
box_ops.normalize_boxes, boxes, image_shape))
self.assertNDArrayNear(normalized_boxes_tpu, normalized_boxes_cpu, 1e-5)
self.assertNDArrayNear(normalized_boxes_tpu,
[[0.2, 0.3, 0.8, 0.9], [0.6, 0.1, 0.8, 0.5]], 1e-5)
def testNormalizeBoxes3DWithImageShapeAsList(self):
boxes = tf.constant([[[10, 30, 40, 90], [30, 10, 40, 50]],
[[20, 40, 50, 80], [30, 50, 40, 90]]], tf.float32)
image_shape = [50, 100]
normalized_boxes_tpu, normalized_boxes_cpu = (
_transform_boxes_on_tpu_and_cpu(
box_ops.normalize_boxes, boxes, image_shape))
self.assertNDArrayNear(normalized_boxes_tpu, normalized_boxes_cpu, 1e-5)
self.assertNDArrayNear(normalized_boxes_tpu,
[[[0.2, 0.3, 0.8, 0.9], [0.6, 0.1, 0.8, 0.5]],
[[0.4, 0.4, 1.0, 0.8], [0.6, 0.5, 0.8, 0.9]]], 1e-5)
def testNormalizeBoxes3DWithImageShapeAsVector(self):
boxes = tf.constant([[[10, 30, 40, 90], [30, 10, 40, 50]],
[[20, 40, 50, 80], [30, 50, 40, 90]]], tf.float32)
image_shape = tf.constant([50, 100], tf.int32)
normalized_boxes_tpu, normalized_boxes_cpu = (
_transform_boxes_on_tpu_and_cpu(
box_ops.normalize_boxes, boxes, image_shape))
self.assertNDArrayNear(normalized_boxes_tpu, normalized_boxes_cpu, 1e-5)
self.assertNDArrayNear(normalized_boxes_tpu,
[[[0.2, 0.3, 0.8, 0.9], [0.6, 0.1, 0.8, 0.5]],
[[0.4, 0.4, 1.0, 0.8], [0.6, 0.5, 0.8, 0.9]]], 1e-5)
def testNormalizeBoxes3DWithImageShapeAsBroadcastableTensor(self):
boxes = tf.constant([[[10, 30, 40, 90], [30, 10, 40, 50]],
[[20, 40, 50, 80], [30, 50, 40, 90]]], tf.float32)
image_shape = tf.constant([[[50, 100]], [[500, 1000]]], tf.int32)
normalized_boxes_tpu, normalized_boxes_cpu = (
_transform_boxes_on_tpu_and_cpu(
box_ops.normalize_boxes, boxes, image_shape))
self.assertNDArrayNear(normalized_boxes_tpu, normalized_boxes_cpu, 1e-5)
self.assertNDArrayNear(
normalized_boxes_tpu,
[[[0.2, 0.3, 0.8, 0.9], [0.6, 0.1, 0.8, 0.5]],
[[0.04, 0.04, 0.1, 0.08], [0.06, 0.05, 0.08, 0.09]]], 1e-5)
def testNormalizeBoxes3DWithImageShapeAsSameShapeTensor(self):
boxes = tf.constant([[[10, 30, 40, 90], [30, 10, 40, 50]],
[[20, 40, 50, 80], [30, 50, 40, 90]]], tf.float32)
image_shape = tf.constant(
[[[50, 100], [50, 100]], [[500, 1000], [500, 1000]]], tf.int32)
normalized_boxes_tpu, normalized_boxes_cpu = (
_transform_boxes_on_tpu_and_cpu(
box_ops.normalize_boxes, boxes, image_shape))
self.assertNDArrayNear(normalized_boxes_tpu, normalized_boxes_cpu, 1e-5)
self.assertNDArrayNear(
normalized_boxes_tpu,
[[[0.2, 0.3, 0.8, 0.9], [0.6, 0.1, 0.8, 0.5]],
[[0.04, 0.04, 0.1, 0.08], [0.06, 0.05, 0.08, 0.09]]], 1e-5)
class DenormalizeBoxesTest(tf.test.TestCase):
def testDenormalizeBoxes1DWithImageShapeAsList(self):
boxes = tf.constant([0.2, 0.3, 0.8, 0.9], tf.float32)
image_shape = [50, 100]
normalized_boxes_tpu, normalized_boxes_cpu = (
_transform_boxes_on_tpu_and_cpu(
box_ops.denormalize_boxes, boxes, image_shape))
self.assertNDArrayNear(normalized_boxes_tpu, normalized_boxes_cpu, 1e-5)
self.assertNDArrayNear(normalized_boxes_tpu, [10, 30, 40, 90], 1e-5)
def testDenormalizeBoxes1DWithImageShapeAsTensor(self):
boxes = tf.constant([0.2, 0.3, 0.8, 0.9], tf.float32)
image_shape = tf.constant([50, 100], tf.int32)
normalized_boxes_tpu, normalized_boxes_cpu = (
_transform_boxes_on_tpu_and_cpu(
box_ops.denormalize_boxes, boxes, image_shape))
self.assertNDArrayNear(normalized_boxes_tpu, normalized_boxes_cpu, 1e-5)
self.assertNDArrayNear(normalized_boxes_tpu, [10, 30, 40, 90], 1e-5)
def testDenormalizeBoxes2DWithImageShapeAsList(self):
boxes = tf.constant([[0.2, 0.3, 0.8, 0.9], [0.6, 0.1, 0.8, 0.5]],
tf.float32)
image_shape = [50, 100]
normalized_boxes_tpu, normalized_boxes_cpu = (
_transform_boxes_on_tpu_and_cpu(
box_ops.denormalize_boxes, boxes, image_shape))
self.assertNDArrayNear(normalized_boxes_tpu, normalized_boxes_cpu, 1e-5)
self.assertNDArrayNear(normalized_boxes_tpu,
[[10, 30, 40, 90], [30, 10, 40, 50]], 1e-5)
def testDenormalizeBoxes2DWithImageShapeAsVector(self):
boxes = tf.constant([[0.2, 0.3, 0.8, 0.9], [0.6, 0.1, 0.8, 0.5]],
tf.float32)
image_shape = tf.constant([50, 100], dtype=tf.int32)
normalized_boxes_tpu, normalized_boxes_cpu = (
_transform_boxes_on_tpu_and_cpu(
box_ops.denormalize_boxes, boxes, image_shape))
self.assertNDArrayNear(normalized_boxes_tpu, normalized_boxes_cpu, 1e-5)
self.assertNDArrayNear(normalized_boxes_tpu,
[[10, 30, 40, 90], [30, 10, 40, 50]], 1e-5)
def testDenormalizeBoxes2DWithImageShapeAsBroadcastableTensor(self):
boxes = tf.constant([[0.2, 0.3, 0.8, 0.9], [0.6, 0.1, 0.8, 0.5]],
tf.float32)
image_shape = tf.constant([[50, 100]], tf.int32)
normalized_boxes_tpu, normalized_boxes_cpu = (
_transform_boxes_on_tpu_and_cpu(
box_ops.denormalize_boxes, boxes, image_shape))
self.assertNDArrayNear(normalized_boxes_tpu, normalized_boxes_cpu, 1e-5)
self.assertNDArrayNear(normalized_boxes_tpu,
[[10, 30, 40, 90], [30, 10, 40, 50]], 1e-5)
def testDenormalizeBoxes2DWithImageShapeAsSameShapeTensor(self):
boxes = tf.constant([[0.2, 0.3, 0.8, 0.9], [0.6, 0.1, 0.8, 0.5]],
tf.float32)
image_shape = tf.constant([[50, 100], [50, 100]], tf.int32)
normalized_boxes_tpu, normalized_boxes_cpu = (
_transform_boxes_on_tpu_and_cpu(
box_ops.denormalize_boxes, boxes, image_shape))
self.assertNDArrayNear(normalized_boxes_tpu, normalized_boxes_cpu, 1e-5)
self.assertNDArrayNear(normalized_boxes_tpu,
[[10, 30, 40, 90], [30, 10, 40, 50]], 1e-5)
def testDenormalizeBoxes3DWithImageShapeAsList(self):
boxes = tf.constant([[[0.2, 0.3, 0.8, 0.9], [0.6, 0.1, 0.8, 0.5]],
[[0.4, 0.4, 1.0, 0.8], [0.6, 0.5, 0.8, 0.9]]],
tf.float32)
image_shape = [50, 100]
normalized_boxes_tpu, normalized_boxes_cpu = (
_transform_boxes_on_tpu_and_cpu(
box_ops.denormalize_boxes, boxes, image_shape))
self.assertNDArrayNear(normalized_boxes_tpu, normalized_boxes_cpu, 1e-5)
self.assertNDArrayNear(normalized_boxes_tpu,
[[[10, 30, 40, 90], [30, 10, 40, 50]],
[[20, 40, 50, 80], [30, 50, 40, 90]]], 1e-5)
def testDenormalizeBoxes3DWithImageShapeAsVector(self):
boxes = tf.constant([[[0.2, 0.3, 0.8, 0.9], [0.6, 0.1, 0.8, 0.5]],
[[0.4, 0.4, 1.0, 0.8], [0.6, 0.5, 0.8, 0.9]]],
tf.float32)
image_shape = tf.constant([50, 100], tf.int32)
normalized_boxes_tpu, normalized_boxes_cpu = (
_transform_boxes_on_tpu_and_cpu(
box_ops.denormalize_boxes, boxes, image_shape))
self.assertNDArrayNear(normalized_boxes_tpu, normalized_boxes_cpu, 1e-5)
self.assertNDArrayNear(normalized_boxes_tpu,
[[[10, 30, 40, 90], [30, 10, 40, 50]],
[[20, 40, 50, 80], [30, 50, 40, 90]]], 1e-5)
def testDenormalizeBoxes3DWithImageShapeAsBroadcastableTensor(self):
boxes = tf.constant([[[0.2, 0.3, 0.8, 0.9], [0.6, 0.1, 0.8, 0.5]],
[[0.04, 0.04, 0.1, 0.08], [0.06, 0.05, 0.08, 0.09]]],
tf.float32)
image_shape = tf.constant([[[50, 100]], [[500, 1000]]], tf.int32)
normalized_boxes_tpu, normalized_boxes_cpu = (
_transform_boxes_on_tpu_and_cpu(
box_ops.denormalize_boxes, boxes, image_shape))
self.assertNDArrayNear(normalized_boxes_tpu, normalized_boxes_cpu, 1e-5)
self.assertNDArrayNear(normalized_boxes_tpu,
[[[10, 30, 40, 90], [30, 10, 40, 50]],
[[20, 40, 50, 80], [30, 50, 40, 90]]], 1e-5)
def testDenormalizeBoxes3DWithImageShapeAsSameShapeTensor(self):
boxes = tf.constant([[[0.2, 0.3, 0.8, 0.9], [0.6, 0.1, 0.8, 0.5]],
[[0.04, 0.04, 0.1, 0.08], [0.06, 0.05, 0.08, 0.09]]],
tf.float32)
image_shape = tf.constant(
[[[50, 100], [50, 100]], [[500, 1000], [500, 1000]]], tf.int32)
normalized_boxes_tpu, normalized_boxes_cpu = (
_transform_boxes_on_tpu_and_cpu(
box_ops.denormalize_boxes, boxes, image_shape))
self.assertNDArrayNear(normalized_boxes_tpu, normalized_boxes_cpu, 1e-5)
self.assertNDArrayNear(normalized_boxes_tpu,
[[[10, 30, 40, 90], [30, 10, 40, 50]],
[[20, 40, 50, 80], [30, 50, 40, 90]]], 1e-5)
class ClipBoxesTest(tf.test.TestCase):
def testClipBoxesImageShapeAsList(self):
boxes_data = [[0, 0, 1, 1], [0, 0.1, 1, 1.1], [0, 0.3, 1, 1.3],
[0, 0.5, 1, 1.5], [0, 0.7, 1, 1.7], [0, 1.9, 1, 1.9]]
image_shape = [3, 3]
boxes = tf.constant(boxes_data)
clipped_boxes_tpu, clipped_boxes_cpu = _transform_boxes_on_tpu_and_cpu(
box_ops.clip_boxes, boxes, image_shape)
self.assertAllClose(clipped_boxes_tpu, clipped_boxes_cpu)
self.assertAllClose(clipped_boxes_tpu,
[[0, 0, 1, 1], [0, 0.1, 1, 1.1], [0, 0.3, 1, 1.3],
[0, 0.5, 1, 1.5], [0, 0.7, 1, 1.7], [0, 1.9, 1, 1.9]])
def testClipBoxesImageShapeAsVector(self):
boxes_data = [[0, 0, 1, 1], [0, 0.1, 1, 1.1], [0, 0.3, 1, 1.3],
[0, 0.5, 1, 1.5], [0, 0.7, 1, 1.7], [0, 1.9, 1, 1.9]]
boxes = tf.constant(boxes_data)
image_shape = np.array([3, 3], dtype=np.float32)
clipped_boxes_tpu, clipped_boxes_cpu = _transform_boxes_on_tpu_and_cpu(
box_ops.clip_boxes, boxes, image_shape)
self.assertAllClose(clipped_boxes_tpu, clipped_boxes_cpu)
self.assertAllClose(clipped_boxes_tpu,
[[0, 0, 1, 1], [0, 0.1, 1, 1.1], [0, 0.3, 1, 1.3],
[0, 0.5, 1, 1.5], [0, 0.7, 1, 1.7], [0, 1.9, 1, 1.9]])
def testClipBoxesImageShapeAsTensor(self):
boxes_data = [[0, 0, 1, 1], [0, 0.1, 1, 1.1], [0, 0.3, 1, 1.3],
[0, 0.5, 1, 1.5], [0, 0.7, 1, 1.7], [0, 1.9, 1, 1.9]]
boxes = tf.constant(boxes_data)
image_shape = tf.constant([[3, 3], [3, 3], [3, 3], [3, 3], [3, 3], [3, 3]],
dtype=tf.float32)
clipped_boxes_tpu, clipped_boxes_cpu = _transform_boxes_on_tpu_and_cpu(
box_ops.clip_boxes, boxes, image_shape)
self.assertAllClose(clipped_boxes_tpu, clipped_boxes_cpu)
self.assertAllClose(clipped_boxes_tpu,
[[0, 0, 1, 1], [0, 0.1, 1, 1.1], [0, 0.3, 1, 1.3],
[0, 0.5, 1, 1.5], [0, 0.7, 1, 1.7], [0, 1.9, 1, 1.9]])
class EncodeDecodeBoxesTest(tf.test.TestCase):
def test_encode_decode_boxes(self):
boxes_np = np.array([[[1.0, 2.0, 3.0, 4.0], [2.0, 3.0, 4.0, 5.0]],
[[4.0, 5.0, 6.0, 7.0], [5.0, 6.0, 7.0, 8.0]]])
boxes = tf.constant(boxes_np, dtype=tf.float32)
anchors = tf.constant([[[1.5, 2.5, 3.5, 4.5], [2.5, 3.5, 4.5, 5.5]],
[[1.5, 2.5, 3.5, 4.5], [2.5, 3.5, 4.5, 5.5]]],
dtype=tf.float32)
weights = [1.0, 1.0, 1.0, 1.0]
def test_fn(boxes, anchors):
encoded_boxes = box_ops.encode_boxes(boxes, anchors, weights)
decoded_boxes = box_ops.decode_boxes(encoded_boxes, anchors, weights)
return decoded_boxes
decoded_boxes_tpu, decoded_boxes_cpu = _transform_boxes_on_tpu_and_cpu(
test_fn, boxes, anchors)
self.assertNDArrayNear(decoded_boxes_tpu, decoded_boxes_cpu, 1e-5)
self.assertNDArrayNear(decoded_boxes_tpu, boxes_np, 1e-5)
def test_encode_decode_boxes_batch_broadcast(self):
boxes_np = np.array([[[1.0, 2.0, 3.0, 4.0], [2.0, 3.0, 4.0, 5.0]],
[[4.0, 5.0, 6.0, 7.0], [5.0, 6.0, 7.0, 8.0]]])
boxes = tf.constant(boxes_np, dtype=tf.float32)
anchors = tf.constant([[[1.5, 2.5, 3.5, 4.5], [2.5, 3.5, 4.5, 5.5]]],
dtype=tf.float32)
weights = [1.0, 1.0, 1.0, 1.0]
def test_fn(boxes, anchors):
encoded_boxes = box_ops.encode_boxes(boxes, anchors, weights)
decoded_boxes = box_ops.decode_boxes(encoded_boxes, anchors, weights)
return decoded_boxes
decoded_boxes_tpu, decoded_boxes_cpu = _transform_boxes_on_tpu_and_cpu(
test_fn, boxes, anchors)
self.assertNDArrayNear(decoded_boxes_tpu, decoded_boxes_cpu, 1e-5)
self.assertNDArrayNear(decoded_boxes_tpu, boxes_np, 1e-5)
class FilterBoxesTest(tf.test.TestCase):
def test_filter_boxes_batch(self):
# boxes -> [[small, good, outside], [outside, small, good]]
boxes_np = np.array([[[1.0, 2.0, 1.5, 2.5], [2.0, 3.0, 4.5, 5.5],
[7.0, 4.0, 9.5, 6.5]],
[[-2.0, 5.0, 0.0, 7.5], [5.0, 6.0, 5.1, 6.0],
[4.0, 1.0, 7.0, 4.0]]])
filtered_boxes_np = np.array([[[0.0, 0.0, 0.0, 0.0], [2.0, 3.0, 4.5, 5.5],
[0.0, 0.0, 0.0, 0.0]],
[[0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0],
[4.0, 1.0, 7.0, 4.0]]])
boxes = tf.constant(boxes_np, dtype=tf.float32)
scores_np = np.array([[0.9, 0.7, 0.5], [0.11, 0.22, 0.33]])
filtered_scores_np = np.array([[0.0, 0.7, 0.0], [0.0, 0.0, 0.33]])
scores = tf.constant(scores_np, dtype=tf.float32)
image_shape = tf.expand_dims(
tf.constant([[8, 8], [8, 8]], dtype=tf.int32), axis=1)
min_size_threshold = 2.0
def test_fn(boxes, scores, image_shape):
filtered_boxes, filtered_scores = box_ops.filter_boxes(
boxes, scores, image_shape, min_size_threshold)
return filtered_boxes, filtered_scores
filtered_results_tpu, filtered_results_cpu = (
_transform_boxes_on_tpu_and_cpu(
test_fn, boxes, scores, image_shape))
filtered_boxes_tpu, filtered_scores_tpu = filtered_results_tpu
filtered_boxes_cpu, filtered_scores_cpu = filtered_results_cpu
self.assertNDArrayNear(filtered_boxes_tpu, filtered_boxes_cpu, 1e-5)
self.assertNDArrayNear(filtered_scores_tpu, filtered_scores_cpu, 1e-5)
self.assertNDArrayNear(filtered_boxes_tpu, filtered_boxes_np, 1e-5)
self.assertNDArrayNear(filtered_scores_tpu, filtered_scores_np, 1e-5)
class FilterBoxesByScoresTest(tf.test.TestCase):
def test_filter_boxes_by_scores_batch(self):
# boxes -> [[small, good, outside], [outside, small, good]]
boxes_np = np.array([[[1.0, 2.0, 1.5, 2.5], [2.0, 3.0, 4.5, 5.5],
[7.0, 4.0, 9.5, 6.5]],
[[-2.0, 5.0, 0.0, 7.5], [5.0, 6.0, 5.1, 6.0],
[4.0, 1.0, 7.0, 4.0]]])
filtered_boxes_np = np.array([[[0.0, 0.0, 0.0, 0.0], [2.0, 3.0, 4.5, 5.5],
[7.0, 4.0, 9.5, 6.5]],
[[0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0],
[4.0, 1.0, 7.0, 4.0]]])
boxes = tf.constant(boxes_np, dtype=tf.float32)
scores_np = np.array([[0.1, 0.7, 0.6], [0.11, 0.22, 0.53]])
filtered_scores_np = np.array([[-1.0, 0.7, 0.6], [-1.0, -1.0, 0.53]])
scores = tf.constant(scores_np, dtype=tf.float32)
min_score_threshold = 0.5
def test_fn(boxes, scores):
filtered_boxes, filtered_scores = box_ops.filter_boxes_by_scores(
boxes, scores, min_score_threshold)
return filtered_boxes, filtered_scores
filtered_results_tpu, filtered_results_cpu = _transform_boxes_on_tpu_and_cpu(
test_fn, boxes, scores)
filtered_boxes_tpu, filtered_scores_tpu = filtered_results_tpu
filtered_boxes_cpu, filtered_scores_cpu = filtered_results_cpu
self.assertNDArrayNear(filtered_boxes_tpu, filtered_boxes_cpu, 1e-5)
self.assertNDArrayNear(filtered_scores_tpu, filtered_scores_cpu, 1e-5)
self.assertNDArrayNear(filtered_boxes_tpu, filtered_boxes_np, 1e-5)
self.assertNDArrayNear(filtered_scores_tpu, filtered_scores_np, 1e-5)
class GatherInstancesTest(tf.test.TestCase):
def test_gather_instances(self):
boxes_np = np.array([[[1.0, 2.0, 1.5, 2.5], [2.0, 3.0, 4.5, 5.5],
[7.0, 4.0, 9.5, 6.5]],
[[-2.0, 5.0, 0.0, 7.5], [5.0, 6.0, 5.1, 6.0],
[4.0, 1.0, 7.0, 4.0]]])
indices_np = np.array([[2, 0], [0, 1]])
boxes = tf.constant(boxes_np, dtype=tf.float32)
indices = tf.constant(indices_np, dtype=tf.int32)
selected_boxes = box_ops.gather_instances(indices, boxes)
expected_selected_boxes = np.array(
[[[7.0, 4.0, 9.5, 6.5], [1.0, 2.0, 1.5, 2.5]],
[[-2.0, 5.0, 0.0, 7.5], [5.0, 6.0, 5.1, 6.0]]])
self.assertNDArrayNear(expected_selected_boxes, selected_boxes, 1e-5)
def test_gather_instances_with_multiple_inputs(self):
boxes_np = np.array([[[1.0, 2.0, 1.5, 2.5], [2.0, 3.0, 4.5, 5.5],
[7.0, 4.0, 9.5, 6.5]],
[[-2.0, 5.0, 0.0, 7.5], [5.0, 6.0, 5.1, 6.0],
[4.0, 1.0, 7.0, 4.0]]])
classes_np = np.array([[1, 2, 3], [20, 30, 40]])
indices_np = np.array([[2, 0], [0, 1]])
boxes = tf.constant(boxes_np, dtype=tf.float32)
classes = tf.constant(classes_np, dtype=tf.int32)
indices = tf.constant(indices_np, dtype=tf.int32)
selected_boxes, selected_classes = box_ops.gather_instances(
indices, boxes, classes)
expected_selected_boxes = np.array(
[[[7.0, 4.0, 9.5, 6.5], [1.0, 2.0, 1.5, 2.5]],
[[-2.0, 5.0, 0.0, 7.5], [5.0, 6.0, 5.1, 6.0]]])
expected_selected_classes = np.array(
[[3, 1], [20, 30]])
self.assertNDArrayNear(expected_selected_boxes, selected_boxes, 1e-5)
self.assertAllEqual(expected_selected_classes, selected_classes)
class TopKBoxesTest(tf.test.TestCase):
def test_top_k_boxes_batch1(self):
boxes_np = np.array([[[1.0, 2.0, 1.5, 2.5], [2.0, 3.0, 4.5, 5.5],
[7.0, 4.0, 9.5, 6.5]]])
boxes = tf.constant(boxes_np, dtype=tf.float32)
scores_np = np.array([[0.9, 0.5, 0.7]])
scores = tf.constant(scores_np, dtype=tf.float32)
top_k_boxes_np = np.array([[[1.0, 2.0, 1.5, 2.5], [7.0, 4.0, 9.5, 6.5]]])
top_k_scores_np = np.array([[0.9, 0.7]])
def test_fn(boxes, scores):
top_k_boxes, top_k_scores = box_ops.top_k_boxes(boxes, scores, k=2)
return top_k_boxes, top_k_scores
top_k_results_tpu, top_k_results_cpu = _transform_boxes_on_tpu_and_cpu(
test_fn, boxes, scores)
top_k_boxes_tpu, top_k_scores_tpu = top_k_results_tpu
top_k_boxes_cpu, top_k_scores_cpu = top_k_results_cpu
self.assertNDArrayNear(top_k_boxes_tpu, top_k_boxes_cpu, 1e-5)
self.assertNDArrayNear(top_k_scores_tpu, top_k_scores_cpu, 1e-5)
self.assertNDArrayNear(top_k_boxes_tpu, top_k_boxes_np, 1e-5)
self.assertNDArrayNear(top_k_scores_tpu, top_k_scores_np, 1e-5)
def test_top_k_boxes_batch2(self):
boxes_np = np.array([[[1.0, 2.0, 1.5, 2.5], [2.0, 3.0, 4.5, 5.5],
[7.0, 4.0, 9.5, 6.5]],
[[-2.0, 5.0, 0.0, 7.5], [5.0, 6.0, 5.1, 6.0],
[4.0, 1.0, 7.0, 4.0]]])
boxes = tf.constant(boxes_np, dtype=tf.float32)
scores_np = np.array([[0.9, 0.7, 0.5], [0.11, 0.22, 0.33]])
scores = tf.constant(scores_np, dtype=tf.float32)
top_k_boxes_np = np.array([[[1.0, 2.0, 1.5, 2.5], [2.0, 3.0, 4.5, 5.5]],
[[4.0, 1.0, 7.0, 4.0], [5.0, 6.0, 5.1, 6.0]]])
top_k_scores_np = np.array([[0.9, 0.7], [0.33, 0.22]])
def test_fn(boxes, scores):
top_k_boxes, top_k_scores = box_ops.top_k_boxes(boxes, scores, k=2)
return top_k_boxes, top_k_scores
top_k_results_tpu, top_k_results_cpu = _transform_boxes_on_tpu_and_cpu(
test_fn, boxes, scores)
top_k_boxes_tpu, top_k_scores_tpu = top_k_results_tpu
top_k_boxes_cpu, top_k_scores_cpu = top_k_results_cpu
self.assertNDArrayNear(top_k_boxes_tpu, top_k_boxes_cpu, 1e-5)
self.assertNDArrayNear(top_k_scores_tpu, top_k_scores_cpu, 1e-5)
self.assertNDArrayNear(top_k_boxes_tpu, top_k_boxes_np, 1e-5)
self.assertNDArrayNear(top_k_scores_tpu, top_k_scores_np, 1e-5)
class BboxeOverlapTest(tf.test.TestCase):
def testBBoxeOverlapOpCorrectness(self):
boxes_data = [[[0, 0, 0.1, 1], [0, 0.2, 0.2, 1.2], [0, 0.3, 0.3, 1.3],
[0, 0.5, 0.4, 1.5], [0, 0.7, 0.5, 1.7], [0, 0.9, 0.6, 1.9],
[0, 0.1, 0.1, 1.1], [0, 0.3, 0.7, 1.3], [0, 0.9, 2, 1.9]],
[[0, 0, 1, 0.2], [0, 0.2, 0.5, 1.2], [0, 0.4, 0.9, 1.4],
[0, 0.6, 1.1, 1.6], [0, 0.8, 1.2, 1.8], [0, 1, 1.5, 2],
[0, 0.5, 1, 1], [0.5, 0.8, 1, 1.8], [-1, -1, -1, -1]]]
boxes_np = np.array(boxes_data, dtype=np.float32)
gt_boxes_data = [[[0, 0.1, 0.1, 1.1], [0, 0.3, 0.7, 1.3], [0, 0.9, 2, 1.9]],
[[0, 0.5, 1, 1], [0.5, 0.8, 1, 1.8], [-1, -1, -1, -1]]]
gt_boxes_np = np.array(gt_boxes_data, dtype=np.float32)
# Runs on TPU.
strategy = tf.distribute.experimental.TPUStrategy()
with strategy.scope():
boxes = tf.constant(boxes_np)
gt_boxes = tf.constant(gt_boxes_np)
iou = box_ops.bbox_overlap(boxes=boxes, gt_boxes=gt_boxes)
iou = iou.numpy()
self.assertEqual(iou.shape, (2, 9, 3))
self.assertAllEqual(
np.argmax(iou, axis=2),
[[0, 0, 1, 1, 1, 2, 0, 1, 2], [0, 0, 0, 0, 1, 1, 0, 1, 0]])
def testBBoxeOverlapOpCheckShape(self):
batch_size = 2
rpn_post_nms_topn = 2000
gt_max_instances = 100
boxes_np = np.random.rand(batch_size, rpn_post_nms_topn,
4).astype(np.float32)
gt_boxes_np = np.random.rand(batch_size, gt_max_instances,
4).astype(np.float32)
strategy = tf.distribute.experimental.TPUStrategy()
with strategy.scope():
boxes = tf.constant(boxes_np)
gt_boxes = tf.constant(gt_boxes_np)
iou = box_ops.bbox_overlap(boxes=boxes, gt_boxes=gt_boxes)
iou = iou.numpy()
self.assertEqual(iou.shape,
(batch_size, (rpn_post_nms_topn), gt_max_instances))
def testBBoxeOverlapOpCorrectnessWithNegativeData(self):
boxes_data = [[[0, -0.01, 0.1, 1.1], [0, 0.2, 0.2, 5.0],
[0, -0.01, 0.1, 1.], [-1, -1, -1, -1]]]
boxes_np = np.array(boxes_data, dtype=np.float32)
gt_boxes_np = boxes_np
strategy = tf.distribute.experimental.TPUStrategy()
with strategy.scope():
boxes = tf.constant(boxes_np)
gt_boxes = tf.constant(gt_boxes_np)
iou = box_ops.bbox_overlap(boxes=boxes, gt_boxes=gt_boxes)
iou = iou.numpy()
expected = np.array([[[0.99999994, 0.0917431, 0.9099099, -1.],
[0.0917431, 1., 0.08154944, -1.],
[0.9099099, 0.08154944, 1., -1.],
[-1., -1., -1., -1.]]])
self.assertAllClose(expected, iou)
class BoxMatchingTest(tf.test.TestCase):
def test_box_matching_single(self):
boxes_np = np.array(
[[[0, 0, 5, 5], [2.5, 2.5, 7.5, 7.5],
[5, 5, 10, 10], [7.5, 7.5, 12.5, 12.5]]])
boxes = tf.constant(boxes_np, dtype=tf.float32)
gt_boxes_np = np.array(
[[[10, 10, 15, 15], [2.5, 2.5, 7.5, 7.5],
[-1, -1, -1, -1]]])
gt_boxes = tf.constant(gt_boxes_np, dtype=tf.float32)
gt_classes_np = np.array([[2, 10, -1]])
gt_classes = tf.constant(gt_classes_np, dtype=tf.int32)
matched_gt_boxes_np = np.array(
[[[2.5, 2.5, 7.5, 7.5],
[2.5, 2.5, 7.5, 7.5],
[2.5, 2.5, 7.5, 7.5],
[10, 10, 15, 15]]])
matched_gt_classes_np = np.array([[10, 10, 10, 2]])
matched_gt_indices_np = np.array([[1, 1, 1, 0]])
matched_iou_np = np.array(
[[0.142857142857, 1.0, 0.142857142857, 0.142857142857]])
iou_np = np.array(
[[[0, 0.142857142857, -1.0],
[0, 1.0, -1.0],
[0, 0.142857142857, -1.0],
[0.142857142857, 0, -1.0]]])
# Runs on TPU.
strategy = tf.distribute.experimental.TPUStrategy()
with strategy.scope():
(matched_gt_boxes_tpu, matched_gt_classes_tpu,
matched_gt_indices_tpu, matched_iou_tpu, iou_tpu) = (
box_ops.box_matching(boxes, gt_boxes, gt_classes))
# Runs on CPU.
(matched_gt_boxes_cpu, matched_gt_classes_cpu,
matched_gt_indices_cpu, matched_iou_cpu, iou_cpu) = (
box_ops.box_matching(boxes, gt_boxes, gt_classes))
# consistency.
self.assertNDArrayNear(
matched_gt_boxes_tpu.numpy(), matched_gt_boxes_cpu.numpy(), 1e-5)
self.assertAllEqual(
matched_gt_classes_tpu.numpy(), matched_gt_classes_cpu.numpy())
self.assertAllEqual(
matched_gt_indices_tpu.numpy(), matched_gt_indices_cpu.numpy())
self.assertNDArrayNear(
matched_iou_tpu.numpy(), matched_iou_cpu.numpy(), 1e-5)
self.assertNDArrayNear(
iou_tpu.numpy(), iou_cpu.numpy(), 1e-5)
# correctness.
self.assertNDArrayNear(
matched_gt_boxes_tpu.numpy(), matched_gt_boxes_np, 1e-5)
self.assertAllEqual(
matched_gt_classes_tpu.numpy(), matched_gt_classes_np)
self.assertAllEqual(
matched_gt_indices_tpu.numpy(), matched_gt_indices_np)
self.assertNDArrayNear(
matched_iou_tpu.numpy(), matched_iou_np, 1e-5)
self.assertNDArrayNear(
iou_tpu.numpy(), iou_np, 1e-5)
def test_box_matching_single_no_gt(self):
boxes_np = np.array(
[[[0, 0, 5, 5], [2.5, 2.5, 7.5, 7.5],
[5, 5, 10, 10], [7.5, 7.5, 12.5, 12.5]]])
boxes = tf.constant(boxes_np, dtype=tf.float32)
gt_boxes_np = np.array(
[[[-1, -1, -1, -1],
[-1, -1, -1, -1],
[-1, -1, -1, -1]]])
gt_boxes = tf.constant(gt_boxes_np, dtype=tf.float32)
gt_classes_np = np.array([[-1, -1, -1]])
gt_classes = tf.constant(gt_classes_np, dtype=tf.int32)
matched_gt_boxes_np = np.array(
[[[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]]])
matched_gt_classes_np = np.array([[0, 0, 0, 0]])
matched_gt_indices_np = np.array([[-1, -1, -1, -1]])
matched_iou_np = np.array([[-1, -1, -1, -1]])
iou_np = np.array(
[[[-1, -1, -1],
[-1, -1, -1],
[-1, -1, -1],
[-1, -1, -1]]])
# Runs on TPU.
strategy = tf.distribute.experimental.TPUStrategy()
with strategy.scope():
(matched_gt_boxes_tpu, matched_gt_classes_tpu,
matched_gt_indices_tpu, matched_iou_tpu, iou_tpu) = (
box_ops.box_matching(boxes, gt_boxes, gt_classes))
# Runs on CPU.
(matched_gt_boxes_cpu, matched_gt_classes_cpu,
matched_gt_indices_cpu, matched_iou_cpu, iou_cpu) = (
box_ops.box_matching(boxes, gt_boxes, gt_classes))
# consistency.
self.assertNDArrayNear(
matched_gt_boxes_tpu.numpy(), matched_gt_boxes_cpu.numpy(), 1e-5)
self.assertAllEqual(
matched_gt_classes_tpu.numpy(), matched_gt_classes_cpu.numpy())
self.assertAllEqual(
matched_gt_indices_tpu.numpy(), matched_gt_indices_cpu.numpy())
self.assertNDArrayNear(
matched_iou_tpu.numpy(), matched_iou_cpu.numpy(), 1e-5)
self.assertNDArrayNear(
iou_tpu.numpy(), iou_cpu.numpy(), 1e-5)
# correctness.
self.assertNDArrayNear(
matched_gt_boxes_tpu.numpy(), matched_gt_boxes_np, 1e-5)
self.assertAllEqual(
matched_gt_classes_tpu.numpy(), matched_gt_classes_np)
self.assertAllEqual(
matched_gt_indices_tpu.numpy(), matched_gt_indices_np)
self.assertNDArrayNear(
matched_iou_tpu.numpy(), matched_iou_np, 1e-5)
self.assertNDArrayNear(
iou_tpu.numpy(), iou_np, 1e-5)
def test_box_matching_batch(self):
boxes_np = np.array(
[[[0, 0, 5, 5], [2.5, 2.5, 7.5, 7.5],
[5, 5, 10, 10], [7.5, 7.5, 12.5, 12.5]],
[[0, 0, 5, 5], [2.5, 2.5, 7.5, 7.5],
[5, 5, 10, 10], [7.5, 7.5, 12.5, 12.5]]])
boxes = tf.constant(boxes_np, dtype=tf.float32)
gt_boxes_np = np.array(
[[[10, 10, 15, 15], [2.5, 2.5, 7.5, 7.5], [-1, -1, -1, -1]],
[[-1, -1, -1, -1], [-1, -1, -1, -1], [-1, -1, -1, -1]]])
gt_boxes = tf.constant(gt_boxes_np, dtype=tf.float32)
gt_classes_np = np.array([[2, 10, -1], [-1, -1, -1]])
gt_classes = tf.constant(gt_classes_np, dtype=tf.int32)
matched_gt_boxes_np = np.array(
[[[2.5, 2.5, 7.5, 7.5],
[2.5, 2.5, 7.5, 7.5],
[2.5, 2.5, 7.5, 7.5],
[10, 10, 15, 15]],
[[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]]])
matched_gt_classes_np = np.array(
[[10, 10, 10, 2],
[0, 0, 0, 0]])
matched_gt_indices_np = np.array(
[[1, 1, 1, 0],
[-1, -1, -1, -1]])
matched_iou_np = np.array(
[[0.142857142857, 1.0, 0.142857142857, 0.142857142857],
[-1, -1, -1, -1]])
iou_np = np.array(
[[[0, 0.142857142857, -1.0],
[0, 1.0, -1.0],
[0, 0.142857142857, -1.0],
[0.142857142857, 0, -1.0]],
[[-1, -1, -1],
[-1, -1, -1],
[-1, -1, -1],
[-1, -1, -1]]])
# Runs on TPU.
strategy = tf.distribute.experimental.TPUStrategy()
with strategy.scope():
(matched_gt_boxes_tpu, matched_gt_classes_tpu,
matched_gt_indices_tpu, matched_iou_tpu, iou_tpu) = (
box_ops.box_matching(boxes, gt_boxes, gt_classes))
# Runs on CPU.
(matched_gt_boxes_cpu, matched_gt_classes_cpu,
matched_gt_indices_cpu, matched_iou_cpu, iou_cpu) = (
box_ops.box_matching(boxes, gt_boxes, gt_classes))
# consistency.
self.assertNDArrayNear(
matched_gt_boxes_tpu.numpy(), matched_gt_boxes_cpu.numpy(), 1e-5)
self.assertAllEqual(
matched_gt_classes_tpu.numpy(), matched_gt_classes_cpu.numpy())
self.assertAllEqual(
matched_gt_indices_tpu.numpy(), matched_gt_indices_cpu.numpy())
self.assertNDArrayNear(
matched_iou_tpu.numpy(), matched_iou_cpu.numpy(), 1e-5)
self.assertNDArrayNear(
iou_tpu.numpy(), iou_cpu.numpy(), 1e-5)
# correctness.
self.assertNDArrayNear(
matched_gt_boxes_tpu.numpy(), matched_gt_boxes_np, 1e-5)
self.assertAllEqual(
matched_gt_classes_tpu.numpy(), matched_gt_classes_np)
self.assertAllEqual(
matched_gt_indices_tpu.numpy(), matched_gt_indices_np)
self.assertNDArrayNear(
matched_iou_tpu.numpy(), matched_iou_np, 1e-5)
self.assertNDArrayNear(
iou_tpu.numpy(), iou_np, 1e-5)
if __name__ == '__main__':
tf.test.main()
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