ops_test.py 63.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 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 object_detection.utils.ops."""
pkulzc's avatar
pkulzc committed
17
18
19
20
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

21
22

from absl.testing import parameterized
23
import numpy as np
pkulzc's avatar
pkulzc committed
24
25
import six
from six.moves import range
26
27
import tensorflow.compat.v1 as tf
import tf_slim as slim
28
29
from object_detection.core import standard_fields as fields
from object_detection.utils import ops
30
from object_detection.utils import test_case
31
32


33
class NormalizedToImageCoordinatesTest(test_case.TestCase):
34
35
36
37

  def test_normalized_to_image_coordinates(self):
    normalized_boxes_np = np.array([[[0.0, 0.0, 1.0, 1.0]],
                                    [[0.5, 0.5, 1.0, 1.0]]])
38
39
40
41
42
43

    def graph_fn(normalized_boxes):
      image_shape = tf.convert_to_tensor([1, 4, 4, 3], dtype=tf.int32)
      absolute_boxes = ops.normalized_to_image_coordinates(
          normalized_boxes, image_shape, parallel_iterations=2)
      return absolute_boxes
44
45
46
47

    expected_boxes = np.array([[[0, 0, 4, 4]],
                               [[2, 2, 4, 4]]])

48
    absolute_boxes = self.execute(graph_fn, [normalized_boxes_np])
49
50
51
    self.assertAllEqual(absolute_boxes, expected_boxes)


52
class ReduceSumTrailingDimensions(test_case.TestCase):
53
54

  def test_reduce_sum_trailing_dimensions(self):
55
56
57
58
59
60

    def graph_fn(input_tensor):
      reduced_tensor = ops.reduce_sum_trailing_dimensions(input_tensor, ndims=2)
      return reduced_tensor

    reduced_np = self.execute(graph_fn, [np.ones((2, 2, 2), np.float32)])
61
62
63
    self.assertAllClose(reduced_np, 2 * np.ones((2, 2), np.float32))


64
class MeshgridTest(test_case.TestCase):
65
66
67

  def test_meshgrid_numpy_comparison(self):
    """Tests meshgrid op with vectors, for which it should match numpy."""
68

69
70
    x = np.arange(4)
    y = np.arange(6)
71
72
73
74
75

    def graph_fn():
      xgrid, ygrid = ops.meshgrid(x, y)
      return xgrid, ygrid

76
    exp_xgrid, exp_ygrid = np.meshgrid(x, y)
77
78
79
    xgrid_output, ygrid_output = self.execute(graph_fn, [])
    self.assertAllEqual(xgrid_output, exp_xgrid)
    self.assertAllEqual(ygrid_output, exp_ygrid)
80
81
82
83
84
85
86

  def test_meshgrid_multidimensional(self):
    np.random.seed(18)
    x = np.random.rand(4, 1, 2).astype(np.float32)
    y = np.random.rand(2, 3).astype(np.float32)

    grid_shape = list(y.shape) + list(x.shape)
87
88
89
90
91
92
93
94

    def graph_fn():
      xgrid, ygrid = ops.meshgrid(x, y)
      self.assertEqual(xgrid.get_shape().as_list(), grid_shape)
      self.assertEqual(ygrid.get_shape().as_list(), grid_shape)
      return xgrid, ygrid

    xgrid_output, ygrid_output = self.execute(graph_fn, [])
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110

    # Check the shape of the output grids
    self.assertEqual(xgrid_output.shape, tuple(grid_shape))
    self.assertEqual(ygrid_output.shape, tuple(grid_shape))

    # Check a few elements
    test_elements = [((3, 0, 0), (1, 2)),
                     ((2, 0, 1), (0, 0)),
                     ((0, 0, 0), (1, 1))]
    for xind, yind in test_elements:
      # These are float equality tests, but the meshgrid op should not introduce
      # rounding.
      self.assertEqual(xgrid_output[yind + xind], x[xind])
      self.assertEqual(ygrid_output[yind + xind], y[yind])


111
class OpsTestFixedPadding(test_case.TestCase):
112
113

  def test_3x3_kernel(self):
114
115
116
117
118
119
120

    def graph_fn():
      tensor = tf.constant([[[[0.], [0.]], [[0.], [0.]]]])
      padded_tensor = ops.fixed_padding(tensor, 3)
      return padded_tensor

    padded_tensor_out = self.execute(graph_fn, [])
121
122
123
    self.assertEqual((1, 4, 4, 1), padded_tensor_out.shape)

  def test_5x5_kernel(self):
124
125
126
127
128
129
130

    def graph_fn():
      tensor = tf.constant([[[[0.], [0.]], [[0.], [0.]]]])
      padded_tensor = ops.fixed_padding(tensor, 5)
      return padded_tensor

    padded_tensor_out = self.execute(graph_fn, [])
131
132
133
    self.assertEqual((1, 6, 6, 1), padded_tensor_out.shape)

  def test_3x3_atrous_kernel(self):
134
135
136
137
138
139
140

    def graph_fn():
      tensor = tf.constant([[[[0.], [0.]], [[0.], [0.]]]])
      padded_tensor = ops.fixed_padding(tensor, 3, 2)
      return padded_tensor

    padded_tensor_out = self.execute(graph_fn, [])
141
142
143
    self.assertEqual((1, 6, 6, 1), padded_tensor_out.shape)


144
class OpsTestPadToMultiple(test_case.TestCase):
145
146

  def test_zero_padding(self):
147
148
149
150
151
152
153

    def graph_fn():
      tensor = tf.constant([[[[0.], [0.]], [[0.], [0.]]]])
      padded_tensor = ops.pad_to_multiple(tensor, 1)
      return padded_tensor

    padded_tensor_out = self.execute(graph_fn, [])
154
155
156
    self.assertEqual((1, 2, 2, 1), padded_tensor_out.shape)

  def test_no_padding(self):
157
158
159
160
161
162
163

    def graph_fn():
      tensor = tf.constant([[[[0.], [0.]], [[0.], [0.]]]])
      padded_tensor = ops.pad_to_multiple(tensor, 2)
      return padded_tensor

    padded_tensor_out = self.execute(graph_fn, [])
164
165
    self.assertEqual((1, 2, 2, 1), padded_tensor_out.shape)

166
  def test_non_square_padding(self):
167
168
169
170
171
172
173

    def graph_fn():
      tensor = tf.constant([[[[0.], [0.]]]])
      padded_tensor = ops.pad_to_multiple(tensor, 2)
      return padded_tensor

    padded_tensor_out = self.execute(graph_fn, [])
174
175
    self.assertEqual((1, 2, 2, 1), padded_tensor_out.shape)

176
  def test_padding(self):
177
178
179
180
181
182
183

    def graph_fn():
      tensor = tf.constant([[[[0.], [0.]], [[0.], [0.]]]])
      padded_tensor = ops.pad_to_multiple(tensor, 4)
      return padded_tensor

    padded_tensor_out = self.execute(graph_fn, [])
184
185
186
    self.assertEqual((1, 4, 4, 1), padded_tensor_out.shape)


187
class OpsTestPaddedOneHotEncoding(test_case.TestCase):
188
189

  def test_correct_one_hot_tensor_with_no_pad(self):
190
191
192
193
194
195

    def graph_fn():
      indices = tf.constant([1, 2, 3, 5])
      one_hot_tensor = ops.padded_one_hot_encoding(indices, depth=6, left_pad=0)
      return one_hot_tensor

196
197
198
199
    expected_tensor = np.array([[0, 1, 0, 0, 0, 0],
                                [0, 0, 1, 0, 0, 0],
                                [0, 0, 0, 1, 0, 0],
                                [0, 0, 0, 0, 0, 1]], np.float32)
200

201
    out_one_hot_tensor = self.execute(graph_fn, [])
202
203
    self.assertAllClose(out_one_hot_tensor, expected_tensor, rtol=1e-10,
                        atol=1e-10)
204
205

  def test_correct_one_hot_tensor_with_pad_one(self):
206
207
208
209
210
211

    def graph_fn():
      indices = tf.constant([1, 2, 3, 5])
      one_hot_tensor = ops.padded_one_hot_encoding(indices, depth=6, left_pad=1)
      return one_hot_tensor

212
213
214
215
    expected_tensor = np.array([[0, 0, 1, 0, 0, 0, 0],
                                [0, 0, 0, 1, 0, 0, 0],
                                [0, 0, 0, 0, 1, 0, 0],
                                [0, 0, 0, 0, 0, 0, 1]], np.float32)
216
    out_one_hot_tensor = self.execute(graph_fn, [])
217
218
    self.assertAllClose(out_one_hot_tensor, expected_tensor, rtol=1e-10,
                        atol=1e-10)
219
220

  def test_correct_one_hot_tensor_with_pad_three(self):
221
222
223
224
225
226

    def graph_fn():
      indices = tf.constant([1, 2, 3, 5])
      one_hot_tensor = ops.padded_one_hot_encoding(indices, depth=6, left_pad=3)
      return one_hot_tensor

227
228
229
230
    expected_tensor = np.array([[0, 0, 0, 0, 1, 0, 0, 0, 0],
                                [0, 0, 0, 0, 0, 1, 0, 0, 0],
                                [0, 0, 0, 0, 0, 0, 1, 0, 0],
                                [0, 0, 0, 0, 0, 0, 0, 0, 1]], np.float32)
231

232
    out_one_hot_tensor = self.execute(graph_fn, [])
233
234
    self.assertAllClose(out_one_hot_tensor, expected_tensor, rtol=1e-10,
                        atol=1e-10)
235
236

  def test_correct_padded_one_hot_tensor_with_empty_indices(self):
237

238
239
    depth = 6
    pad = 2
240
241
242
243
244
245
246

    def graph_fn():
      indices = tf.constant([])
      one_hot_tensor = ops.padded_one_hot_encoding(
          indices, depth=depth, left_pad=pad)
      return one_hot_tensor

247
    expected_tensor = np.zeros((0, depth + pad))
248
    out_one_hot_tensor = self.execute(graph_fn, [])
249
250
    self.assertAllClose(out_one_hot_tensor, expected_tensor, rtol=1e-10,
                        atol=1e-10)
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277

  def test_return_none_on_zero_depth(self):
    indices = tf.constant([1, 2, 3, 4, 5])
    one_hot_tensor = ops.padded_one_hot_encoding(indices, depth=0, left_pad=2)
    self.assertEqual(one_hot_tensor, None)

  def test_raise_value_error_on_rank_two_input(self):
    indices = tf.constant(1.0, shape=(2, 3))
    with self.assertRaises(ValueError):
      ops.padded_one_hot_encoding(indices, depth=6, left_pad=2)

  def test_raise_value_error_on_negative_pad(self):
    indices = tf.constant(1.0, shape=(2, 3))
    with self.assertRaises(ValueError):
      ops.padded_one_hot_encoding(indices, depth=6, left_pad=-1)

  def test_raise_value_error_on_float_pad(self):
    indices = tf.constant(1.0, shape=(2, 3))
    with self.assertRaises(ValueError):
      ops.padded_one_hot_encoding(indices, depth=6, left_pad=0.1)

  def test_raise_value_error_on_float_depth(self):
    indices = tf.constant(1.0, shape=(2, 3))
    with self.assertRaises(ValueError):
      ops.padded_one_hot_encoding(indices, depth=0.1, left_pad=2)


278
class OpsDenseToSparseBoxesTest(test_case.TestCase):
279
280
281
282
283

  def test_return_all_boxes_when_all_input_boxes_are_valid(self):
    num_classes = 4
    num_valid_boxes = 3
    code_size = 4
284
285
286
287
288
289
290
291
292
293

    def graph_fn(dense_location, dense_num_boxes):
      box_locations, box_classes = ops.dense_to_sparse_boxes(
          dense_location, dense_num_boxes, num_classes)
      return box_locations, box_classes

    dense_location_np = np.random.uniform(size=[num_valid_boxes, code_size])
    dense_num_boxes_np = np.array([1, 0, 0, 2], dtype=np.int32)

    expected_box_locations = dense_location_np
294
    expected_box_classses = np.array([0, 3, 3])
295
296
297
298

    # Executing on CPU only since output shape is not constant.
    box_locations, box_classes = self.execute_cpu(
        graph_fn, [dense_location_np, dense_num_boxes_np])
299
300
301
302
303
304
305
306
307
308
309

    self.assertAllClose(box_locations, expected_box_locations, rtol=1e-6,
                        atol=1e-6)
    self.assertAllEqual(box_classes, expected_box_classses)

  def test_return_only_valid_boxes_when_input_contains_invalid_boxes(self):
    num_classes = 4
    num_valid_boxes = 3
    num_boxes = 10
    code_size = 4

310
311
312
313
314
315
316
317
318
    def graph_fn(dense_location, dense_num_boxes):
      box_locations, box_classes = ops.dense_to_sparse_boxes(
          dense_location, dense_num_boxes, num_classes)
      return box_locations, box_classes

    dense_location_np = np.random.uniform(size=[num_boxes, code_size])
    dense_num_boxes_np = np.array([1, 0, 0, 2], dtype=np.int32)

    expected_box_locations = dense_location_np[:num_valid_boxes]
319
    expected_box_classses = np.array([0, 3, 3])
320
321
322
323

    # Executing on CPU only since output shape is not constant.
    box_locations, box_classes = self.execute_cpu(
        graph_fn, [dense_location_np, dense_num_boxes_np])
324
325
326
327
328
329

    self.assertAllClose(box_locations, expected_box_locations, rtol=1e-6,
                        atol=1e-6)
    self.assertAllEqual(box_classes, expected_box_classses)


330
class OpsTestIndicesToDenseVector(test_case.TestCase):
331
332
333
334
335
336
337
338
339

  def test_indices_to_dense_vector(self):
    size = 10000
    num_indices = np.random.randint(size)
    rand_indices = np.random.permutation(np.arange(size))[0:num_indices]

    expected_output = np.zeros(size, dtype=np.float32)
    expected_output[rand_indices] = 1.

340
341
342
343
    def graph_fn():
      tf_rand_indices = tf.constant(rand_indices)
      indicator = ops.indices_to_dense_vector(tf_rand_indices, size)
      return indicator
344

345
346
347
    output = self.execute(graph_fn, [])
    self.assertAllEqual(output, expected_output)
    self.assertEqual(output.dtype, expected_output.dtype)
348
349
350
351
352
353
354
355
356
357

  def test_indices_to_dense_vector_size_at_inference(self):
    size = 5000
    num_indices = 250
    all_indices = np.arange(size)
    rand_indices = np.random.permutation(all_indices)[0:num_indices]

    expected_output = np.zeros(size, dtype=np.float32)
    expected_output[rand_indices] = 1.

358
359
360
361
362
    def graph_fn(tf_all_indices):
      tf_rand_indices = tf.constant(rand_indices)
      indicator = ops.indices_to_dense_vector(tf_rand_indices,
                                              tf.shape(tf_all_indices)[0])
      return indicator
363

364
365
366
    output = self.execute(graph_fn, [all_indices])
    self.assertAllEqual(output, expected_output)
    self.assertEqual(output.dtype, expected_output.dtype)
367
368
369
370
371
372
373
374
375

  def test_indices_to_dense_vector_int(self):
    size = 500
    num_indices = 25
    rand_indices = np.random.permutation(np.arange(size))[0:num_indices]

    expected_output = np.zeros(size, dtype=np.int64)
    expected_output[rand_indices] = 1

376
377
378
379
380
    def graph_fn():
      tf_rand_indices = tf.constant(rand_indices)
      indicator = ops.indices_to_dense_vector(
          tf_rand_indices, size, 1, dtype=tf.int64)
      return indicator
381

382
383
384
    output = self.execute(graph_fn, [])
    self.assertAllEqual(output, expected_output)
    self.assertEqual(output.dtype, expected_output.dtype)
385
386
387
388
389
390
391
392
393
394
395

  def test_indices_to_dense_vector_custom_values(self):
    size = 100
    num_indices = 10
    rand_indices = np.random.permutation(np.arange(size))[0:num_indices]
    indices_value = np.random.rand(1)
    default_value = np.random.rand(1)

    expected_output = np.float32(np.ones(size) * default_value)
    expected_output[rand_indices] = indices_value

396
397
398
399
400
401
402
403
    def graph_fn():
      tf_rand_indices = tf.constant(rand_indices)
      indicator = ops.indices_to_dense_vector(
          tf_rand_indices,
          size,
          indices_value=indices_value,
          default_value=default_value)
      return indicator
404

405
406
407
    output = self.execute(graph_fn, [])
    self.assertAllClose(output, expected_output)
    self.assertEqual(output.dtype, expected_output.dtype)
408
409
410
411
412
413
414
415

  def test_indices_to_dense_vector_all_indices_as_input(self):
    size = 500
    num_indices = 500
    rand_indices = np.random.permutation(np.arange(size))[0:num_indices]

    expected_output = np.ones(size, dtype=np.float32)

416
417
418
419
    def graph_fn():
      tf_rand_indices = tf.constant(rand_indices)
      indicator = ops.indices_to_dense_vector(tf_rand_indices, size)
      return indicator
420

421
422
423
    output = self.execute(graph_fn, [])
    self.assertAllEqual(output, expected_output)
    self.assertEqual(output.dtype, expected_output.dtype)
424
425
426
427
428
429
430

  def test_indices_to_dense_vector_empty_indices_as_input(self):
    size = 500
    rand_indices = []

    expected_output = np.zeros(size, dtype=np.float32)

431
432
433
434
    def graph_fn():
      tf_rand_indices = tf.constant(rand_indices)
      indicator = ops.indices_to_dense_vector(tf_rand_indices, size)
      return indicator
435

436
437
438
    output = self.execute(graph_fn, [])
    self.assertAllEqual(output, expected_output)
    self.assertEqual(output.dtype, expected_output.dtype)
439
440


441
class GroundtruthFilterTest(test_case.TestCase):
442
443

  def test_filter_groundtruth(self):
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480

    def graph_fn(input_image, input_boxes, input_classes, input_is_crowd,
                 input_area, input_difficult, input_label_types,
                 input_confidences, valid_indices):
      input_tensors = {
          fields.InputDataFields.image: input_image,
          fields.InputDataFields.groundtruth_boxes: input_boxes,
          fields.InputDataFields.groundtruth_classes: input_classes,
          fields.InputDataFields.groundtruth_is_crowd: input_is_crowd,
          fields.InputDataFields.groundtruth_area: input_area,
          fields.InputDataFields.groundtruth_difficult: input_difficult,
          fields.InputDataFields.groundtruth_label_types: input_label_types,
          fields.InputDataFields.groundtruth_confidences: input_confidences,
      }

      output_tensors = ops.retain_groundtruth(input_tensors, valid_indices)
      return output_tensors

    input_image = np.random.rand(224, 224, 3)
    input_boxes = np.array([[0.2, 0.4, 0.1, 0.8], [0.2, 0.4, 1.0, 0.8]],
                           dtype=np.float32)
    input_classes = np.array([1, 2], dtype=np.int32)
    input_is_crowd = np.array([False, True], dtype=np.bool)
    input_area = np.array([32, 48], dtype=np.float32)
    input_difficult = np.array([True, False], dtype=np.bool)
    input_label_types = np.array(['APPROPRIATE', 'INCORRECT'],
                                 dtype=np.string_)
    input_confidences = np.array([0.99, 0.5], dtype=np.float32)
    valid_indices = np.array([0], dtype=np.int32)

    # Strings are not supported on TPU.
    output_tensors = self.execute_cpu(
        graph_fn,
        [input_image, input_boxes, input_classes, input_is_crowd, input_area,
         input_difficult, input_label_types, input_confidences, valid_indices]
    )

481
    expected_tensors = {
482
        fields.InputDataFields.image: input_image,
483
484
485
486
487
        fields.InputDataFields.groundtruth_boxes: [[0.2, 0.4, 0.1, 0.8]],
        fields.InputDataFields.groundtruth_classes: [1],
        fields.InputDataFields.groundtruth_is_crowd: [False],
        fields.InputDataFields.groundtruth_area: [32],
        fields.InputDataFields.groundtruth_difficult: [True],
pkulzc's avatar
pkulzc committed
488
        fields.InputDataFields.groundtruth_label_types: [six.b('APPROPRIATE')],
489
        fields.InputDataFields.groundtruth_confidences: [0.99],
490
    }
491
492
493
494
495
496
497
498
499
500
    for key in [fields.InputDataFields.image,
                fields.InputDataFields.groundtruth_boxes,
                fields.InputDataFields.groundtruth_area,
                fields.InputDataFields.groundtruth_confidences]:
      self.assertAllClose(expected_tensors[key], output_tensors[key])

    for key in [fields.InputDataFields.groundtruth_classes,
                fields.InputDataFields.groundtruth_is_crowd,
                fields.InputDataFields.groundtruth_label_types]:
      self.assertAllEqual(expected_tensors[key], output_tensors[key])
501
502

  def test_filter_with_missing_fields(self):
503
504
505
506
507
508

    input_boxes = np.array([[0.2, 0.4, 0.1, 0.8], [0.2, 0.4, 1.0, 0.8]],
                           dtype=np.float)
    input_classes = np.array([1, 2], dtype=np.int32)
    valid_indices = np.array([0], dtype=np.int32)

509
510
511
512
513
514
515
    expected_tensors = {
        fields.InputDataFields.groundtruth_boxes:
        [[0.2, 0.4, 0.1, 0.8]],
        fields.InputDataFields.groundtruth_classes:
        [1]
    }

516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
    def graph_fn(input_boxes, input_classes, valid_indices):
      input_tensors = {
          fields.InputDataFields.groundtruth_boxes: input_boxes,
          fields.InputDataFields.groundtruth_classes: input_classes
      }
      output_tensors = ops.retain_groundtruth(input_tensors, valid_indices)
      return output_tensors

    output_tensors = self.execute(graph_fn, [input_boxes, input_classes,
                                             valid_indices])

    for key in [fields.InputDataFields.groundtruth_boxes]:
      self.assertAllClose(expected_tensors[key], output_tensors[key])
    for key in [fields.InputDataFields.groundtruth_classes]:
      self.assertAllEqual(expected_tensors[key], output_tensors[key])
531
532

  def test_filter_with_empty_fields(self):
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555

    def graph_fn(input_boxes, input_classes, input_is_crowd, input_area,
                 input_difficult, input_confidences, valid_indices):
      input_tensors = {
          fields.InputDataFields.groundtruth_boxes: input_boxes,
          fields.InputDataFields.groundtruth_classes: input_classes,
          fields.InputDataFields.groundtruth_is_crowd: input_is_crowd,
          fields.InputDataFields.groundtruth_area: input_area,
          fields.InputDataFields.groundtruth_difficult: input_difficult,
          fields.InputDataFields.groundtruth_confidences: input_confidences,
      }
      output_tensors = ops.retain_groundtruth(input_tensors, valid_indices)
      return output_tensors

    input_boxes = np.array([[0.2, 0.4, 0.1, 0.8], [0.2, 0.4, 1.0, 0.8]],
                           dtype=np.float)
    input_classes = np.array([1, 2], dtype=np.int32)
    input_is_crowd = np.array([False, True], dtype=np.bool)
    input_area = np.array([], dtype=np.float32)
    input_difficult = np.array([], dtype=np.float32)
    input_confidences = np.array([0.99, 0.5], dtype=np.float32)
    valid_indices = np.array([0], dtype=np.int32)

556
    expected_tensors = {
557
558
559
560
561
562
        fields.InputDataFields.groundtruth_boxes: [[0.2, 0.4, 0.1, 0.8]],
        fields.InputDataFields.groundtruth_classes: [1],
        fields.InputDataFields.groundtruth_is_crowd: [False],
        fields.InputDataFields.groundtruth_area: [],
        fields.InputDataFields.groundtruth_difficult: [],
        fields.InputDataFields.groundtruth_confidences: [0.99],
563
    }
564
565
566
567
568
569
570
571
572
573
574
    output_tensors = self.execute(graph_fn, [
        input_boxes, input_classes, input_is_crowd, input_area,
        input_difficult, input_confidences, valid_indices])

    for key in [fields.InputDataFields.groundtruth_boxes,
                fields.InputDataFields.groundtruth_area,
                fields.InputDataFields.groundtruth_confidences]:
      self.assertAllClose(expected_tensors[key], output_tensors[key])
    for key in [fields.InputDataFields.groundtruth_classes,
                fields.InputDataFields.groundtruth_is_crowd]:
      self.assertAllEqual(expected_tensors[key], output_tensors[key])
575
576
577

  def test_filter_with_empty_groundtruth_boxes(self):

578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
    def graph_fn(input_boxes, input_classes, input_is_crowd, input_area,
                 input_difficult, input_confidences, valid_indices):
      input_tensors = {
          fields.InputDataFields.groundtruth_boxes: input_boxes,
          fields.InputDataFields.groundtruth_classes: input_classes,
          fields.InputDataFields.groundtruth_is_crowd: input_is_crowd,
          fields.InputDataFields.groundtruth_area: input_area,
          fields.InputDataFields.groundtruth_difficult: input_difficult,
          fields.InputDataFields.groundtruth_confidences: input_confidences,
      }
      output_tensors = ops.retain_groundtruth(input_tensors, valid_indices)
      return output_tensors

    input_boxes = np.array([], dtype=np.float).reshape(0, 4)
    input_classes = np.array([], dtype=np.int32)
    input_is_crowd = np.array([], dtype=np.bool)
    input_area = np.array([], dtype=np.float32)
    input_difficult = np.array([], dtype=np.float32)
    input_confidences = np.array([], dtype=np.float32)
    valid_indices = np.array([], dtype=np.int32)

    output_tensors = self.execute(graph_fn, [input_boxes, input_classes,
                                             input_is_crowd, input_area,
                                             input_difficult,
                                             input_confidences,
                                             valid_indices])
    for key in output_tensors:
      if key == fields.InputDataFields.groundtruth_boxes:
        self.assertAllEqual([0, 4], output_tensors[key].shape)
      else:
        self.assertAllEqual([0], output_tensors[key].shape)


class RetainGroundTruthWithPositiveClasses(test_case.TestCase):
612
613

  def test_filter_groundtruth_with_positive_classes(self):
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642

    def graph_fn(input_image, input_boxes, input_classes, input_is_crowd,
                 input_area, input_difficult, input_label_types,
                 input_confidences):
      input_tensors = {
          fields.InputDataFields.image: input_image,
          fields.InputDataFields.groundtruth_boxes: input_boxes,
          fields.InputDataFields.groundtruth_classes: input_classes,
          fields.InputDataFields.groundtruth_is_crowd: input_is_crowd,
          fields.InputDataFields.groundtruth_area: input_area,
          fields.InputDataFields.groundtruth_difficult: input_difficult,
          fields.InputDataFields.groundtruth_label_types: input_label_types,
          fields.InputDataFields.groundtruth_confidences: input_confidences,
      }
      output_tensors = ops.retain_groundtruth_with_positive_classes(
          input_tensors)
      return output_tensors

    input_image = np.random.rand(224, 224, 3)
    input_boxes = np.array([[0.2, 0.4, 0.1, 0.8], [0.2, 0.4, 1.0, 0.8]],
                           dtype=np.float)
    input_classes = np.array([1, 0], dtype=np.int32)
    input_is_crowd = np.array([False, True], dtype=np.bool)
    input_area = np.array([32, 48], dtype=np.float32)
    input_difficult = np.array([True, False], dtype=np.bool)
    input_label_types = np.array(['APPROPRIATE', 'INCORRECT'],
                                 dtype=np.string_)
    input_confidences = np.array([0.99, 0.5], dtype=np.float32)

643
    expected_tensors = {
644
        fields.InputDataFields.image: input_image,
645
646
647
648
649
        fields.InputDataFields.groundtruth_boxes: [[0.2, 0.4, 0.1, 0.8]],
        fields.InputDataFields.groundtruth_classes: [1],
        fields.InputDataFields.groundtruth_is_crowd: [False],
        fields.InputDataFields.groundtruth_area: [32],
        fields.InputDataFields.groundtruth_difficult: [True],
pkulzc's avatar
pkulzc committed
650
        fields.InputDataFields.groundtruth_label_types: [six.b('APPROPRIATE')],
651
        fields.InputDataFields.groundtruth_confidences: [0.99],
652
653
    }

654
655
656
657
658
659
660
    # Executing on CPU because string types are not supported on TPU.
    output_tensors = self.execute_cpu(graph_fn,
                                      [input_image, input_boxes,
                                       input_classes, input_is_crowd,
                                       input_area,
                                       input_difficult, input_label_types,
                                       input_confidences])
661

662
663
664
665
666
667
668
669
670
671
672
673
    for key in [fields.InputDataFields.image,
                fields.InputDataFields.groundtruth_boxes,
                fields.InputDataFields.groundtruth_area,
                fields.InputDataFields.groundtruth_confidences]:
      self.assertAllClose(expected_tensors[key], output_tensors[key])
    for key in [fields.InputDataFields.groundtruth_classes,
                fields.InputDataFields.groundtruth_is_crowd,
                fields.InputDataFields.groundtruth_label_types]:
      self.assertAllEqual(expected_tensors[key], output_tensors[key])


class ReplaceNaNGroundtruthLabelScoresWithOnes(test_case.TestCase):
674
675

  def test_replace_nan_groundtruth_label_scores_with_ones(self):
676
677
678
679
680
681
682

    def graph_fn():
      label_scores = tf.constant([np.nan, 1.0, np.nan])
      output_tensor = ops.replace_nan_groundtruth_label_scores_with_ones(
          label_scores)
      return output_tensor

683
    expected_tensor = [1.0, 1.0, 1.0]
684
685
    output_tensor = self.execute(graph_fn, [])
    self.assertAllClose(expected_tensor, output_tensor)
686
687

  def test_input_equals_output_when_no_nans(self):
688

689
    input_label_scores = [0.5, 1.0, 1.0]
690
691
692
693
694
695
696
697
698
    def graph_fn():
      label_scores_tensor = tf.constant(input_label_scores)
      output_label_scores = ops.replace_nan_groundtruth_label_scores_with_ones(
          label_scores_tensor)
      return output_label_scores

    output_label_scores = self.execute(graph_fn, [])

    self.assertAllClose(input_label_scores, output_label_scores)
699
700


701
class GroundtruthFilterWithCrowdBoxesTest(test_case.TestCase):
702
703

  def test_filter_groundtruth_with_crowd_boxes(self):
704
705
706
707
708
709
710
711
712
713
714
715
716

    def graph_fn():
      input_tensors = {
          fields.InputDataFields.groundtruth_boxes:
          [[0.1, 0.2, 0.6, 0.8], [0.2, 0.4, 0.1, 0.8]],
          fields.InputDataFields.groundtruth_classes: [1, 2],
          fields.InputDataFields.groundtruth_is_crowd: [True, False],
          fields.InputDataFields.groundtruth_area: [100.0, 238.7],
          fields.InputDataFields.groundtruth_confidences: [0.5, 0.99],
      }
      output_tensors = ops.filter_groundtruth_with_crowd_boxes(
          input_tensors)
      return output_tensors
717
718

    expected_tensors = {
719
720
721
722
723
        fields.InputDataFields.groundtruth_boxes: [[0.2, 0.4, 0.1, 0.8]],
        fields.InputDataFields.groundtruth_classes: [2],
        fields.InputDataFields.groundtruth_is_crowd: [False],
        fields.InputDataFields.groundtruth_area: [238.7],
        fields.InputDataFields.groundtruth_confidences: [0.99],
724
725
    }

726
727
728
729
730
731
732
733
    output_tensors = self.execute(graph_fn, [])
    for key in [fields.InputDataFields.groundtruth_boxes,
                fields.InputDataFields.groundtruth_area,
                fields.InputDataFields.groundtruth_confidences]:
      self.assertAllClose(expected_tensors[key], output_tensors[key])
    for key in [fields.InputDataFields.groundtruth_classes,
                fields.InputDataFields.groundtruth_is_crowd]:
      self.assertAllEqual(expected_tensors[key], output_tensors[key])
734
735


736
class GroundtruthFilterWithNanBoxTest(test_case.TestCase):
737
738

  def test_filter_groundtruth_with_nan_box_coordinates(self):
739
740
741
742
743
744
745
746
747
748
749
750
751

    def graph_fn():
      input_tensors = {
          fields.InputDataFields.groundtruth_boxes:
          [[np.nan, np.nan, np.nan, np.nan], [0.2, 0.4, 0.1, 0.8]],
          fields.InputDataFields.groundtruth_classes: [1, 2],
          fields.InputDataFields.groundtruth_is_crowd: [False, True],
          fields.InputDataFields.groundtruth_area: [100.0, 238.7],
          fields.InputDataFields.groundtruth_confidences: [0.5, 0.99],
      }
      output_tensors = ops.filter_groundtruth_with_nan_box_coordinates(
          input_tensors)
      return output_tensors
752
753

    expected_tensors = {
754
755
756
757
758
        fields.InputDataFields.groundtruth_boxes: [[0.2, 0.4, 0.1, 0.8]],
        fields.InputDataFields.groundtruth_classes: [2],
        fields.InputDataFields.groundtruth_is_crowd: [True],
        fields.InputDataFields.groundtruth_area: [238.7],
        fields.InputDataFields.groundtruth_confidences: [0.99],
759
760
    }

761
762
763
764
765
766
767
768
    output_tensors = self.execute(graph_fn, [])
    for key in [fields.InputDataFields.groundtruth_boxes,
                fields.InputDataFields.groundtruth_area,
                fields.InputDataFields.groundtruth_confidences]:
      self.assertAllClose(expected_tensors[key], output_tensors[key])
    for key in [fields.InputDataFields.groundtruth_classes,
                fields.InputDataFields.groundtruth_is_crowd]:
      self.assertAllEqual(expected_tensors[key], output_tensors[key])
769
770


771
class GroundtruthFilterWithUnrecognizedClassesTest(test_case.TestCase):
772
773

  def test_filter_unrecognized_classes(self):
774
775
776
777
778
779
780
781
782
783
784
    def graph_fn():
      input_tensors = {
          fields.InputDataFields.groundtruth_boxes:
          [[.3, .3, .5, .7], [0.2, 0.4, 0.1, 0.8]],
          fields.InputDataFields.groundtruth_classes: [-1, 2],
          fields.InputDataFields.groundtruth_is_crowd: [False, True],
          fields.InputDataFields.groundtruth_area: [100.0, 238.7],
          fields.InputDataFields.groundtruth_confidences: [0.5, 0.99],
      }
      output_tensors = ops.filter_unrecognized_classes(input_tensors)
      return output_tensors
785
786
787
788
789
790
791
792
793

    expected_tensors = {
        fields.InputDataFields.groundtruth_boxes: [[0.2, 0.4, 0.1, 0.8]],
        fields.InputDataFields.groundtruth_classes: [2],
        fields.InputDataFields.groundtruth_is_crowd: [True],
        fields.InputDataFields.groundtruth_area: [238.7],
        fields.InputDataFields.groundtruth_confidences: [0.99],
    }

794
795
796
797
798
799
800
801
    output_tensors = self.execute(graph_fn, [])
    for key in [fields.InputDataFields.groundtruth_boxes,
                fields.InputDataFields.groundtruth_area,
                fields.InputDataFields.groundtruth_confidences]:
      self.assertAllClose(expected_tensors[key], output_tensors[key])
    for key in [fields.InputDataFields.groundtruth_classes,
                fields.InputDataFields.groundtruth_is_crowd]:
      self.assertAllEqual(expected_tensors[key], output_tensors[key])
802
803


804
class OpsTestNormalizeToTarget(test_case.TestCase):
805
806

  def test_create_normalize_to_target(self):
807
808
809
810

    if self.is_tf2():
      self.skipTest('Skipping as variable names not supported in eager mode.')

811
812
813
814
815
816
    inputs = tf.random_uniform([5, 10, 12, 3])
    target_norm_value = 4.0
    dim = 3
    with self.test_session():
      output = ops.normalize_to_target(inputs, target_norm_value, dim)
      self.assertEqual(output.op.name, 'NormalizeToTarget/mul')
817
      var_name = slim.get_variables()[0].name
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
      self.assertEqual(var_name, 'NormalizeToTarget/weights:0')

  def test_invalid_dim(self):
    inputs = tf.random_uniform([5, 10, 12, 3])
    target_norm_value = 4.0
    dim = 10
    with self.assertRaisesRegexp(
        ValueError,
        'dim must be non-negative but smaller than the input rank.'):
      ops.normalize_to_target(inputs, target_norm_value, dim)

  def test_invalid_target_norm_values(self):
    inputs = tf.random_uniform([5, 10, 12, 3])
    target_norm_value = [4.0, 4.0]
    dim = 3
    with self.assertRaisesRegexp(
        ValueError, 'target_norm_value must be a float or a list of floats'):
      ops.normalize_to_target(inputs, target_norm_value, dim)

  def test_correct_output_shape(self):
838
839
840
841
842
843
844
845
846

    if self.is_tf2():
      self.skipTest('normalize_to_target not supported in eager mode because,'
                    ' it requires creating variables.')

    inputs = np.random.uniform(size=(5, 10, 12, 3)).astype(np.float32)
    def graph_fn(inputs):
      target_norm_value = 4.0
      dim = 3
847
      output = ops.normalize_to_target(inputs, target_norm_value, dim)
848
849
850
851
852
853
      return output

    # Executing on CPU since creating a variable inside a conditional is not
    # supported.
    outputs = self.execute_cpu(graph_fn, [inputs])
    self.assertEqual(outputs.shape, inputs.shape)
854
855

  def test_correct_initial_output_values(self):
856
857
858
859
860
861
862
863
864
865
866
867
868

    if self.is_tf2():
      self.skipTest('normalize_to_target not supported in eager mode because,'
                    ' it requires creating variables.')
    def graph_fn():
      inputs = tf.constant([[[[3, 4], [7, 24]],
                             [[5, -12], [-1, 0]]]], tf.float32)
      target_norm_value = 10.0
      dim = 3
      normalized_inputs = ops.normalize_to_target(inputs, target_norm_value,
                                                  dim)
      return normalized_inputs

869
870
    expected_output = [[[[30/5.0, 40/5.0], [70/25.0, 240/25.0]],
                        [[50/13.0, -120/13.0], [-10, 0]]]]
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
    # Executing on CPU since creating a variable inside a conditional is not
    # supported.
    output = self.execute_cpu(graph_fn, [])
    self.assertAllClose(output, expected_output)

  def test_multiple_target_norm_values(self):

    if self.is_tf2():
      self.skipTest('normalize_to_target not supported in eager mode because,'
                    ' it requires creating variables.')

    def graph_fn():
      inputs = tf.constant([[[[3, 4], [7, 24]],
                             [[5, -12], [-1, 0]]]], tf.float32)
      target_norm_value = [10.0, 20.0]
      dim = 3
887
888
      normalized_inputs = ops.normalize_to_target(inputs, target_norm_value,
                                                  dim)
889
      return normalized_inputs
890
891
892

    expected_output = [[[[30/5.0, 80/5.0], [70/25.0, 480/25.0]],
                        [[50/13.0, -240/13.0], [-10, 0]]]]
893
894
895
896
897

    # Executing on CPU since creating a variable inside a conditional is not
    # supported.
    output = self.execute_cpu(graph_fn, [])
    self.assertAllClose(output, expected_output)
898
899


900
class OpsTestPositionSensitiveCropRegions(test_case.TestCase):
901
902
903

  def test_position_sensitive(self):
    num_spatial_bins = [3, 2]
904
    image_shape = [3, 2, 6]
905
906
907
908
909
910
911
912

    # The result for both boxes should be [[1, 2], [3, 4], [5, 6]]
    # before averaging.
    expected_output = np.array([3.5, 3.5]).reshape([2, 1, 1, 1])

    for crop_size_mult in range(1, 3):
      crop_size = [3 * crop_size_mult, 2 * crop_size_mult]

913
914
915
916
917
918
919
920
921
922
923
924
925
      def graph_fn():
        # First channel is 1's, second channel is 2's, etc.
        image = tf.constant(
            list(range(1, 3 * 2 + 1)) * 6, dtype=tf.float32, shape=image_shape)
        boxes = tf.random_uniform((2, 4))

        # pylint:disable=cell-var-from-loop
        ps_crop_and_pool = ops.position_sensitive_crop_regions(
            image, boxes, crop_size, num_spatial_bins, global_pool=True)
        return ps_crop_and_pool

      output = self.execute(graph_fn, [])
      self.assertAllClose(output, expected_output)
926
927
928

  def test_position_sensitive_with_equal_channels(self):
    num_spatial_bins = [2, 2]
929
    image_shape = [3, 3, 4]
930
931
    crop_size = [2, 2]

932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
    def graph_fn():
      image = tf.constant(
          list(range(1, 3 * 3 + 1)), dtype=tf.float32, shape=[3, 3, 1])
      tiled_image = tf.tile(image, [1, 1, image_shape[2]])
      boxes = tf.random_uniform((3, 4))
      box_ind = tf.constant([0, 0, 0], dtype=tf.int32)

      # All channels are equal so position-sensitive crop and resize should
      # work as the usual crop and resize for just one channel.
      crop = tf.image.crop_and_resize(tf.expand_dims(image, axis=0), boxes,
                                      box_ind, crop_size)
      crop_and_pool = tf.reduce_mean(crop, [1, 2], keepdims=True)

      ps_crop_and_pool = ops.position_sensitive_crop_regions(
          tiled_image,
          boxes,
          crop_size,
          num_spatial_bins,
          global_pool=True)

      return crop_and_pool, ps_crop_and_pool

    # Crop and resize op is not supported in TPUs.
    expected_output, output = self.execute_cpu(graph_fn, [])
    self.assertAllClose(output, expected_output)
957
958
959

  def test_raise_value_error_on_num_bins_less_than_one(self):
    num_spatial_bins = [1, -1]
960
    image_shape = [1, 1, 2]
961
962
963
964
965
966
967
    crop_size = [2, 2]

    image = tf.constant(1, dtype=tf.float32, shape=image_shape)
    boxes = tf.constant([[0, 0, 1, 1]], dtype=tf.float32)

    with self.assertRaisesRegexp(ValueError, 'num_spatial_bins should be >= 1'):
      ops.position_sensitive_crop_regions(
968
          image, boxes, crop_size, num_spatial_bins, global_pool=True)
969
970
971

  def test_raise_value_error_on_non_divisible_crop_size(self):
    num_spatial_bins = [2, 3]
972
    image_shape = [1, 1, 6]
973
974
975
976
977
978
979
980
    crop_size = [3, 2]

    image = tf.constant(1, dtype=tf.float32, shape=image_shape)
    boxes = tf.constant([[0, 0, 1, 1]], dtype=tf.float32)

    with self.assertRaisesRegexp(
        ValueError, 'crop_size should be divisible by num_spatial_bins'):
      ops.position_sensitive_crop_regions(
981
          image, boxes, crop_size, num_spatial_bins, global_pool=True)
982
983
984

  def test_raise_value_error_on_non_divisible_num_channels(self):
    num_spatial_bins = [2, 2]
985
    image_shape = [1, 1, 5]
986
987
    crop_size = [2, 2]

988
989
990
991
992
993
    def graph_fn():
      image = tf.constant(1, dtype=tf.float32, shape=image_shape)
      boxes = tf.constant([[0, 0, 1, 1]], dtype=tf.float32)

      return ops.position_sensitive_crop_regions(
          image, boxes, crop_size, num_spatial_bins, global_pool=True)
994
995
996

    with self.assertRaisesRegexp(
        ValueError, 'Dimension size must be evenly divisible by 4 but is 5'):
997
      self.execute(graph_fn, [])
998
999
1000

  def test_position_sensitive_with_global_pool_false(self):
    num_spatial_bins = [3, 2]
1001
    image_shape = [3, 2, 6]
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
    num_boxes = 2

    expected_output = []

    # Expected output, when crop_size = [3, 2].
    expected_output.append(np.expand_dims(
        np.tile(np.array([[1, 2],
                          [3, 4],
                          [5, 6]]), (num_boxes, 1, 1)),
        axis=-1))

    # Expected output, when crop_size = [6, 4].
    expected_output.append(np.expand_dims(
        np.tile(np.array([[1, 1, 2, 2],
                          [1, 1, 2, 2],
                          [3, 3, 4, 4],
                          [3, 3, 4, 4],
                          [5, 5, 6, 6],
                          [5, 5, 6, 6]]), (num_boxes, 1, 1)),
        axis=-1))

    for crop_size_mult in range(1, 3):
      crop_size = [3 * crop_size_mult, 2 * crop_size_mult]
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
      # First channel is 1's, second channel is 2's, etc.

      def graph_fn():
        # pylint:disable=cell-var-from-loop
        image = tf.constant(
            list(range(1, 3 * 2 + 1)) * 6, dtype=tf.float32, shape=image_shape)
        boxes = tf.random_uniform((num_boxes, 4))

        ps_crop = ops.position_sensitive_crop_regions(
            image, boxes, crop_size, num_spatial_bins, global_pool=False)
        return ps_crop

      output = self.execute(graph_fn, [])
1038
      self.assertAllClose(output, expected_output[crop_size_mult - 1])
1039
1040
1041

  def test_position_sensitive_with_global_pool_false_and_do_global_pool(self):
    num_spatial_bins = [3, 2]
1042
    image_shape = [3, 2, 6]
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
    num_boxes = 2

    expected_output = []

    # Expected output, when crop_size = [3, 2].
    expected_output.append(np.mean(
        np.expand_dims(
            np.tile(np.array([[1, 2],
                              [3, 4],
                              [5, 6]]), (num_boxes, 1, 1)),
            axis=-1),
        axis=(1, 2), keepdims=True))

    # Expected output, when crop_size = [6, 4].
    expected_output.append(np.mean(
        np.expand_dims(
            np.tile(np.array([[1, 1, 2, 2],
                              [1, 1, 2, 2],
                              [3, 3, 4, 4],
                              [3, 3, 4, 4],
                              [5, 5, 6, 6],
                              [5, 5, 6, 6]]), (num_boxes, 1, 1)),
            axis=-1),
        axis=(1, 2), keepdims=True))

    for crop_size_mult in range(1, 3):
      crop_size = [3 * crop_size_mult, 2 * crop_size_mult]

1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
      def graph_fn():
        # pylint:disable=cell-var-from-loop
        # First channel is 1's, second channel is 2's, etc.
        image = tf.constant(
            list(range(1, 3 * 2 + 1)) * 6, dtype=tf.float32, shape=image_shape)
        boxes = tf.random_uniform((num_boxes, 4))

        # Perform global_pooling after running the function with
        # global_pool=False.
        ps_crop = ops.position_sensitive_crop_regions(
            image, boxes, crop_size, num_spatial_bins, global_pool=False)
        ps_crop_and_pool = tf.reduce_mean(
            ps_crop, reduction_indices=(1, 2), keepdims=True)
        return ps_crop_and_pool

      output = self.execute(graph_fn, [])
1087
      self.assertAllClose(output, expected_output[crop_size_mult - 1])
1088
1089
1090

  def test_raise_value_error_on_non_square_block_size(self):
    num_spatial_bins = [3, 2]
1091
    image_shape = [3, 2, 6]
1092
1093
1094
1095
1096
1097
1098
1099
    crop_size = [6, 2]

    image = tf.constant(1, dtype=tf.float32, shape=image_shape)
    boxes = tf.constant([[0, 0, 1, 1]], dtype=tf.float32)

    with self.assertRaisesRegexp(
        ValueError, 'Only support square bin crop size for now.'):
      ops.position_sensitive_crop_regions(
1100
1101
1102
          image, boxes, crop_size, num_spatial_bins, global_pool=False)


1103
class OpsTestBatchPositionSensitiveCropRegions(test_case.TestCase):
1104
1105
1106
1107
1108
1109

  def test_position_sensitive_with_single_bin(self):
    num_spatial_bins = [1, 1]
    image_shape = [2, 3, 3, 4]
    crop_size = [2, 2]

1110
1111
1112
1113
    def graph_fn():
      image = tf.random_uniform(image_shape)
      boxes = tf.random_uniform((2, 3, 4))
      box_ind = tf.constant([0, 0, 0, 1, 1, 1], dtype=tf.int32)
1114

1115
1116
1117
1118
1119
1120
1121
      # When a single bin is used, position-sensitive crop and pool should be
      # the same as non-position sensitive crop and pool.
      crop = tf.image.crop_and_resize(image,
                                      tf.reshape(boxes, [-1, 4]), box_ind,
                                      crop_size)
      crop_and_pool = tf.reduce_mean(crop, [1, 2], keepdims=True)
      crop_and_pool = tf.reshape(crop_and_pool, [2, 3, 1, 1, 4])
1122

1123
1124
1125
      ps_crop_and_pool = ops.batch_position_sensitive_crop_regions(
          image, boxes, crop_size, num_spatial_bins, global_pool=True)
      return crop_and_pool, ps_crop_and_pool
1126

1127
1128
1129
    # Crop and resize is not supported on TPUs.
    expected_output, output = self.execute_cpu(graph_fn, [])
    self.assertAllClose(output, expected_output)
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154

  def test_position_sensitive_with_global_pool_false_and_known_boxes(self):
    num_spatial_bins = [2, 2]
    image_shape = [2, 2, 2, 4]
    crop_size = [2, 2]

    # box_ind = tf.constant([0, 1], dtype=tf.int32)

    expected_output = []

    # Expected output, when the box containing whole image.
    expected_output.append(
        np.reshape(np.array([[4, 7],
                             [10, 13]]),
                   (1, 2, 2, 1))
    )

    # Expected output, when the box containing only first row.
    expected_output.append(
        np.reshape(np.array([[3, 6],
                             [7, 10]]),
                   (1, 2, 2, 1))
    )
    expected_output = np.stack(expected_output, axis=0)

1155
1156
1157
1158
    def graph_fn():
      images = tf.constant(
          list(range(1, 2 * 2 * 4 + 1)) * 2, dtype=tf.float32,
          shape=image_shape)
1159

1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
      # First box contains whole image, and second box contains only first row.
      boxes = tf.constant(np.array([[[0., 0., 1., 1.]],
                                    [[0., 0., 0.5, 1.]]]), dtype=tf.float32)

      ps_crop = ops.batch_position_sensitive_crop_regions(
          images, boxes, crop_size, num_spatial_bins, global_pool=False)
      return ps_crop

    output = self.execute(graph_fn, [])
    self.assertAllEqual(output, expected_output)
1170
1171
1172
1173
1174
1175

  def test_position_sensitive_with_global_pool_false_and_single_bin(self):
    num_spatial_bins = [1, 1]
    image_shape = [2, 3, 3, 4]
    crop_size = [1, 1]

1176
1177
1178
1179
    def graph_fn():
      images = tf.random_uniform(image_shape)
      boxes = tf.random_uniform((2, 3, 4))
      # box_ind = tf.constant([0, 0, 0, 1, 1, 1], dtype=tf.int32)
1180

1181
1182
1183
1184
1185
1186
1187
      # Since single_bin is used and crop_size = [1, 1] (i.e., no crop resize),
      # the outputs are the same whatever the global_pool value is.
      ps_crop_and_pool = ops.batch_position_sensitive_crop_regions(
          images, boxes, crop_size, num_spatial_bins, global_pool=True)
      ps_crop = ops.batch_position_sensitive_crop_regions(
          images, boxes, crop_size, num_spatial_bins, global_pool=False)
      return ps_crop_and_pool, ps_crop
1188

1189
1190
    pooled_output, unpooled_output = self.execute(graph_fn, [])
    self.assertAllClose(pooled_output, unpooled_output)
1191
1192


1193
1194
# The following tests are only executed on CPU because the output
# shape is not constant.
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
class ReframeBoxMasksToImageMasksTest(test_case.TestCase,
                                      parameterized.TestCase):

  @parameterized.parameters(
      {'mask_dtype': tf.float32, 'mask_dtype_np': np.float32,
       'resize_method': 'bilinear'},
      {'mask_dtype': tf.float32, 'mask_dtype_np': np.float32,
       'resize_method': 'nearest'},
      {'mask_dtype': tf.uint8, 'mask_dtype_np': np.uint8,
       'resize_method': 'bilinear'},
      {'mask_dtype': tf.uint8, 'mask_dtype_np': np.uint8,
       'resize_method': 'nearest'},
  )
  def testZeroImageOnEmptyMask(self, mask_dtype, mask_dtype_np, resize_method):
1209
1210
1211
    np_expected_image_masks = np.array([[[0, 0, 0, 0],
                                         [0, 0, 0, 0],
                                         [0, 0, 0, 0],
1212
                                         [0, 0, 0, 0]]])
1213
1214
    def graph_fn():
      box_masks = tf.constant([[[0, 0],
1215
                                [0, 0]]], dtype=mask_dtype)
1216
      boxes = tf.constant([[0.0, 0.0, 1.0, 1.0]], dtype=tf.float32)
1217
1218
1219
      image_masks = ops.reframe_box_masks_to_image_masks(
          box_masks, boxes, image_height=4, image_width=4,
          resize_method=resize_method)
1220
1221
1222
      return image_masks

    np_image_masks = self.execute_cpu(graph_fn, [])
1223
    self.assertEqual(np_image_masks.dtype, mask_dtype_np)
1224
    self.assertAllClose(np_image_masks, np_expected_image_masks)
1225

1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
  @parameterized.parameters(
      {'mask_dtype': tf.float32, 'mask_dtype_np': np.float32,
       'resize_method': 'bilinear'},
      {'mask_dtype': tf.float32, 'mask_dtype_np': np.float32,
       'resize_method': 'nearest'},
      {'mask_dtype': tf.uint8, 'mask_dtype_np': np.uint8,
       'resize_method': 'bilinear'},
      {'mask_dtype': tf.uint8, 'mask_dtype_np': np.uint8,
       'resize_method': 'nearest'},
  )
  def testZeroBoxMasks(self, mask_dtype, mask_dtype_np, resize_method):
1237
1238

    def graph_fn():
1239
      box_masks = tf.zeros([0, 3, 3], dtype=mask_dtype)
1240
      boxes = tf.zeros([0, 4], dtype=tf.float32)
1241
1242
1243
      image_masks = ops.reframe_box_masks_to_image_masks(
          box_masks, boxes, image_height=4, image_width=4,
          resize_method=resize_method)
1244
1245
1246
      return image_masks

    np_image_masks = self.execute_cpu(graph_fn, [])
1247
    self.assertEqual(np_image_masks.dtype, mask_dtype_np)
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
    self.assertAllEqual(np_image_masks.shape, np.array([0, 4, 4]))

  def testBoxWithZeroArea(self):

    def graph_fn():
      box_masks = tf.zeros([1, 3, 3], dtype=tf.float32)
      boxes = tf.constant([[0.1, 0.2, 0.1, 0.7]], dtype=tf.float32)
      image_masks = ops.reframe_box_masks_to_image_masks(box_masks, boxes,
                                                         image_height=4,
                                                         image_width=4)
      return image_masks

    np_image_masks = self.execute_cpu(graph_fn, [])
    self.assertAllEqual(np_image_masks.shape, np.array([1, 4, 4]))
1262

1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
  @parameterized.parameters(
      {'mask_dtype': tf.float32, 'mask_dtype_np': np.float32,
       'resize_method': 'bilinear'},
      {'mask_dtype': tf.float32, 'mask_dtype_np': np.float32,
       'resize_method': 'nearest'},
      {'mask_dtype': tf.uint8, 'mask_dtype_np': np.uint8,
       'resize_method': 'bilinear'},
      {'mask_dtype': tf.uint8, 'mask_dtype_np': np.uint8,
       'resize_method': 'nearest'},
  )
  def testMaskIsCenteredInImageWhenBoxIsCentered(self, mask_dtype,
                                                 mask_dtype_np, resize_method):
1275
1276

    def graph_fn():
1277
1278
      box_masks = tf.constant([[[4, 4],
                                [4, 4]]], dtype=mask_dtype)
1279
      boxes = tf.constant([[0.25, 0.25, 0.75, 0.75]], dtype=tf.float32)
1280
1281
1282
      image_masks = ops.reframe_box_masks_to_image_masks(
          box_masks, boxes, image_height=4, image_width=4,
          resize_method=resize_method)
1283
1284
      return image_masks

1285
    np_expected_image_masks = np.array([[[0, 0, 0, 0],
1286
1287
1288
                                         [0, 4, 4, 0],
                                         [0, 4, 4, 0],
                                         [0, 0, 0, 0]]], dtype=mask_dtype_np)
1289
    np_image_masks = self.execute_cpu(graph_fn, [])
1290
    self.assertEqual(np_image_masks.dtype, mask_dtype_np)
1291
    self.assertAllClose(np_image_masks, np_expected_image_masks)
1292

1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
  @parameterized.parameters(
      {'mask_dtype': tf.float32, 'mask_dtype_np': np.float32,
       'resize_method': 'bilinear'},
      {'mask_dtype': tf.float32, 'mask_dtype_np': np.float32,
       'resize_method': 'nearest'},
      {'mask_dtype': tf.uint8, 'mask_dtype_np': np.uint8,
       'resize_method': 'bilinear'},
      {'mask_dtype': tf.uint8, 'mask_dtype_np': np.uint8,
       'resize_method': 'nearest'},
  )
  def testMaskOffCenterRemainsOffCenterInImage(self, mask_dtype,
                                               mask_dtype_np, resize_method):
1305
1306
1307

    def graph_fn():
      box_masks = tf.constant([[[1, 0],
1308
                                [0, 1]]], dtype=mask_dtype)
1309
      boxes = tf.constant([[0.25, 0.5, 0.75, 1.0]], dtype=tf.float32)
1310
1311
1312
      image_masks = ops.reframe_box_masks_to_image_masks(
          box_masks, boxes, image_height=4, image_width=4,
          resize_method=resize_method)
1313
1314
      return image_masks

1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
    if mask_dtype == tf.float32 and resize_method == 'bilinear':
      np_expected_image_masks = np.array([[[0, 0, 0, 0],
                                           [0, 0, 0.6111111, 0.16666669],
                                           [0, 0, 0.3888889, 0.83333337],
                                           [0, 0, 0, 0]]], dtype=np.float32)
    else:
      np_expected_image_masks = np.array([[[0, 0, 0, 0],
                                           [0, 0, 1, 0],
                                           [0, 0, 0, 1],
                                           [0, 0, 0, 0]]], dtype=mask_dtype_np)
1325
    np_image_masks = self.execute_cpu(graph_fn, [])
1326
    self.assertEqual(np_image_masks.dtype, mask_dtype_np)
1327
    self.assertAllClose(np_image_masks, np_expected_image_masks)
1328
1329


1330
class MergeBoxesWithMultipleLabelsTest(test_case.TestCase):
1331
1332

  def testMergeBoxesWithMultipleLabels(self):
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348

    def graph_fn():
      boxes = tf.constant(
          [[0.25, 0.25, 0.75, 0.75], [0.0, 0.0, 0.5, 0.75],
           [0.25, 0.25, 0.75, 0.75]],
          dtype=tf.float32)
      class_indices = tf.constant([0, 4, 2], dtype=tf.int32)
      class_confidences = tf.constant([0.8, 0.2, 0.1], dtype=tf.float32)
      num_classes = 5
      merged_boxes, merged_classes, merged_confidences, merged_box_indices = (
          ops.merge_boxes_with_multiple_labels(
              boxes, class_indices, class_confidences, num_classes))

      return (merged_boxes, merged_classes, merged_confidences,
              merged_box_indices)

1349
1350
1351
1352
    expected_merged_boxes = np.array(
        [[0.25, 0.25, 0.75, 0.75], [0.0, 0.0, 0.5, 0.75]], dtype=np.float32)
    expected_merged_classes = np.array(
        [[1, 0, 1, 0, 0], [0, 0, 0, 0, 1]], dtype=np.int32)
1353
1354
    expected_merged_confidences = np.array(
        [[0.8, 0, 0.1, 0, 0], [0, 0, 0, 0, 0.2]], dtype=np.float32)
1355
    expected_merged_box_indices = np.array([0, 1], dtype=np.int32)
1356
1357
1358
1359
1360
1361
1362
1363

    # Running on CPU only as tf.unique is not supported on TPU.
    (np_merged_boxes, np_merged_classes, np_merged_confidences,
     np_merged_box_indices) = self.execute_cpu(graph_fn, [])
    self.assertAllClose(np_merged_boxes, expected_merged_boxes)
    self.assertAllClose(np_merged_classes, expected_merged_classes)
    self.assertAllClose(np_merged_confidences, expected_merged_confidences)
    self.assertAllClose(np_merged_box_indices, expected_merged_box_indices)
1364
1365

  def testMergeBoxesWithMultipleLabelsCornerCase(self):
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381

    def graph_fn():
      boxes = tf.constant(
          [[0, 0, 1, 1], [0, 1, 1, 1], [1, 0, 1, 1], [1, 1, 1, 1],
           [1, 1, 1, 1], [1, 0, 1, 1], [0, 1, 1, 1], [0, 0, 1, 1]],
          dtype=tf.float32)
      class_indices = tf.constant([0, 1, 2, 3, 2, 1, 0, 3], dtype=tf.int32)
      class_confidences = tf.constant([0.1, 0.9, 0.2, 0.8, 0.3, 0.7, 0.4, 0.6],
                                      dtype=tf.float32)
      num_classes = 4
      merged_boxes, merged_classes, merged_confidences, merged_box_indices = (
          ops.merge_boxes_with_multiple_labels(
              boxes, class_indices, class_confidences, num_classes))
      return (merged_boxes, merged_classes, merged_confidences,
              merged_box_indices)

1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
    expected_merged_boxes = np.array(
        [[0, 0, 1, 1], [0, 1, 1, 1], [1, 0, 1, 1], [1, 1, 1, 1]],
        dtype=np.float32)
    expected_merged_classes = np.array(
        [[1, 0, 0, 1], [1, 1, 0, 0], [0, 1, 1, 0], [0, 0, 1, 1]],
        dtype=np.int32)
    expected_merged_confidences = np.array(
        [[0.1, 0, 0, 0.6], [0.4, 0.9, 0, 0],
         [0, 0.7, 0.2, 0], [0, 0, 0.3, 0.8]], dtype=np.float32)
    expected_merged_box_indices = np.array([0, 1, 2, 3], dtype=np.int32)
1392
1393
1394
1395
1396
1397
1398
1399
1400

    # Running on CPU only as tf.unique is not supported on TPU.
    (np_merged_boxes, np_merged_classes, np_merged_confidences,
     np_merged_box_indices) = self.execute_cpu(graph_fn, [])

    self.assertAllClose(np_merged_boxes, expected_merged_boxes)
    self.assertAllClose(np_merged_classes, expected_merged_classes)
    self.assertAllClose(np_merged_confidences, expected_merged_confidences)
    self.assertAllClose(np_merged_box_indices, expected_merged_box_indices)
1401
1402

  def testMergeBoxesWithEmptyInputs(self):
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421

    def graph_fn():
      boxes = tf.zeros([0, 4], dtype=tf.float32)
      class_indices = tf.constant([], dtype=tf.int32)
      class_confidences = tf.constant([], dtype=tf.float32)
      num_classes = 5
      merged_boxes, merged_classes, merged_confidences, merged_box_indices = (
          ops.merge_boxes_with_multiple_labels(
              boxes, class_indices, class_confidences, num_classes))
      return (merged_boxes, merged_classes, merged_confidences,
              merged_box_indices)

    # Running on CPU only as tf.unique is not supported on TPU.
    (np_merged_boxes, np_merged_classes, np_merged_confidences,
     np_merged_box_indices) = self.execute_cpu(graph_fn, [])
    self.assertAllEqual(np_merged_boxes.shape, [0, 4])
    self.assertAllEqual(np_merged_classes.shape, [0, 5])
    self.assertAllEqual(np_merged_confidences.shape, [0, 5])
    self.assertAllEqual(np_merged_box_indices.shape, [0])
1422

1423
  def testMergeBoxesWithMultipleLabelsUsesInt64(self):
1424
1425
1426
1427

    if self.is_tf2():
      self.skipTest('Getting op names is not supported in eager mode.')

1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
    boxes = tf.constant(
        [[0.25, 0.25, 0.75, 0.75], [0.0, 0.0, 0.5, 0.75],
         [0.25, 0.25, 0.75, 0.75]],
        dtype=tf.float32)
    class_indices = tf.constant([0, 4, 2], dtype=tf.int32)
    class_confidences = tf.constant([0.8, 0.2, 0.1], dtype=tf.float32)
    num_classes = 5
    ops.merge_boxes_with_multiple_labels(
        boxes, class_indices, class_confidences, num_classes)

    graph = tf.get_default_graph()

    def assert_dtype_is_int64(op_name):
      op = graph.get_operation_by_name(op_name)
      self.assertEqual(op.get_attr('dtype'), tf.int64)

    def assert_t_is_int64(op_name):
      op = graph.get_operation_by_name(op_name)
      self.assertEqual(op.get_attr('T'), tf.int64)

    assert_dtype_is_int64('map/TensorArray')
    assert_dtype_is_int64('map/TensorArray_1')
    assert_dtype_is_int64('map/while/TensorArrayReadV3')
    assert_t_is_int64('map/while/TensorArrayWrite/TensorArrayWriteV3')
    assert_t_is_int64(
        'map/TensorArrayUnstack/TensorArrayScatter/TensorArrayScatterV3')
    assert_dtype_is_int64('map/TensorArrayStack/TensorArrayGatherV3')

1456

1457
1458
class NearestNeighborUpsamplingTest(test_case.TestCase):

1459
  def test_upsampling_with_single_scale(self):
1460
1461
1462

    def graph_fn(inputs):
      custom_op_output = ops.nearest_neighbor_upsampling(inputs, scale=2)
1463
1464
1465
1466
1467
1468
1469
1470
1471
      return custom_op_output
    inputs = np.reshape(np.arange(4).astype(np.float32), [1, 2, 2, 1])
    custom_op_output = self.execute(graph_fn, [inputs])

    expected_output = [[[[0], [0], [1], [1]],
                        [[0], [0], [1], [1]],
                        [[2], [2], [3], [3]],
                        [[2], [2], [3], [3]]]]
    self.assertAllClose(custom_op_output, expected_output)
1472

1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
  def test_upsampling_with_separate_height_width_scales(self):

    def graph_fn(inputs):
      custom_op_output = ops.nearest_neighbor_upsampling(inputs,
                                                         height_scale=2,
                                                         width_scale=3)
      return custom_op_output
    inputs = np.reshape(np.arange(4).astype(np.float32), [1, 2, 2, 1])
    custom_op_output = self.execute(graph_fn, [inputs])

    expected_output = [[[[0], [0], [0], [1], [1], [1]],
                        [[0], [0], [0], [1], [1], [1]],
                        [[2], [2], [2], [3], [3], [3]],
                        [[2], [2], [2], [3], [3], [3]]]]
    self.assertAllClose(custom_op_output, expected_output)

1489

1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
class MatmulGatherOnZerothAxis(test_case.TestCase):

  def test_gather_2d(self):

    def graph_fn(params, indices):
      return ops.matmul_gather_on_zeroth_axis(params, indices)

    params = np.array([[1, 2, 3, 4],
                       [5, 6, 7, 8],
                       [9, 10, 11, 12],
                       [0, 1, 0, 0]], dtype=np.float32)
1501
    indices = np.array([2, 2, 1], dtype=np.int32)
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
    expected_output = np.array([[9, 10, 11, 12], [9, 10, 11, 12], [5, 6, 7, 8]])
    gather_output = self.execute(graph_fn, [params, indices])
    self.assertAllClose(gather_output, expected_output)

  def test_gather_3d(self):

    def graph_fn(params, indices):
      return ops.matmul_gather_on_zeroth_axis(params, indices)

    params = np.array([[[1, 2], [3, 4]],
                       [[5, 6], [7, 8]],
                       [[9, 10], [11, 12]],
                       [[0, 1], [0, 0]]], dtype=np.float32)
1515
    indices = np.array([0, 3, 1], dtype=np.int32)
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
    expected_output = np.array([[[1, 2], [3, 4]],
                                [[0, 1], [0, 0]],
                                [[5, 6], [7, 8]]])
    gather_output = self.execute(graph_fn, [params, indices])
    self.assertAllClose(gather_output, expected_output)

  def test_gather_with_many_indices(self):

    def graph_fn(params, indices):
      return ops.matmul_gather_on_zeroth_axis(params, indices)

    params = np.array([[1, 2, 3, 4],
                       [5, 6, 7, 8],
                       [9, 10, 11, 12],
                       [0, 1, 0, 0]], dtype=np.float32)
1531
    indices = np.array([0, 0, 0, 0, 0, 0], dtype=np.int32)
1532
1533
1534
1535
    expected_output = np.array(6*[[1, 2, 3, 4]])
    gather_output = self.execute(graph_fn, [params, indices])
    self.assertAllClose(gather_output, expected_output)

1536
  def test_gather_with_dynamic_shape_input(self):
1537
1538
1539
1540

    def graph_fn(params, indices):
      return ops.matmul_gather_on_zeroth_axis(params, indices)

1541
1542
1543
1544
1545
1546
    params = np.array([[1, 2, 3, 4],
                       [5, 6, 7, 8],
                       [9, 10, 11, 12],
                       [0, 1, 0, 0]], dtype=np.float32)
    indices = np.array([0, 0, 0, 0, 0, 0])
    expected_output = np.array(6*[[1, 2, 3, 4]])
1547
1548
    gather_output = self.execute(graph_fn, [params, indices])
    self.assertAllClose(gather_output, expected_output)
1549

1550

1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
class FpnFeatureLevelsTest(test_case.TestCase):

  def test_correct_fpn_levels(self):
    image_size = 640
    pretraininig_image_size = 224
    image_ratio = image_size * 1.0 / pretraininig_image_size
    boxes = np.array(
        [
            [
                [0, 0, 111, 111],  # Level 0.
                [0, 0, 113, 113],  # Level 1.
                [0, 0, 223, 223],  # Level 1.
                [0, 0, 225, 225],  # Level 2.
                [0, 0, 449, 449]   # Level 3.
            ],
        ],
        dtype=np.float32) / image_size

    def graph_fn(boxes):
      return ops.fpn_feature_levels(
          num_levels=5, unit_scale_index=2, image_ratio=image_ratio,
          boxes=boxes)

    levels = self.execute(graph_fn, [boxes])
    self.assertAllEqual([[0, 1, 1, 2, 3]], levels)
1576
1577


1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
class TestBfloat16ToFloat32(test_case.TestCase):

  def test_convert_list(self):
    var_list = [
        tf.constant([1.], dtype=tf.bfloat16),
        tf.constant([2], dtype=tf.int32)
    ]
    casted_var_list = ops.bfloat16_to_float32_nested(var_list)
    self.assertEqual(casted_var_list[0].dtype, tf.float32)
    self.assertEqual(casted_var_list[1].dtype, tf.int32)

  def test_convert_tensor_dict(self):
    tensor_dict = {
        'key1': tf.constant([1.], dtype=tf.bfloat16),
        'key2': [
            tf.constant([0.5], dtype=tf.bfloat16),
            tf.constant([7], dtype=tf.int32),
        ],
        'key3': tf.constant([2], dtype=tf.uint8),
    }
    tensor_dict = ops.bfloat16_to_float32_nested(tensor_dict)

    self.assertEqual(tensor_dict['key1'].dtype, tf.float32)
    self.assertEqual(tensor_dict['key2'][0].dtype, tf.float32)
    self.assertEqual(tensor_dict['key2'][1].dtype, tf.int32)
    self.assertEqual(tensor_dict['key3'].dtype, tf.uint8)


class TestGatherWithPaddingValues(test_case.TestCase):

  def test_gather_with_padding_values(self):
    expected_gathered_tensor = [
        [0, 0, 0.2, 0.2],
        [0, 0, 0, 0],
        [0, 0, 0.1, 0.1],
        [0, 0, 0, 0],
    ]
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629

    def graph_fn():
      indices = tf.constant([1, -1, 0, -1])
      input_tensor = tf.constant([[0, 0, 0.1, 0.1], [0, 0, 0.2, 0.2]],
                                 dtype=tf.float32)

      gathered_tensor = ops.gather_with_padding_values(
          input_tensor,
          indices=indices,
          padding_value=tf.zeros_like(input_tensor[0]))
      self.assertEqual(gathered_tensor.dtype, tf.float32)

      return gathered_tensor

    gathered_tensor_np = self.execute(graph_fn, [])
1630
1631
1632
    self.assertAllClose(expected_gathered_tensor, gathered_tensor_np)


Kaushik Shivakumar's avatar
Kaushik Shivakumar committed
1633
class TestGIoU(test_case.TestCase):
1634

Kaushik Shivakumar's avatar
Kaushik Shivakumar committed
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
  def test_giou(self):
    expected_giou_tensor = [
        1, 0, -1/3, 1/25, 1, -3/4
    ]

    def graph_fn():
      boxes1 = tf.constant([[3, 3, 5, 5], [3, 4, 5, 6],
                            [3, 3, 5, 5], [2, 1, 7, 6],
                            [1, 1, 1, 1], [0, 0, 0, 0]], dtype=tf.float32)
      boxes2 = tf.constant([[3, 3, 5, 5], [3, 2, 5, 4],
                                 [3, 7, 5, 9], [4, 3, 5, 4],
                                 [1, 1, 1, 1], [5, 5, 10, 10]], dtype=tf.float32)

      giou = ops.giou(boxes1, boxes2)
      self.assertEqual(giou.dtype, tf.float32)

      return giou

    giou = self.execute(graph_fn, [])
    self.assertAllClose(expected_giou_tensor, giou)


class TestCoordinateConversion(test_case.TestCase):

  def test_coord_conv(self):
    expected_box_tensor = [
        [0.5, 0.5, 5.5, 5.5], [2, 1, 4, 7]
    ]

    def graph_fn():
      boxes = tf.constant([[3, 3, 5, 5], [3, 4, 2, 6]], dtype=tf.float32)

      converted = ops.cy_cx_h_w_to_ymin_xmin_ymax_xmax_coords(boxes)
      self.assertEqual(converted.dtype, tf.float32)

      return converted
1671

Kaushik Shivakumar's avatar
Kaushik Shivakumar committed
1672
1673
    converted = self.execute(graph_fn, [])
    self.assertAllClose(expected_box_tensor, converted)
1674

1675
1676


1677
1678
if __name__ == '__main__':
  tf.test.main()