• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #include "tensorflow/core/framework/allocator.h"
17 #include "tensorflow/core/framework/fake_input.h"
18 #include "tensorflow/core/framework/node_def_builder.h"
19 #include "tensorflow/core/framework/op_kernel.h"
20 #include "tensorflow/core/framework/tensor.h"
21 #include "tensorflow/core/framework/tensor_testutil.h"
22 #include "tensorflow/core/framework/types.h"
23 #include "tensorflow/core/framework/types.pb.h"
24 #include "tensorflow/core/kernels/ops_testutil.h"
25 #include "tensorflow/core/kernels/ops_util.h"
26 #include "tensorflow/core/lib/core/status_test_util.h"
27 #include "tensorflow/core/lib/strings/str_util.h"
28 #include "tensorflow/core/platform/test.h"
29 
30 namespace tensorflow {
31 
32 class NonMaxSuppressionOpTest : public OpsTestBase {
33  protected:
MakeOp(float iou_threshold)34   void MakeOp(float iou_threshold) {
35     TF_EXPECT_OK(NodeDefBuilder("non_max_suppression_op", "NonMaxSuppression")
36                      .Input(FakeInput(DT_FLOAT))
37                      .Input(FakeInput(DT_FLOAT))
38                      .Input(FakeInput(DT_INT32))
39                      .Attr("iou_threshold", iou_threshold)
40                      .Finalize(node_def()));
41     TF_EXPECT_OK(InitOp());
42   }
43 };
44 
TEST_F(NonMaxSuppressionOpTest,TestSelectFromThreeClusters)45 TEST_F(NonMaxSuppressionOpTest, TestSelectFromThreeClusters) {
46   MakeOp(.5);
47   AddInputFromArray<float>(
48       TensorShape({6, 4}),
49       {0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
50        0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
51   AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f});
52   AddInputFromArray<int>(TensorShape({}), {3});
53   TF_ASSERT_OK(RunOpKernel());
54 
55   Tensor expected(allocator(), DT_INT32, TensorShape({3}));
56   test::FillValues<int>(&expected, {3, 0, 5});
57   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
58 }
59 
TEST_F(NonMaxSuppressionOpTest,TestSelectFromThreeClustersFlippedCoordinates)60 TEST_F(NonMaxSuppressionOpTest, TestSelectFromThreeClustersFlippedCoordinates) {
61   MakeOp(.5);
62   AddInputFromArray<float>(TensorShape({6, 4}),
63                            {1, 1,  0, 0,  0, 0.1f,  1, 1.1f,  0, .9f, 1, -0.1f,
64                             0, 10, 1, 11, 1, 10.1f, 0, 11.1f, 1, 101, 0, 100});
65   AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f});
66   AddInputFromArray<int>(TensorShape({}), {3});
67   TF_ASSERT_OK(RunOpKernel());
68 
69   Tensor expected(allocator(), DT_INT32, TensorShape({3}));
70   test::FillValues<int>(&expected, {3, 0, 5});
71   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
72 }
73 
TEST_F(NonMaxSuppressionOpTest,TestSelectAtMostTwoBoxesFromThreeClusters)74 TEST_F(NonMaxSuppressionOpTest, TestSelectAtMostTwoBoxesFromThreeClusters) {
75   MakeOp(.5);
76   AddInputFromArray<float>(
77       TensorShape({6, 4}),
78       {0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
79        0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
80   AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f});
81   AddInputFromArray<int>(TensorShape({}), {2});
82   TF_ASSERT_OK(RunOpKernel());
83 
84   Tensor expected(allocator(), DT_INT32, TensorShape({2}));
85   test::FillValues<int>(&expected, {3, 0});
86   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
87 }
88 
TEST_F(NonMaxSuppressionOpTest,TestSelectWithNegativeScores)89 TEST_F(NonMaxSuppressionOpTest, TestSelectWithNegativeScores) {
90   MakeOp(.5);
91   AddInputFromArray<float>(
92       TensorShape({6, 4}),
93       {0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
94        0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
95   AddInputFromArray<float>(
96       TensorShape({6}), {.9f - 10.0f, .75f - 10.0f, .6f - 10.0f, .95f - 10.0f,
97                          .5f - 10.0f, .3f - 10.0f});
98   AddInputFromArray<int>(TensorShape({}), {6});
99   TF_ASSERT_OK(RunOpKernel());
100 
101   Tensor expected(allocator(), DT_INT32, TensorShape({3}));
102   test::FillValues<int>(&expected, {3, 0, 5});
103   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
104 }
105 
TEST_F(NonMaxSuppressionOpTest,TestFirstBoxDegenerate)106 TEST_F(NonMaxSuppressionOpTest, TestFirstBoxDegenerate) {
107   MakeOp(.5);
108   AddInputFromArray<float>(TensorShape({3, 4}),
109                            {0, 0, 0, 0, 1, 1, 2, 2, 2, 2, 3, 3});
110   AddInputFromArray<float>(TensorShape({3}), {.9f, .75f, .6f});
111   AddInputFromArray<int>(TensorShape({}), {3});
112   TF_ASSERT_OK(RunOpKernel());
113 
114   Tensor expected(allocator(), DT_INT32, TensorShape({3}));
115   test::FillValues<int>(&expected, {0, 1, 2});
116   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
117 }
118 
TEST_F(NonMaxSuppressionOpTest,TestSelectAtMostThirtyBoxesFromThreeClusters)119 TEST_F(NonMaxSuppressionOpTest, TestSelectAtMostThirtyBoxesFromThreeClusters) {
120   MakeOp(.5);
121   AddInputFromArray<float>(
122       TensorShape({6, 4}),
123       {0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
124        0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
125   AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f});
126   AddInputFromArray<int>(TensorShape({}), {30});
127   TF_ASSERT_OK(RunOpKernel());
128 
129   Tensor expected(allocator(), DT_INT32, TensorShape({3}));
130   test::FillValues<int>(&expected, {3, 0, 5});
131   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
132 }
133 
TEST_F(NonMaxSuppressionOpTest,TestSelectSingleBox)134 TEST_F(NonMaxSuppressionOpTest, TestSelectSingleBox) {
135   MakeOp(.5);
136   AddInputFromArray<float>(TensorShape({1, 4}), {0, 0, 1, 1});
137   AddInputFromArray<float>(TensorShape({1}), {.9f});
138   AddInputFromArray<int>(TensorShape({}), {3});
139   TF_ASSERT_OK(RunOpKernel());
140 
141   Tensor expected(allocator(), DT_INT32, TensorShape({1}));
142   test::FillValues<int>(&expected, {0});
143   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
144 }
145 
TEST_F(NonMaxSuppressionOpTest,TestSelectFromTenIdenticalBoxes)146 TEST_F(NonMaxSuppressionOpTest, TestSelectFromTenIdenticalBoxes) {
147   MakeOp(.5);
148 
149   int num_boxes = 10;
150   std::vector<float> corners(num_boxes * 4);
151   std::vector<float> scores(num_boxes);
152   for (int i = 0; i < num_boxes; ++i) {
153     corners[i * 4 + 0] = 0;
154     corners[i * 4 + 1] = 0;
155     corners[i * 4 + 2] = 1;
156     corners[i * 4 + 3] = 1;
157     scores[i] = .9;
158   }
159   AddInputFromArray<float>(TensorShape({num_boxes, 4}), corners);
160   AddInputFromArray<float>(TensorShape({num_boxes}), scores);
161   AddInputFromArray<int>(TensorShape({}), {3});
162   TF_ASSERT_OK(RunOpKernel());
163 
164   Tensor expected(allocator(), DT_INT32, TensorShape({1}));
165   test::FillValues<int>(&expected, {0});
166   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
167 }
168 
TEST_F(NonMaxSuppressionOpTest,TestInconsistentBoxAndScoreShapes)169 TEST_F(NonMaxSuppressionOpTest, TestInconsistentBoxAndScoreShapes) {
170   MakeOp(.5);
171   AddInputFromArray<float>(
172       TensorShape({6, 4}),
173       {0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
174        0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
175   AddInputFromArray<float>(TensorShape({5}), {.9f, .75f, .6f, .95f, .5f});
176   AddInputFromArray<int>(TensorShape({}), {30});
177   Status s = RunOpKernel();
178 
179   ASSERT_FALSE(s.ok());
180   EXPECT_TRUE(absl::StrContains(s.ToString(), "scores has incompatible shape"))
181       << s;
182 }
183 
TEST_F(NonMaxSuppressionOpTest,TestInvalidIOUThreshold)184 TEST_F(NonMaxSuppressionOpTest, TestInvalidIOUThreshold) {
185   MakeOp(1.2);
186   AddInputFromArray<float>(TensorShape({1, 4}), {0, 0, 1, 1});
187   AddInputFromArray<float>(TensorShape({1}), {.9f});
188   AddInputFromArray<int>(TensorShape({}), {3});
189   Status s = RunOpKernel();
190 
191   ASSERT_FALSE(s.ok());
192   EXPECT_TRUE(
193       absl::StrContains(s.ToString(), "iou_threshold must be in [0, 1]"))
194       << s;
195 }
196 
TEST_F(NonMaxSuppressionOpTest,TestEmptyInput)197 TEST_F(NonMaxSuppressionOpTest, TestEmptyInput) {
198   MakeOp(.5);
199   AddInputFromArray<float>(TensorShape({0, 4}), {});
200   AddInputFromArray<float>(TensorShape({0}), {});
201   AddInputFromArray<int>(TensorShape({}), {30});
202   TF_ASSERT_OK(RunOpKernel());
203 
204   Tensor expected(allocator(), DT_INT32, TensorShape({0}));
205   test::FillValues<int>(&expected, {});
206   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
207 }
208 
209 //
210 // NonMaxSuppressionV2Op Tests
211 //
212 
213 class NonMaxSuppressionV2OpTest : public OpsTestBase {
214  protected:
MakeOp()215   void MakeOp() {
216     TF_EXPECT_OK(NodeDefBuilder("non_max_suppression_op", "NonMaxSuppressionV2")
217                      .Input(FakeInput(DT_FLOAT))
218                      .Input(FakeInput(DT_FLOAT))
219                      .Input(FakeInput(DT_INT32))
220                      .Input(FakeInput(DT_FLOAT))
221                      .Finalize(node_def()));
222     TF_EXPECT_OK(InitOp());
223   }
224 };
225 
TEST_F(NonMaxSuppressionV2OpTest,TestSelectFromThreeClusters)226 TEST_F(NonMaxSuppressionV2OpTest, TestSelectFromThreeClusters) {
227   MakeOp();
228   AddInputFromArray<float>(
229       TensorShape({6, 4}),
230       {0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
231        0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
232   AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f});
233   AddInputFromArray<int>(TensorShape({}), {3});
234   AddInputFromArray<float>(TensorShape({}), {.5f});
235   TF_ASSERT_OK(RunOpKernel());
236 
237   Tensor expected(allocator(), DT_INT32, TensorShape({3}));
238   test::FillValues<int>(&expected, {3, 0, 5});
239   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
240 }
241 
TEST_F(NonMaxSuppressionV2OpTest,TestSelectFromThreeClustersFlippedCoordinates)242 TEST_F(NonMaxSuppressionV2OpTest,
243        TestSelectFromThreeClustersFlippedCoordinates) {
244   MakeOp();
245   AddInputFromArray<float>(TensorShape({6, 4}),
246                            {1, 1,  0, 0,  0, 0.1f,  1, 1.1f,  0, .9f, 1, -0.1f,
247                             0, 10, 1, 11, 1, 10.1f, 0, 11.1f, 1, 101, 0, 100});
248   AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f});
249   AddInputFromArray<int>(TensorShape({}), {3});
250   AddInputFromArray<float>(TensorShape({}), {.5f});
251   TF_ASSERT_OK(RunOpKernel());
252 
253   Tensor expected(allocator(), DT_INT32, TensorShape({3}));
254   test::FillValues<int>(&expected, {3, 0, 5});
255   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
256 }
257 
TEST_F(NonMaxSuppressionV2OpTest,TestSelectAtMostTwoBoxesFromThreeClusters)258 TEST_F(NonMaxSuppressionV2OpTest, TestSelectAtMostTwoBoxesFromThreeClusters) {
259   MakeOp();
260   AddInputFromArray<float>(
261       TensorShape({6, 4}),
262       {0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
263        0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
264   AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f});
265   AddInputFromArray<int>(TensorShape({}), {2});
266   AddInputFromArray<float>(TensorShape({}), {.5f});
267   TF_ASSERT_OK(RunOpKernel());
268 
269   Tensor expected(allocator(), DT_INT32, TensorShape({2}));
270   test::FillValues<int>(&expected, {3, 0});
271   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
272 }
273 
TEST_F(NonMaxSuppressionV2OpTest,TestSelectAtMostThirtyBoxesFromThreeClusters)274 TEST_F(NonMaxSuppressionV2OpTest,
275        TestSelectAtMostThirtyBoxesFromThreeClusters) {
276   MakeOp();
277   AddInputFromArray<float>(
278       TensorShape({6, 4}),
279       {0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
280        0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
281   AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f});
282   AddInputFromArray<int>(TensorShape({}), {30});
283   AddInputFromArray<float>(TensorShape({}), {.5f});
284   TF_ASSERT_OK(RunOpKernel());
285 
286   Tensor expected(allocator(), DT_INT32, TensorShape({3}));
287   test::FillValues<int>(&expected, {3, 0, 5});
288   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
289 }
290 
TEST_F(NonMaxSuppressionV2OpTest,TestSelectSingleBox)291 TEST_F(NonMaxSuppressionV2OpTest, TestSelectSingleBox) {
292   MakeOp();
293   AddInputFromArray<float>(TensorShape({1, 4}), {0, 0, 1, 1});
294   AddInputFromArray<float>(TensorShape({1}), {.9f});
295   AddInputFromArray<int>(TensorShape({}), {3});
296   AddInputFromArray<float>(TensorShape({}), {.5f});
297   TF_ASSERT_OK(RunOpKernel());
298 
299   Tensor expected(allocator(), DT_INT32, TensorShape({1}));
300   test::FillValues<int>(&expected, {0});
301   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
302 }
303 
TEST_F(NonMaxSuppressionV2OpTest,TestSelectFromTenIdenticalBoxes)304 TEST_F(NonMaxSuppressionV2OpTest, TestSelectFromTenIdenticalBoxes) {
305   MakeOp();
306 
307   int num_boxes = 10;
308   std::vector<float> corners(num_boxes * 4);
309   std::vector<float> scores(num_boxes);
310   for (int i = 0; i < num_boxes; ++i) {
311     corners[i * 4 + 0] = 0;
312     corners[i * 4 + 1] = 0;
313     corners[i * 4 + 2] = 1;
314     corners[i * 4 + 3] = 1;
315     scores[i] = .9;
316   }
317   AddInputFromArray<float>(TensorShape({num_boxes, 4}), corners);
318   AddInputFromArray<float>(TensorShape({num_boxes}), scores);
319   AddInputFromArray<int>(TensorShape({}), {3});
320   AddInputFromArray<float>(TensorShape({}), {.5f});
321   TF_ASSERT_OK(RunOpKernel());
322 
323   Tensor expected(allocator(), DT_INT32, TensorShape({1}));
324   test::FillValues<int>(&expected, {0});
325   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
326 }
327 
TEST_F(NonMaxSuppressionV2OpTest,TestInconsistentBoxAndScoreShapes)328 TEST_F(NonMaxSuppressionV2OpTest, TestInconsistentBoxAndScoreShapes) {
329   MakeOp();
330   AddInputFromArray<float>(
331       TensorShape({6, 4}),
332       {0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
333        0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
334   AddInputFromArray<float>(TensorShape({5}), {.9f, .75f, .6f, .95f, .5f});
335   AddInputFromArray<int>(TensorShape({}), {30});
336   AddInputFromArray<float>(TensorShape({}), {.5f});
337   Status s = RunOpKernel();
338 
339   ASSERT_FALSE(s.ok());
340   EXPECT_TRUE(absl::StrContains(s.ToString(), "scores has incompatible shape"))
341       << s;
342 }
343 
TEST_F(NonMaxSuppressionV2OpTest,TestInvalidIOUThreshold)344 TEST_F(NonMaxSuppressionV2OpTest, TestInvalidIOUThreshold) {
345   MakeOp();
346   AddInputFromArray<float>(TensorShape({1, 4}), {0, 0, 1, 1});
347   AddInputFromArray<float>(TensorShape({1}), {.9f});
348   AddInputFromArray<int>(TensorShape({}), {3});
349   AddInputFromArray<float>(TensorShape({}), {1.2f});
350   Status s = RunOpKernel();
351 
352   ASSERT_FALSE(s.ok());
353   EXPECT_TRUE(
354       absl::StrContains(s.ToString(), "iou_threshold must be in [0, 1]"))
355       << s;
356 }
357 
TEST_F(NonMaxSuppressionV2OpTest,TestEmptyInput)358 TEST_F(NonMaxSuppressionV2OpTest, TestEmptyInput) {
359   MakeOp();
360   AddInputFromArray<float>(TensorShape({0, 4}), {});
361   AddInputFromArray<float>(TensorShape({0}), {});
362   AddInputFromArray<int>(TensorShape({}), {30});
363   AddInputFromArray<float>(TensorShape({}), {.5f});
364   TF_ASSERT_OK(RunOpKernel());
365 
366   Tensor expected(allocator(), DT_INT32, TensorShape({0}));
367   test::FillValues<int>(&expected, {});
368   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
369 }
370 
371 //
372 // NonMaxSuppressionV3Op Tests
373 //
374 
375 class NonMaxSuppressionV3OpTest : public OpsTestBase {
376  protected:
MakeOp()377   void MakeOp() {
378     TF_EXPECT_OK(NodeDefBuilder("non_max_suppression_op", "NonMaxSuppressionV3")
379                      .Input(FakeInput(DT_FLOAT))
380                      .Input(FakeInput(DT_FLOAT))
381                      .Input(FakeInput(DT_INT32))
382                      .Input(FakeInput(DT_FLOAT))
383                      .Input(FakeInput(DT_FLOAT))
384                      .Finalize(node_def()));
385     TF_EXPECT_OK(InitOp());
386   }
387 };
388 
TEST_F(NonMaxSuppressionV3OpTest,TestSelectFromThreeClusters)389 TEST_F(NonMaxSuppressionV3OpTest, TestSelectFromThreeClusters) {
390   MakeOp();
391   AddInputFromArray<float>(
392       TensorShape({6, 4}),
393       {0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
394        0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
395   AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f});
396   AddInputFromArray<int>(TensorShape({}), {3});
397   AddInputFromArray<float>(TensorShape({}), {.5f});
398   AddInputFromArray<float>(TensorShape({}), {0.0f});
399   TF_ASSERT_OK(RunOpKernel());
400 
401   Tensor expected(allocator(), DT_INT32, TensorShape({3}));
402   test::FillValues<int>(&expected, {3, 0, 5});
403   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
404 }
405 
TEST_F(NonMaxSuppressionV3OpTest,TestSelectFromThreeClustersWithScoreThreshold)406 TEST_F(NonMaxSuppressionV3OpTest,
407        TestSelectFromThreeClustersWithScoreThreshold) {
408   MakeOp();
409   AddInputFromArray<float>(
410       TensorShape({6, 4}),
411       {0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
412        0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
413   AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f});
414   AddInputFromArray<int>(TensorShape({}), {3});
415   AddInputFromArray<float>(TensorShape({}), {0.5f});
416   AddInputFromArray<float>(TensorShape({}), {0.4f});
417   TF_ASSERT_OK(RunOpKernel());
418 
419   Tensor expected(allocator(), DT_INT32, TensorShape({2}));
420   test::FillValues<int>(&expected, {3, 0});
421   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
422 }
423 
TEST_F(NonMaxSuppressionV3OpTest,TestSelectFromThreeClustersWithScoreThresholdZeroScores)424 TEST_F(NonMaxSuppressionV3OpTest,
425        TestSelectFromThreeClustersWithScoreThresholdZeroScores) {
426   MakeOp();
427   AddInputFromArray<float>(
428       TensorShape({6, 4}),
429       {0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
430        0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
431   AddInputFromArray<float>(TensorShape({6}), {.1, 0, 0, .3, .2, -5.0});
432   // If we ask for more boxes than we actually expect to get back;
433   // should still only get 2 boxes back.
434   AddInputFromArray<int>(TensorShape({}), {6});
435   AddInputFromArray<float>(TensorShape({}), {0.5f});
436   AddInputFromArray<float>(TensorShape({}), {-3.0f});
437   TF_ASSERT_OK(RunOpKernel());
438 
439   Tensor expected(allocator(), DT_INT32, TensorShape({2}));
440   test::FillValues<int>(&expected, {3, 0});
441 
442   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
443 }
444 
TEST_F(NonMaxSuppressionV3OpTest,TestSelectFromThreeClustersFlippedCoordinates)445 TEST_F(NonMaxSuppressionV3OpTest,
446        TestSelectFromThreeClustersFlippedCoordinates) {
447   MakeOp();
448   AddInputFromArray<float>(TensorShape({6, 4}),
449                            {1, 1,  0, 0,  0, 0.1f,  1, 1.1f,  0, .9f, 1, -0.1f,
450                             0, 10, 1, 11, 1, 10.1f, 0, 11.1f, 1, 101, 0, 100});
451   AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f});
452   AddInputFromArray<int>(TensorShape({}), {3});
453   AddInputFromArray<float>(TensorShape({}), {.5f});
454   AddInputFromArray<float>(TensorShape({}), {0.0f});
455   TF_ASSERT_OK(RunOpKernel());
456 
457   Tensor expected(allocator(), DT_INT32, TensorShape({3}));
458   test::FillValues<int>(&expected, {3, 0, 5});
459   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
460 }
461 
TEST_F(NonMaxSuppressionV3OpTest,TestSelectAtMostTwoBoxesFromThreeClusters)462 TEST_F(NonMaxSuppressionV3OpTest, TestSelectAtMostTwoBoxesFromThreeClusters) {
463   MakeOp();
464   AddInputFromArray<float>(
465       TensorShape({6, 4}),
466       {0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
467        0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
468   AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f});
469   AddInputFromArray<int>(TensorShape({}), {2});
470   AddInputFromArray<float>(TensorShape({}), {.5f});
471   AddInputFromArray<float>(TensorShape({}), {0.0f});
472   TF_ASSERT_OK(RunOpKernel());
473 
474   Tensor expected(allocator(), DT_INT32, TensorShape({2}));
475   test::FillValues<int>(&expected, {3, 0});
476   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
477 }
478 
TEST_F(NonMaxSuppressionV3OpTest,TestSelectAtMostThirtyBoxesFromThreeClusters)479 TEST_F(NonMaxSuppressionV3OpTest,
480        TestSelectAtMostThirtyBoxesFromThreeClusters) {
481   MakeOp();
482   AddInputFromArray<float>(
483       TensorShape({6, 4}),
484       {0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
485        0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
486   AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f});
487   AddInputFromArray<int>(TensorShape({}), {30});
488   AddInputFromArray<float>(TensorShape({}), {.5f});
489   AddInputFromArray<float>(TensorShape({}), {0.0f});
490   TF_ASSERT_OK(RunOpKernel());
491 
492   Tensor expected(allocator(), DT_INT32, TensorShape({3}));
493   test::FillValues<int>(&expected, {3, 0, 5});
494   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
495 }
496 
TEST_F(NonMaxSuppressionV3OpTest,TestSelectSingleBox)497 TEST_F(NonMaxSuppressionV3OpTest, TestSelectSingleBox) {
498   MakeOp();
499   AddInputFromArray<float>(TensorShape({1, 4}), {0, 0, 1, 1});
500   AddInputFromArray<float>(TensorShape({1}), {.9f});
501   AddInputFromArray<int>(TensorShape({}), {3});
502   AddInputFromArray<float>(TensorShape({}), {.5f});
503   AddInputFromArray<float>(TensorShape({}), {0.0f});
504   TF_ASSERT_OK(RunOpKernel());
505 
506   Tensor expected(allocator(), DT_INT32, TensorShape({1}));
507   test::FillValues<int>(&expected, {0});
508   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
509 }
510 
TEST_F(NonMaxSuppressionV3OpTest,TestSelectFromTenIdenticalBoxes)511 TEST_F(NonMaxSuppressionV3OpTest, TestSelectFromTenIdenticalBoxes) {
512   MakeOp();
513 
514   int num_boxes = 10;
515   std::vector<float> corners(num_boxes * 4);
516   std::vector<float> scores(num_boxes);
517   for (int i = 0; i < num_boxes; ++i) {
518     corners[i * 4 + 0] = 0;
519     corners[i * 4 + 1] = 0;
520     corners[i * 4 + 2] = 1;
521     corners[i * 4 + 3] = 1;
522     scores[i] = .9;
523   }
524   AddInputFromArray<float>(TensorShape({num_boxes, 4}), corners);
525   AddInputFromArray<float>(TensorShape({num_boxes}), scores);
526   AddInputFromArray<int>(TensorShape({}), {3});
527   AddInputFromArray<float>(TensorShape({}), {.5f});
528   AddInputFromArray<float>(TensorShape({}), {0.0f});
529   TF_ASSERT_OK(RunOpKernel());
530 
531   Tensor expected(allocator(), DT_INT32, TensorShape({1}));
532   test::FillValues<int>(&expected, {0});
533   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
534 }
535 
TEST_F(NonMaxSuppressionV3OpTest,TestInconsistentBoxAndScoreShapes)536 TEST_F(NonMaxSuppressionV3OpTest, TestInconsistentBoxAndScoreShapes) {
537   MakeOp();
538   AddInputFromArray<float>(
539       TensorShape({6, 4}),
540       {0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
541        0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
542   AddInputFromArray<float>(TensorShape({5}), {.9f, .75f, .6f, .95f, .5f});
543   AddInputFromArray<int>(TensorShape({}), {30});
544   AddInputFromArray<float>(TensorShape({}), {.5f});
545   AddInputFromArray<float>(TensorShape({}), {0.0f});
546   Status s = RunOpKernel();
547 
548   ASSERT_FALSE(s.ok());
549   EXPECT_TRUE(absl::StrContains(s.ToString(), "scores has incompatible shape"))
550       << s;
551 }
552 
TEST_F(NonMaxSuppressionV3OpTest,TestInvalidIOUThreshold)553 TEST_F(NonMaxSuppressionV3OpTest, TestInvalidIOUThreshold) {
554   MakeOp();
555   AddInputFromArray<float>(TensorShape({1, 4}), {0, 0, 1, 1});
556   AddInputFromArray<float>(TensorShape({1}), {.9f});
557   AddInputFromArray<int>(TensorShape({}), {3});
558   AddInputFromArray<float>(TensorShape({}), {1.2f});
559   AddInputFromArray<float>(TensorShape({}), {0.0f});
560   Status s = RunOpKernel();
561 
562   ASSERT_FALSE(s.ok());
563   EXPECT_TRUE(
564       absl::StrContains(s.ToString(), "iou_threshold must be in [0, 1]"))
565       << s;
566 }
567 
TEST_F(NonMaxSuppressionV3OpTest,TestEmptyInput)568 TEST_F(NonMaxSuppressionV3OpTest, TestEmptyInput) {
569   MakeOp();
570   AddInputFromArray<float>(TensorShape({0, 4}), {});
571   AddInputFromArray<float>(TensorShape({0}), {});
572   AddInputFromArray<int>(TensorShape({}), {30});
573   AddInputFromArray<float>(TensorShape({}), {.5f});
574   AddInputFromArray<float>(TensorShape({}), {0.0f});
575   TF_ASSERT_OK(RunOpKernel());
576 
577   Tensor expected(allocator(), DT_INT32, TensorShape({0}));
578   test::FillValues<int>(&expected, {});
579   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
580 }
581 
582 //
583 // NonMaxSuppressionV4Op Tests
584 //
585 
586 class NonMaxSuppressionV4OpTest : public OpsTestBase {
587  protected:
MakeOp()588   void MakeOp() {
589     TF_EXPECT_OK(NodeDefBuilder("non_max_suppression_op", "NonMaxSuppressionV4")
590                      .Input(FakeInput(DT_FLOAT))
591                      .Input(FakeInput(DT_FLOAT))
592                      .Input(FakeInput(DT_INT32))
593                      .Input(FakeInput(DT_FLOAT))
594                      .Input(FakeInput(DT_FLOAT))
595                      .Attr("pad_to_max_output_size", true)
596                      .Finalize(node_def()));
597     TF_EXPECT_OK(InitOp());
598   }
599 };
600 
TEST_F(NonMaxSuppressionV4OpTest,TestSelectFromThreeClustersPadFive)601 TEST_F(NonMaxSuppressionV4OpTest, TestSelectFromThreeClustersPadFive) {
602   MakeOp();
603   AddInputFromArray<float>(
604       TensorShape({6, 4}),
605       {0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
606        0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
607   AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f});
608   AddInputFromArray<int>(TensorShape({}), {5});
609   AddInputFromArray<float>(TensorShape({}), {.5f});
610   AddInputFromArray<float>(TensorShape({}), {0.0f});
611   TF_ASSERT_OK(RunOpKernel());
612 
613   const auto expected_indices = test::AsTensor<int>({3, 0, 5, 0, 0});
614   test::ExpectTensorEqual<int>(expected_indices, *GetOutput(0));
615   Tensor expected_num_valid = test::AsScalar<int>(3);
616   test::ExpectTensorEqual<int>(expected_num_valid, *GetOutput(1));
617 }
618 
TEST_F(NonMaxSuppressionV4OpTest,TestSelectFromThreeClustersPadFiveScoreThr)619 TEST_F(NonMaxSuppressionV4OpTest, TestSelectFromThreeClustersPadFiveScoreThr) {
620   MakeOp();
621   AddInputFromArray<float>(
622       TensorShape({6, 4}),
623       {0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
624        0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
625   AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f});
626   AddInputFromArray<int>(TensorShape({}), {6});
627   AddInputFromArray<float>(TensorShape({}), {.5f});
628   AddInputFromArray<float>(TensorShape({}), {0.4f});
629   TF_ASSERT_OK(RunOpKernel());
630 
631   const auto expected_indices = test::AsTensor<int>({3, 0, 0, 0, 0, 0});
632   test::ExpectTensorEqual<int>(expected_indices, *GetOutput(0));
633   Tensor expected_num_valid = test::AsScalar<int>(2);
634   test::ExpectTensorEqual<int>(expected_num_valid, *GetOutput(1));
635 }
636 
637 //
638 // NonMaxSuppressionV5Op Tests
639 //
640 
641 class NonMaxSuppressionV5OpTest : public OpsTestBase {
642  protected:
MakeOp()643   void MakeOp() {
644     TF_EXPECT_OK(NodeDefBuilder("non_max_suppression_op", "NonMaxSuppressionV5")
645                      .Input(FakeInput(DT_FLOAT))
646                      .Input(FakeInput(DT_FLOAT))
647                      .Input(FakeInput(DT_INT32))
648                      .Input(FakeInput(DT_FLOAT))
649                      .Input(FakeInput(DT_FLOAT))
650                      .Input(FakeInput(DT_FLOAT))
651                      .Attr("pad_to_max_output_size", true)
652                      .Finalize(node_def()));
653     TF_EXPECT_OK(InitOp());
654   }
655 };
656 
TEST_F(NonMaxSuppressionV5OpTest,TestSelectFromThreeClustersPadFive)657 TEST_F(NonMaxSuppressionV5OpTest, TestSelectFromThreeClustersPadFive) {
658   MakeOp();
659   AddInputFromArray<float>(
660       TensorShape({6, 4}),
661       {0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
662        0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
663   AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f});
664   AddInputFromArray<int>(TensorShape({}), {5});
665   AddInputFromArray<float>(TensorShape({}), {.5f});
666   AddInputFromArray<float>(TensorShape({}), {0.0f});
667   AddInputFromArray<float>(TensorShape({}), {0.0f});
668   TF_ASSERT_OK(RunOpKernel());
669 
670   const auto expected_indices = test::AsTensor<int>({3, 0, 5, 0, 0});
671   test::ExpectTensorEqual<int>(expected_indices, *GetOutput(0));
672 
673   const auto expected_scores =
674       test::AsTensor<float>({.95f, .9f, .3f, 0.0f, 0.0f});
675   test::ExpectTensorNear<float>(expected_scores, *GetOutput(1), 1e-2);
676 
677   Tensor expected_num_valid = test::AsScalar<int>(3);
678   test::ExpectTensorEqual<int>(expected_num_valid, *GetOutput(2));
679 }
680 
TEST_F(NonMaxSuppressionV5OpTest,TestSelectFromThreeClustersWithSoftNMS)681 TEST_F(NonMaxSuppressionV5OpTest, TestSelectFromThreeClustersWithSoftNMS) {
682   // In the above TestSelectFromThreeClusters test, we select boxes with indices
683   // 3, 0, 5, where box 0 suppresses box 1 because of a high IOU overlap.
684   // In this test we have the same boxes and box 0 soft-suppresses box 1, but
685   // not enough to cause it to fall under `score_threshold` (which is 0.0) or
686   // the score of box 5, so in this test, box 1 ends up being selected before
687   // box 5.
688   MakeOp();
689   AddInputFromArray<float>(
690       TensorShape({6, 4}),
691       {0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
692        0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
693   AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f});
694   AddInputFromArray<int>(TensorShape({}), {6});
695   AddInputFromArray<float>(TensorShape({}), {0.5f});
696   AddInputFromArray<float>(TensorShape({}), {0.0f});
697   AddInputFromArray<float>(TensorShape({}), {0.5f});
698   TF_ASSERT_OK(RunOpKernel());
699 
700   Tensor expected(allocator(), DT_INT32, TensorShape({6}));
701   test::FillValues<int>(&expected, {3, 0, 1, 5, 4, 2});
702   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
703 
704   Tensor expected_scores(allocator(), DT_FLOAT, TensorShape({6}));
705   test::FillValues<float>(&expected_scores,
706                           {0.95, 0.9, 0.384, 0.3, 0.256, 0.197});
707   test::ExpectTensorNear<float>(expected_scores, *GetOutput(1), 1e-2);
708 
709   Tensor expected_num_valid = test::AsScalar<int>(6);
710   test::ExpectTensorEqual<int>(expected_num_valid, *GetOutput(2));
711 }
712 
713 //
714 // NonMaxSuppressionWithOverlapsOp Tests
715 //
716 
717 class NonMaxSuppressionWithOverlapsOpTest : public OpsTestBase {
718  protected:
MakeOp()719   void MakeOp() {
720     TF_EXPECT_OK(NodeDefBuilder("non_max_suppression_op",
721                                 "NonMaxSuppressionWithOverlaps")
722                      .Input(FakeInput(DT_FLOAT))
723                      .Input(FakeInput(DT_FLOAT))
724                      .Input(FakeInput(DT_INT32))
725                      .Input(FakeInput(DT_FLOAT))
726                      .Input(FakeInput(DT_FLOAT))
727                      .Finalize(node_def()));
728     TF_EXPECT_OK(InitOp());
729   }
730 
AddIoUInput(const std::vector<float> & boxes)731   void AddIoUInput(const std::vector<float>& boxes) {
732     ASSERT_EQ((boxes.size() % 4), 0);
733     size_t num_boxes = boxes.size() / 4;
734     std::vector<float> iou_overlaps(num_boxes * num_boxes);
735 
736     // compute the pairwise IoU overlaps
737     auto corner_access = [&boxes](size_t box_idx, size_t corner_idx) {
738       return boxes[box_idx * 4 + corner_idx];
739     };
740     for (size_t i = 0; i < num_boxes; ++i) {
741       for (size_t j = 0; j < num_boxes; ++j) {
742         const float ymin_i =
743             std::min<float>(corner_access(i, 0), corner_access(i, 2));
744         const float xmin_i =
745             std::min<float>(corner_access(i, 1), corner_access(i, 3));
746         const float ymax_i =
747             std::max<float>(corner_access(i, 0), corner_access(i, 2));
748         const float xmax_i =
749             std::max<float>(corner_access(i, 1), corner_access(i, 3));
750         const float ymin_j =
751             std::min<float>(corner_access(j, 0), corner_access(j, 2));
752         const float xmin_j =
753             std::min<float>(corner_access(j, 1), corner_access(j, 3));
754         const float ymax_j =
755             std::max<float>(corner_access(j, 0), corner_access(j, 2));
756         const float xmax_j =
757             std::max<float>(corner_access(j, 1), corner_access(j, 3));
758         const float area_i = (ymax_i - ymin_i) * (xmax_i - xmin_i);
759         const float area_j = (ymax_j - ymin_j) * (xmax_j - xmin_j);
760 
761         float iou;
762         if (area_i <= 0 || area_j <= 0) {
763           iou = 0.0;
764         } else {
765           const float intersection_ymin = std::max<float>(ymin_i, ymin_j);
766           const float intersection_xmin = std::max<float>(xmin_i, xmin_j);
767           const float intersection_ymax = std::min<float>(ymax_i, ymax_j);
768           const float intersection_xmax = std::min<float>(xmax_i, xmax_j);
769           const float intersection_area =
770               std::max<float>(intersection_ymax - intersection_ymin, 0.0) *
771               std::max<float>(intersection_xmax - intersection_xmin, 0.0);
772           iou = intersection_area / (area_i + area_j - intersection_area);
773         }
774         iou_overlaps[i * num_boxes + j] = iou;
775       }
776     }
777 
778     AddInputFromArray<float>(TensorShape({static_cast<signed>(num_boxes),
779                                           static_cast<signed>(num_boxes)}),
780                              iou_overlaps);
781   }
782 };
783 
TEST_F(NonMaxSuppressionWithOverlapsOpTest,TestSelectFromThreeClusters)784 TEST_F(NonMaxSuppressionWithOverlapsOpTest, TestSelectFromThreeClusters) {
785   MakeOp();
786   AddIoUInput({0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
787                0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
788   AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f});
789   AddInputFromArray<int>(TensorShape({}), {3});
790   AddInputFromArray<float>(TensorShape({}), {.5f});
791   AddInputFromArray<float>(TensorShape({}), {0.0f});
792   TF_ASSERT_OK(RunOpKernel());
793 
794   Tensor expected(allocator(), DT_INT32, TensorShape({3}));
795   test::FillValues<int>(&expected, {3, 0, 5});
796   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
797 }
798 
TEST_F(NonMaxSuppressionWithOverlapsOpTest,TestSelectFromThreeClustersFlippedCoordinates)799 TEST_F(NonMaxSuppressionWithOverlapsOpTest,
800        TestSelectFromThreeClustersFlippedCoordinates) {
801   MakeOp();
802   AddIoUInput({1, 1,  0, 0,  0, 0.1f,  1, 1.1f,  0, .9f, 1, -0.1f,
803                0, 10, 1, 11, 1, 10.1f, 0, 11.1f, 1, 101, 0, 100});
804   AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f});
805   AddInputFromArray<int>(TensorShape({}), {3});
806   AddInputFromArray<float>(TensorShape({}), {.5f});
807   AddInputFromArray<float>(TensorShape({}), {0.0f});
808   TF_ASSERT_OK(RunOpKernel());
809 
810   Tensor expected(allocator(), DT_INT32, TensorShape({3}));
811   test::FillValues<int>(&expected, {3, 0, 5});
812   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
813 }
814 
TEST_F(NonMaxSuppressionWithOverlapsOpTest,TestSelectAtMostTwoBoxesFromThreeClusters)815 TEST_F(NonMaxSuppressionWithOverlapsOpTest,
816        TestSelectAtMostTwoBoxesFromThreeClusters) {
817   MakeOp();
818   AddIoUInput({0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
819                0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
820   AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f});
821   AddInputFromArray<int>(TensorShape({}), {2});
822   AddInputFromArray<float>(TensorShape({}), {.5f});
823   AddInputFromArray<float>(TensorShape({}), {0.0f});
824   TF_ASSERT_OK(RunOpKernel());
825 
826   Tensor expected(allocator(), DT_INT32, TensorShape({2}));
827   test::FillValues<int>(&expected, {3, 0});
828   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
829 }
830 
TEST_F(NonMaxSuppressionWithOverlapsOpTest,TestSelectAtMostThirtyBoxesFromThreeClusters)831 TEST_F(NonMaxSuppressionWithOverlapsOpTest,
832        TestSelectAtMostThirtyBoxesFromThreeClusters) {
833   MakeOp();
834   AddIoUInput({0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
835                0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
836   AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f});
837   AddInputFromArray<int>(TensorShape({}), {30});
838   AddInputFromArray<float>(TensorShape({}), {.5f});
839   AddInputFromArray<float>(TensorShape({}), {0.0f});
840   TF_ASSERT_OK(RunOpKernel());
841 
842   Tensor expected(allocator(), DT_INT32, TensorShape({3}));
843   test::FillValues<int>(&expected, {3, 0, 5});
844   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
845 }
846 
TEST_F(NonMaxSuppressionWithOverlapsOpTest,TestSelectSingleBox)847 TEST_F(NonMaxSuppressionWithOverlapsOpTest, TestSelectSingleBox) {
848   MakeOp();
849   AddIoUInput({0, 0, 1, 1});
850   AddInputFromArray<float>(TensorShape({1}), {.9f});
851   AddInputFromArray<int>(TensorShape({}), {3});
852   AddInputFromArray<float>(TensorShape({}), {.5f});
853   AddInputFromArray<float>(TensorShape({}), {0.0f});
854   TF_ASSERT_OK(RunOpKernel());
855 
856   Tensor expected(allocator(), DT_INT32, TensorShape({1}));
857   test::FillValues<int>(&expected, {0});
858   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
859 }
860 
TEST_F(NonMaxSuppressionWithOverlapsOpTest,TestSelectFromTenIdenticalBoxes)861 TEST_F(NonMaxSuppressionWithOverlapsOpTest, TestSelectFromTenIdenticalBoxes) {
862   MakeOp();
863 
864   int num_boxes = 10;
865   std::vector<float> corners(num_boxes * 4);
866   std::vector<float> scores(num_boxes);
867   for (int i = 0; i < num_boxes; ++i) {
868     corners[i * 4 + 0] = 0;
869     corners[i * 4 + 1] = 0;
870     corners[i * 4 + 2] = 1;
871     corners[i * 4 + 3] = 1;
872     scores[i] = .9;
873   }
874   AddIoUInput(corners);
875   AddInputFromArray<float>(TensorShape({num_boxes}), scores);
876   AddInputFromArray<int>(TensorShape({}), {3});
877   AddInputFromArray<float>(TensorShape({}), {.5f});
878   AddInputFromArray<float>(TensorShape({}), {0.0f});
879   TF_ASSERT_OK(RunOpKernel());
880 
881   Tensor expected(allocator(), DT_INT32, TensorShape({1}));
882   test::FillValues<int>(&expected, {0});
883   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
884 }
885 
TEST_F(NonMaxSuppressionWithOverlapsOpTest,TestInconsistentBoxAndScoreShapes)886 TEST_F(NonMaxSuppressionWithOverlapsOpTest, TestInconsistentBoxAndScoreShapes) {
887   MakeOp();
888   AddIoUInput({0, 0,  1, 1,  0, 0.1f,  1, 1.1f,  0, -0.1f, 1, 0.9f,
889                0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100,   1, 101});
890   AddInputFromArray<float>(TensorShape({5}), {.9f, .75f, .6f, .95f, .5f});
891   AddInputFromArray<int>(TensorShape({}), {30});
892   AddInputFromArray<float>(TensorShape({}), {.5f});
893   AddInputFromArray<float>(TensorShape({}), {0.0f});
894   Status s = RunOpKernel();
895 
896   ASSERT_FALSE(s.ok());
897   EXPECT_TRUE(absl::StrContains(s.ToString(), "scores has incompatible shape"))
898       << s;
899 }
900 
TEST_F(NonMaxSuppressionWithOverlapsOpTest,TestInvalidOverlapsShape)901 TEST_F(NonMaxSuppressionWithOverlapsOpTest, TestInvalidOverlapsShape) {
902   MakeOp();
903   AddInputFromArray<float>(TensorShape({2, 3}), {0, 0, 0, 0, 0, 0});
904   AddInputFromArray<float>(TensorShape({2}), {0.5f, 0.5f});
905   AddInputFromArray<int>(TensorShape({}), {30});
906   AddInputFromArray<float>(TensorShape({}), {0.f});
907   AddInputFromArray<float>(TensorShape({}), {0.0f});
908   Status s = RunOpKernel();
909 
910   ASSERT_FALSE(s.ok());
911   EXPECT_TRUE(absl::StrContains(s.ToString(), "overlaps must be square")) << s;
912 }
913 
TEST_F(NonMaxSuppressionWithOverlapsOpTest,TestThresholdGreaterOne)914 TEST_F(NonMaxSuppressionWithOverlapsOpTest, TestThresholdGreaterOne) {
915   MakeOp();
916   AddIoUInput({0, 0, 1, 1});
917   AddInputFromArray<float>(TensorShape({1}), {.9f});
918   AddInputFromArray<int>(TensorShape({}), {3});
919   AddInputFromArray<float>(TensorShape({}), {1.2f});
920   AddInputFromArray<float>(TensorShape({}), {0.0f});
921   TF_ASSERT_OK(RunOpKernel());
922 }
923 
TEST_F(NonMaxSuppressionWithOverlapsOpTest,TestThresholdSmallerZero)924 TEST_F(NonMaxSuppressionWithOverlapsOpTest, TestThresholdSmallerZero) {
925   MakeOp();
926   AddIoUInput({0, 0, 1, 1});
927   AddInputFromArray<float>(TensorShape({1}), {.9f});
928   AddInputFromArray<int>(TensorShape({}), {3});
929   AddInputFromArray<float>(TensorShape({}), {-0.2f});
930   AddInputFromArray<float>(TensorShape({}), {0.0f});
931   TF_ASSERT_OK(RunOpKernel());
932 }
933 
TEST_F(NonMaxSuppressionWithOverlapsOpTest,TestEmptyInput)934 TEST_F(NonMaxSuppressionWithOverlapsOpTest, TestEmptyInput) {
935   MakeOp();
936   AddIoUInput({});
937   AddInputFromArray<float>(TensorShape({0}), {});
938   AddInputFromArray<int>(TensorShape({}), {30});
939   AddInputFromArray<float>(TensorShape({}), {.5f});
940   AddInputFromArray<float>(TensorShape({}), {0.0f});
941   TF_ASSERT_OK(RunOpKernel());
942 
943   Tensor expected(allocator(), DT_INT32, TensorShape({0}));
944   test::FillValues<int>(&expected, {});
945   test::ExpectTensorEqual<int>(expected, *GetOutput(0));
946 }
947 
948 class CombinedNonMaxSuppressionOpTest : public OpsTestBase {
949  protected:
MakeOp(bool pad_per_class=false,bool clip_boxes=true)950   void MakeOp(bool pad_per_class = false, bool clip_boxes = true) {
951     TF_EXPECT_OK(NodeDefBuilder("combined_non_max_suppression_op",
952                                 "CombinedNonMaxSuppression")
953                      .Input(FakeInput(DT_FLOAT))
954                      .Input(FakeInput(DT_FLOAT))
955                      .Input(FakeInput(DT_INT32))
956                      .Input(FakeInput(DT_INT32))
957                      .Input(FakeInput(DT_FLOAT))
958                      .Input(FakeInput(DT_FLOAT))
959                      .Attr("pad_per_class", pad_per_class)
960                      .Attr("clip_boxes", clip_boxes)
961                      .Finalize(node_def()));
962     TF_EXPECT_OK(InitOp());
963   }
964 };
965 
TEST_F(CombinedNonMaxSuppressionOpTest,TestEmptyInput)966 TEST_F(CombinedNonMaxSuppressionOpTest, TestEmptyInput) {
967   MakeOp();
968   AddInputFromArray<float>(TensorShape({0, 0, 0, 4}), {});
969   AddInputFromArray<float>(TensorShape({0, 0, 0}), {});
970   AddInputFromArray<int>(TensorShape({}), {30});
971   AddInputFromArray<int>(TensorShape({}), {10});
972   AddInputFromArray<float>(TensorShape({}), {.5f});
973   AddInputFromArray<float>(TensorShape({}), {0.0f});
974   TF_ASSERT_OK(RunOpKernel());
975 
976   // boxes
977   Tensor expected_boxes(allocator(), DT_FLOAT, TensorShape({0, 10, 4}));
978   test::FillValues<float>(&expected_boxes, {});
979   test::ExpectTensorEqual<float>(expected_boxes, *GetOutput(0));
980 
981   // scores
982   Tensor expected_scores(allocator(), DT_FLOAT, TensorShape({0, 10}));
983   test::FillValues<float>(&expected_scores, {});
984   test::ExpectTensorEqual<float>(expected_scores, *GetOutput(1));
985 
986   // classes
987   Tensor expected_classes(allocator(), DT_FLOAT, TensorShape({0, 10}));
988   test::FillValues<float>(&expected_classes, {});
989   test::ExpectTensorEqual<float>(expected_classes, *GetOutput(2));
990 
991   // valid
992   Tensor expected_valid_d(allocator(), DT_INT32, TensorShape({0}));
993   test::FillValues<int>(&expected_valid_d, {});
994   test::ExpectTensorEqual<int>(expected_valid_d, *GetOutput(3));
995 }
996 
TEST_F(CombinedNonMaxSuppressionOpTest,TestSelectFromThreeClusters)997 TEST_F(CombinedNonMaxSuppressionOpTest, TestSelectFromThreeClusters) {
998   MakeOp();
999   AddInputFromArray<float>(
1000       TensorShape({1, 6, 1, 4}),
1001       {0, 0,    0.1, 0.1, 0, 0.01f, 0.1, 0.11f, 0, -0.01, 0.1, 0.09f,
1002        0, 0.11, 0.1, 0.2, 0, 0.12f, 0.1, 0.21f, 0, 0.3,   1,   0.4});
1003   AddInputFromArray<float>(TensorShape({1, 6, 1}),
1004                            {.9f, .75f, .6f, .95f, .5f, .3f});
1005   AddInputFromArray<int>(TensorShape({}), {3});
1006   AddInputFromArray<int>(TensorShape({}), {3});
1007   AddInputFromArray<float>(TensorShape({}), {.5f});
1008   AddInputFromArray<float>(TensorShape({}), {0.0f});
1009   TF_ASSERT_OK(RunOpKernel());
1010 
1011   // boxes
1012   Tensor expected_boxes(allocator(), DT_FLOAT, TensorShape({1, 3, 4}));
1013   test::FillValues<float>(&expected_boxes,
1014                           {0, 0.11, 0.1, 0.2, 0, 0, 0.1, 0.1, 0, 0.3, 1, 0.4});
1015   test::ExpectTensorEqual<float>(expected_boxes, *GetOutput(0));
1016   // scores
1017   Tensor expected_scores(allocator(), DT_FLOAT, TensorShape({1, 3}));
1018   test::FillValues<float>(&expected_scores, {0.95, 0.9, 0.3});
1019   test::ExpectTensorEqual<float>(expected_scores, *GetOutput(1));
1020   // classes
1021   Tensor expected_classes(allocator(), DT_FLOAT, TensorShape({1, 3}));
1022   test::FillValues<float>(&expected_classes, {0, 0, 0});
1023   test::ExpectTensorEqual<float>(expected_classes, *GetOutput(2));
1024   // valid
1025   Tensor expected_valid_d(allocator(), DT_INT32, TensorShape({1}));
1026   test::FillValues<int>(&expected_valid_d, {3});
1027   test::ExpectTensorEqual<int>(expected_valid_d, *GetOutput(3));
1028 }
1029 
TEST_F(CombinedNonMaxSuppressionOpTest,TestSelectFromThreeClustersNoBoxClipping)1030 TEST_F(CombinedNonMaxSuppressionOpTest,
1031        TestSelectFromThreeClustersNoBoxClipping) {
1032   MakeOp(false, false);
1033   AddInputFromArray<float>(TensorShape({1, 6, 1, 4}),
1034                            {0, 0,  10, 10, 0, 1,  10, 11, 0, 1,  10,  9,
1035                             0, 11, 10, 20, 0, 12, 10, 21, 0, 30, 100, 40});
1036   AddInputFromArray<float>(TensorShape({1, 6, 1}),
1037                            {.9f, .75f, .6f, .95f, .5f, .3f});
1038   AddInputFromArray<int>(TensorShape({}), {3});
1039   AddInputFromArray<int>(TensorShape({}), {3});
1040   AddInputFromArray<float>(TensorShape({}), {.5f});
1041   AddInputFromArray<float>(TensorShape({}), {0.0f});
1042   TF_ASSERT_OK(RunOpKernel());
1043 
1044   // boxes
1045   Tensor expected_boxes(allocator(), DT_FLOAT, TensorShape({1, 3, 4}));
1046   test::FillValues<float>(&expected_boxes,
1047                           {0, 11, 10, 20, 0, 0, 10, 10, 0, 30, 100, 40});
1048   test::ExpectTensorEqual<float>(expected_boxes, *GetOutput(0));
1049   // scores
1050   Tensor expected_scores(allocator(), DT_FLOAT, TensorShape({1, 3}));
1051   test::FillValues<float>(&expected_scores, {0.95, 0.9, 0.3});
1052   test::ExpectTensorEqual<float>(expected_scores, *GetOutput(1));
1053   // classes
1054   Tensor expected_classes(allocator(), DT_FLOAT, TensorShape({1, 3}));
1055   test::FillValues<float>(&expected_classes, {0, 0, 0});
1056   test::ExpectTensorEqual<float>(expected_classes, *GetOutput(2));
1057   // valid
1058   Tensor expected_valid_d(allocator(), DT_INT32, TensorShape({1}));
1059   test::FillValues<int>(&expected_valid_d, {3});
1060   test::ExpectTensorEqual<int>(expected_valid_d, *GetOutput(3));
1061 }
1062 
TEST_F(CombinedNonMaxSuppressionOpTest,TestSelectFromThreeClustersWithScoreThreshold)1063 TEST_F(CombinedNonMaxSuppressionOpTest,
1064        TestSelectFromThreeClustersWithScoreThreshold) {
1065   MakeOp();
1066   AddInputFromArray<float>(
1067       TensorShape({1, 6, 1, 4}),
1068       {0, 0,    0.1, 0.1, 0, 0.01f, 0.1, 0.11f, 0, -0.01, 0.1, 0.09f,
1069        0, 0.11, 0.1, 0.2, 0, 0.12f, 0.1, 0.21f, 0, 0.3,   1,   0.4});
1070   AddInputFromArray<float>(TensorShape({1, 6, 1}),
1071                            {.9f, .75f, .6f, .95f, .5f, .3f});
1072   AddInputFromArray<int>(TensorShape({}), {3});
1073   AddInputFromArray<int>(TensorShape({}), {3});
1074   AddInputFromArray<float>(TensorShape({}), {.5f});
1075   AddInputFromArray<float>(TensorShape({}), {0.4f});
1076   TF_ASSERT_OK(RunOpKernel());
1077 
1078   // boxes
1079   Tensor expected_boxes(allocator(), DT_FLOAT, TensorShape({1, 3, 4}));
1080   test::FillValues<float>(&expected_boxes,
1081                           {0, 0.11, 0.1, 0.2, 0, 0, 0.1, 0.1, 0, 0, 0, 0});
1082   test::ExpectTensorEqual<float>(expected_boxes, *GetOutput(0));
1083   // scores
1084   Tensor expected_scores(allocator(), DT_FLOAT, TensorShape({1, 3}));
1085   test::FillValues<float>(&expected_scores, {0.95, 0.9, 0});
1086   test::ExpectTensorEqual<float>(expected_scores, *GetOutput(1));
1087   // classes
1088   Tensor expected_classes(allocator(), DT_FLOAT, TensorShape({1, 3}));
1089   test::FillValues<float>(&expected_classes, {0, 0, 0});
1090   test::ExpectTensorEqual<float>(expected_classes, *GetOutput(2));
1091   // valid
1092   Tensor expected_valid_d(allocator(), DT_INT32, TensorShape({1}));
1093   test::FillValues<int>(&expected_valid_d, {2});
1094   test::ExpectTensorEqual<int>(expected_valid_d, *GetOutput(3));
1095 }
1096 
TEST_F(CombinedNonMaxSuppressionOpTest,TestSelectFromThreeClustersWithScoreThresholdZeroScores)1097 TEST_F(CombinedNonMaxSuppressionOpTest,
1098        TestSelectFromThreeClustersWithScoreThresholdZeroScores) {
1099   MakeOp();
1100   AddInputFromArray<float>(
1101       TensorShape({1, 6, 1, 4}),
1102       {0, 0,    0.1, 0.1, 0, 0.01f, 0.1, 0.11f, 0, -0.01, 0.1, 0.09f,
1103        0, 0.11, 0.1, 0.2, 0, 0.12f, 0.1, 0.21f, 0, 0.3,   1,   0.4});
1104   AddInputFromArray<float>(TensorShape({1, 6, 1}),
1105                            {.1f, 0, 0, .3f, .2f, -5.0f});
1106   // If we ask for more boxes than we actually expect to get back;
1107   // should still only get 2 boxes back.
1108   AddInputFromArray<int>(TensorShape({}), {4});
1109   AddInputFromArray<int>(TensorShape({}), {5});
1110   AddInputFromArray<float>(TensorShape({}), {.5f});
1111   AddInputFromArray<float>(TensorShape({}), {-3.0f});
1112   TF_ASSERT_OK(RunOpKernel());
1113 
1114   // boxes
1115   Tensor expected_boxes(allocator(), DT_FLOAT, TensorShape({1, 5, 4}));
1116   test::FillValues<float>(
1117       &expected_boxes,
1118       {
1119           0, 0.11, 0.1, 0.2, 0, 0, 0.1, 0.1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1120       });
1121   test::ExpectTensorEqual<float>(expected_boxes, *GetOutput(0));
1122   // scores
1123   Tensor expected_scores(allocator(), DT_FLOAT, TensorShape({1, 5}));
1124   test::FillValues<float>(&expected_scores, {0.3, 0.1, 0, 0, 0});
1125   test::ExpectTensorEqual<float>(expected_scores, *GetOutput(1));
1126   // classes
1127   Tensor expected_classes(allocator(), DT_FLOAT, TensorShape({1, 5}));
1128   test::FillValues<float>(&expected_classes, {0, 0, 0, 0, 0});
1129   test::ExpectTensorEqual<float>(expected_classes, *GetOutput(2));
1130   // valid
1131   Tensor expected_valid_d(allocator(), DT_INT32, TensorShape({1}));
1132   test::FillValues<int>(&expected_valid_d, {2});
1133   test::ExpectTensorEqual<int>(expected_valid_d, *GetOutput(3));
1134 }
1135 
TEST_F(CombinedNonMaxSuppressionOpTest,TestSelectSingleBox)1136 TEST_F(CombinedNonMaxSuppressionOpTest, TestSelectSingleBox) {
1137   MakeOp();
1138   AddInputFromArray<float>(TensorShape({1, 1, 1, 4}), {0, 0, 1, 1});
1139   AddInputFromArray<float>(TensorShape({1, 1, 1}), {.9f});
1140   AddInputFromArray<int>(TensorShape({}), {3});
1141   AddInputFromArray<int>(TensorShape({}), {1});
1142   AddInputFromArray<float>(TensorShape({}), {.5f});
1143   AddInputFromArray<float>(TensorShape({}), {0.0f});
1144   TF_ASSERT_OK(RunOpKernel());
1145 
1146   // boxes
1147   Tensor expected_boxes(allocator(), DT_FLOAT, TensorShape({1, 1, 4}));
1148   test::FillValues<float>(&expected_boxes, {0, 0, 1, 1});
1149   test::ExpectTensorEqual<float>(expected_boxes, *GetOutput(0));
1150   // scores
1151   Tensor expected_scores(allocator(), DT_FLOAT, TensorShape({1, 1}));
1152   test::FillValues<float>(&expected_scores, {0.9});
1153   test::ExpectTensorEqual<float>(expected_scores, *GetOutput(1));
1154   // classes
1155   Tensor expected_classes(allocator(), DT_FLOAT, TensorShape({1, 1}));
1156   test::FillValues<float>(&expected_classes, {0});
1157   test::ExpectTensorEqual<float>(expected_classes, *GetOutput(2));
1158   // valid
1159   Tensor expected_valid_d(allocator(), DT_INT32, TensorShape({1}));
1160   test::FillValues<int>(&expected_valid_d, {1});
1161   test::ExpectTensorEqual<int>(expected_valid_d, *GetOutput(3));
1162 }
1163 
TEST_F(CombinedNonMaxSuppressionOpTest,TestSelectFromTwoBatchesWithScoreThreshold)1164 TEST_F(CombinedNonMaxSuppressionOpTest,
1165        TestSelectFromTwoBatchesWithScoreThreshold) {
1166   MakeOp();
1167   AddInputFromArray<float>(
1168       TensorShape({2, 6, 1, 4}),
1169       {0, 0,    0.1, 0.1, 0, 0.01f, 0.1, 0.11f, 0, -0.01, 0.1, 0.09f,
1170        0, 0.11, 0.1, 0.2, 0, 0.12f, 0.1, 0.21f, 0, 0.3,   1,   0.4,
1171        0, 0,    0.2, 0.2, 0, 0.02f, 0.2, 0.22f, 0, -0.02, 0.2, 0.19f,
1172        0, 0.21, 0.2, 0.3, 0, 0.22f, 0.2, 0.31f, 0, 0.4,   1,   0.5});
1173   AddInputFromArray<float>(
1174       TensorShape({2, 6, 1}),
1175       {.9f, .75f, .6f, .95f, .5f, .3f, .9f, .75f, .6f, .95f, .5f, .3f});
1176   AddInputFromArray<int>(TensorShape({}), {3});
1177   AddInputFromArray<int>(TensorShape({}), {3});
1178   AddInputFromArray<float>(TensorShape({}), {.5f});
1179   AddInputFromArray<float>(TensorShape({}), {0.4f});
1180   TF_ASSERT_OK(RunOpKernel());
1181 
1182   // boxes
1183   Tensor expected_boxes(allocator(), DT_FLOAT, TensorShape({2, 3, 4}));
1184   test::FillValues<float>(&expected_boxes,
1185                           {0, 0.11, 0.1, 0.2, 0, 0, 0.1, 0.1, 0, 0, 0, 0,
1186                            0, 0.21, 0.2, 0.3, 0, 0, 0.2, 0.2, 0, 0, 0, 0});
1187   test::ExpectTensorEqual<float>(expected_boxes, *GetOutput(0));
1188   // scores
1189   Tensor expected_scores(allocator(), DT_FLOAT, TensorShape({2, 3}));
1190   test::FillValues<float>(&expected_scores, {0.95, 0.9, 0, 0.95, 0.9, 0});
1191   test::ExpectTensorEqual<float>(expected_scores, *GetOutput(1));
1192   // classes
1193   Tensor expected_classes(allocator(), DT_FLOAT, TensorShape({2, 3}));
1194   test::FillValues<float>(&expected_classes, {0, 0, 0, 0, 0, 0});
1195   test::ExpectTensorEqual<float>(expected_classes, *GetOutput(2));
1196   // valid
1197   Tensor expected_valid_d(allocator(), DT_INT32, TensorShape({2}));
1198   test::FillValues<int>(&expected_valid_d, {2, 2});
1199   test::ExpectTensorEqual<int>(expected_valid_d, *GetOutput(3));
1200 }
1201 
TEST_F(CombinedNonMaxSuppressionOpTest,TestSelectFromTwoBatchesTwoClasses)1202 TEST_F(CombinedNonMaxSuppressionOpTest, TestSelectFromTwoBatchesTwoClasses) {
1203   MakeOp();
1204   AddInputFromArray<float>(
1205       TensorShape({2, 6, 1, 4}),
1206       {0, 0,    0.1, 0.1, 0, 0.01f, 0.1, 0.11f, 0, -0.01, 0.1, 0.09f,
1207        0, 0.11, 0.1, 0.2, 0, 0.12f, 0.1, 0.21f, 0, 0.3,   1,   0.4,
1208        0, 0,    0.2, 0.2, 0, 0.02f, 0.2, 0.22f, 0, -0.02, 0.2, 0.19f,
1209        0, 0.21, 0.2, 0.3, 0, 0.22f, 0.2, 0.31f, 0, 0.4,   1,   0.5});
1210   AddInputFromArray<float>(TensorShape({2, 6, 2}),
1211                            {0.1f, 0.9f, 0.75f, 0.8f, 0.6f, 0.3f, 0.95f, 0.1f,
1212                             0.5f, 0.5f, 0.3f,  0.1f, 0.1f, 0.9f, 0.75f, 0.8f,
1213                             0.6f, 0.3f, 0.95f, 0.1f, 0.5f, 0.5f, 0.3f,  0.1f});
1214   AddInputFromArray<int>(TensorShape({}), {3});
1215   AddInputFromArray<int>(TensorShape({}), {3});
1216   AddInputFromArray<float>(TensorShape({}), {.5f});
1217   AddInputFromArray<float>(TensorShape({}), {0.0f});
1218   TF_ASSERT_OK(RunOpKernel());
1219 
1220   // boxes
1221   Tensor expected_boxes(allocator(), DT_FLOAT, TensorShape({2, 3, 4}));
1222   test::FillValues<float>(
1223       &expected_boxes,
1224       {0, 0.11, 0.1, 0.2, 0, 0, 0.1, 0.1, 0, 0.01f, 0.1, 0.11f,
1225        0, 0.21, 0.2, 0.3, 0, 0, 0.2, 0.2, 0, 0.02f, 0.2, 0.22f});
1226   test::ExpectTensorEqual<float>(expected_boxes, *GetOutput(0));
1227   // scores
1228   Tensor expected_scores(allocator(), DT_FLOAT, TensorShape({2, 3}));
1229   test::FillValues<float>(&expected_scores, {0.95, 0.9, 0.75, 0.95, 0.9, 0.75});
1230   test::ExpectTensorEqual<float>(expected_scores, *GetOutput(1));
1231   // classes
1232   Tensor expected_classes(allocator(), DT_FLOAT, TensorShape({2, 3}));
1233   test::FillValues<float>(&expected_classes, {0, 1, 0, 0, 1, 0});
1234   test::ExpectTensorEqual<float>(expected_classes, *GetOutput(2));
1235   // valid
1236   Tensor expected_valid_d(allocator(), DT_INT32, TensorShape({2}));
1237   test::FillValues<int>(&expected_valid_d, {3, 3});
1238   test::ExpectTensorEqual<int>(expected_valid_d, *GetOutput(3));
1239 }
1240 
TEST_F(CombinedNonMaxSuppressionOpTest,TestSelectFromTwoBatchesTwoClassesWithScoreThreshold)1241 TEST_F(CombinedNonMaxSuppressionOpTest,
1242        TestSelectFromTwoBatchesTwoClassesWithScoreThreshold) {
1243   MakeOp();
1244   AddInputFromArray<float>(
1245       TensorShape({2, 6, 1, 4}),
1246       {0, 0,    0.1, 0.1, 0, 0.01f, 0.1, 0.11f, 0, -0.01, 0.1, 0.09f,
1247        0, 0.11, 0.1, 0.2, 0, 0.12f, 0.1, 0.21f, 0, 0.3,   1,   0.4,
1248        0, 0,    0.2, 0.2, 0, 0.02f, 0.2, 0.22f, 0, -0.02, 0.2, 0.19f,
1249        0, 0.21, 0.2, 0.3, 0, 0.22f, 0.2, 0.31f, 0, 0.4,   1,   0.5});
1250   AddInputFromArray<float>(TensorShape({2, 6, 2}),
1251                            {0.1f, 0.9f, 0.75f, 0.8f, 0.6f, 0.3f, 0.95f, 0.1f,
1252                             0.5f, 0.5f, 0.3f,  0.1f, 0.1f, 0.9f, 0.75f, 0.8f,
1253                             0.6f, 0.3f, 0.95f, 0.1f, 0.5f, 0.5f, 0.3f,  0.1f});
1254   AddInputFromArray<int>(TensorShape({}), {3});
1255   AddInputFromArray<int>(TensorShape({}), {3});
1256   AddInputFromArray<float>(TensorShape({}), {.5f});
1257   AddInputFromArray<float>(TensorShape({}), {0.8f});
1258   TF_ASSERT_OK(RunOpKernel());
1259 
1260   // boxes
1261   Tensor expected_boxes(allocator(), DT_FLOAT, TensorShape({2, 3, 4}));
1262   test::FillValues<float>(&expected_boxes,
1263                           {0, 0.11, 0.1, 0.2, 0, 0, 0.1, 0.1, 0, 0, 0, 0,
1264                            0, 0.21, 0.2, 0.3, 0, 0, 0.2, 0.2, 0, 0, 0, 0});
1265   test::ExpectTensorEqual<float>(expected_boxes, *GetOutput(0));
1266   // scores
1267   Tensor expected_scores(allocator(), DT_FLOAT, TensorShape({2, 3}));
1268   test::FillValues<float>(&expected_scores, {0.95, 0.9, 0, 0.95, 0.9, 0});
1269   test::ExpectTensorEqual<float>(expected_scores, *GetOutput(1));
1270   // classes
1271   Tensor expected_classes(allocator(), DT_FLOAT, TensorShape({2, 3}));
1272   test::FillValues<float>(&expected_classes, {0, 1, 0, 0, 1, 0});
1273   test::ExpectTensorEqual<float>(expected_classes, *GetOutput(2));
1274   // valid
1275   Tensor expected_valid_d(allocator(), DT_INT32, TensorShape({2}));
1276   test::FillValues<int>(&expected_valid_d, {2, 2});
1277   test::ExpectTensorEqual<int>(expected_valid_d, *GetOutput(3));
1278 }
1279 
TEST_F(CombinedNonMaxSuppressionOpTest,TestSelectFromTwoBatchesTwoClassesWithScoreThresholdPaddedTotalSize)1280 TEST_F(CombinedNonMaxSuppressionOpTest,
1281        TestSelectFromTwoBatchesTwoClassesWithScoreThresholdPaddedTotalSize) {
1282   MakeOp(true);
1283   AddInputFromArray<float>(
1284       TensorShape({2, 6, 1, 4}),
1285       {0, 0,    0.1, 0.1, 0, 0.01f, 0.1, 0.11f, 0, -0.01, 0.1, 0.09f,
1286        0, 0.11, 0.1, 0.2, 0, 0.12f, 0.1, 0.21f, 0, 0.3,   1,   0.4,
1287        0, 0,    0.2, 0.2, 0, 0.02f, 0.2, 0.22f, 0, -0.02, 0.2, 0.19f,
1288        0, 0.21, 0.2, 0.3, 0, 0.22f, 0.2, 0.31f, 0, 0.4,   1,   0.5});
1289   AddInputFromArray<float>(TensorShape({2, 6, 2}),
1290                            {0.1f, 0.9f, 0.75f, 0.8f, 0.6f, 0.3f, 0.95f, 0.1f,
1291                             0.5f, 0.5f, 0.3f,  0.1f, 0.1f, 0.9f, 0.75f, 0.8f,
1292                             0.6f, 0.3f, 0.95f, 0.1f, 0.5f, 0.5f, 0.3f,  0.1f});
1293   AddInputFromArray<int>(TensorShape({}), {10});
1294   AddInputFromArray<int>(TensorShape({}), {3});
1295   AddInputFromArray<float>(TensorShape({}), {.5f});
1296   AddInputFromArray<float>(TensorShape({}), {0.8f});
1297   TF_ASSERT_OK(RunOpKernel());
1298 
1299   // boxes
1300   Tensor expected_boxes(allocator(), DT_FLOAT, TensorShape({2, 3, 4}));
1301   test::FillValues<float>(&expected_boxes,
1302                           {0, 0.11, 0.1, 0.2, 0, 0, 0.1, 0.1, 0, 0, 0, 0,
1303                            0, 0.21, 0.2, 0.3, 0, 0, 0.2, 0.2, 0, 0, 0, 0});
1304   test::ExpectTensorEqual<float>(expected_boxes, *GetOutput(0));
1305   // scores
1306   Tensor expected_scores(allocator(), DT_FLOAT, TensorShape({2, 3}));
1307   test::FillValues<float>(&expected_scores, {0.95, 0.9, 0, 0.95, 0.9, 0});
1308   test::ExpectTensorEqual<float>(expected_scores, *GetOutput(1));
1309   // classes
1310   Tensor expected_classes(allocator(), DT_FLOAT, TensorShape({2, 3}));
1311   test::FillValues<float>(&expected_classes, {0, 1, 0, 0, 1, 0});
1312   test::ExpectTensorEqual<float>(expected_classes, *GetOutput(2));
1313   // valid
1314   Tensor expected_valid_d(allocator(), DT_INT32, TensorShape({2}));
1315   test::FillValues<int>(&expected_valid_d, {2, 2});
1316   test::ExpectTensorEqual<int>(expected_valid_d, *GetOutput(3));
1317 }
1318 
TEST_F(CombinedNonMaxSuppressionOpTest,TestSelectFromTwoBatchesTwoClassesWithScoreThresholdPaddedPerClass)1319 TEST_F(CombinedNonMaxSuppressionOpTest,
1320        TestSelectFromTwoBatchesTwoClassesWithScoreThresholdPaddedPerClass) {
1321   MakeOp(true);
1322   AddInputFromArray<float>(
1323       TensorShape({2, 6, 1, 4}),
1324       {0, 0,    0.1, 0.1, 0, 0.01f, 0.1, 0.11f, 0, -0.01, 0.1, 0.09f,
1325        0, 0.11, 0.1, 0.2, 0, 0.12f, 0.1, 0.21f, 0, 0.3,   1,   0.4,
1326        0, 0,    0.2, 0.2, 0, 0.02f, 0.2, 0.22f, 0, -0.02, 0.2, 0.19f,
1327        0, 0.21, 0.2, 0.3, 0, 0.22f, 0.2, 0.31f, 0, 0.4,   1,   0.5});
1328   AddInputFromArray<float>(TensorShape({2, 6, 2}),
1329                            {0.1f, 0.9f, 0.75f, 0.8f, 0.6f, 0.3f, 0.95f, 0.1f,
1330                             0.5f, 0.5f, 0.3f,  0.1f, 0.1f, 0.9f, 0.75f, 0.8f,
1331                             0.6f, 0.3f, 0.95f, 0.1f, 0.5f, 0.5f, 0.3f,  0.1f});
1332   AddInputFromArray<int>(TensorShape({}), {2});
1333   AddInputFromArray<int>(TensorShape({}), {50});
1334   AddInputFromArray<float>(TensorShape({}), {.5f});
1335   AddInputFromArray<float>(TensorShape({}), {0.8f});
1336   TF_ASSERT_OK(RunOpKernel());
1337 
1338   // boxes
1339   Tensor expected_boxes(allocator(), DT_FLOAT, TensorShape({2, 4, 4}));
1340   test::FillValues<float>(
1341       &expected_boxes,
1342       {0, 0.11, 0.1, 0.2, 0, 0, 0.1, 0.1, 0, 0, 0, 0, 0, 0, 0, 0,
1343        0, 0.21, 0.2, 0.3, 0, 0, 0.2, 0.2, 0, 0, 0, 0, 0, 0, 0, 0});
1344   test::ExpectTensorEqual<float>(expected_boxes, *GetOutput(0));
1345   // scores
1346   Tensor expected_scores(allocator(), DT_FLOAT, TensorShape({2, 4}));
1347   test::FillValues<float>(&expected_scores, {0.95, 0.9, 0, 0, 0.95, 0.9, 0, 0});
1348   test::ExpectTensorEqual<float>(expected_scores, *GetOutput(1));
1349   // classes
1350   Tensor expected_classes(allocator(), DT_FLOAT, TensorShape({2, 4}));
1351   test::FillValues<float>(&expected_classes, {0, 1, 0, 0, 0, 1, 0, 0});
1352   test::ExpectTensorEqual<float>(expected_classes, *GetOutput(2));
1353   // valid
1354   Tensor expected_valid_d(allocator(), DT_INT32, TensorShape({2}));
1355   test::FillValues<int>(&expected_valid_d, {2, 2});
1356   test::ExpectTensorEqual<int>(expected_valid_d, *GetOutput(3));
1357 }
1358 
TEST_F(CombinedNonMaxSuppressionOpTest,TestSelectFromTwoBatchesTwoClassesTotalSize)1359 TEST_F(CombinedNonMaxSuppressionOpTest,
1360        TestSelectFromTwoBatchesTwoClassesTotalSize) {
1361   MakeOp();
1362   AddInputFromArray<float>(
1363       TensorShape({2, 6, 1, 4}),
1364       {0, 0,    0.1, 0.1, 0, 0.01f, 0.1, 0.11f, 0, -0.01, 0.1, 0.09f,
1365        0, 0.11, 0.1, 0.2, 0, 0.12f, 0.1, 0.21f, 0, 0.3,   1,   0.4,
1366        0, 0,    0.2, 0.2, 0, 0.02f, 0.2, 0.22f, 0, -0.02, 0.2, 0.19f,
1367        0, 0.21, 0.2, 0.3, 0, 0.22f, 0.2, 0.31f, 0, 0.4,   1,   0.5});
1368   AddInputFromArray<float>(TensorShape({2, 6, 2}),
1369                            {0.1f, 0.9f, 0.75f, 0.8f, 0.6f, 0.3f, 0.95f, 0.1f,
1370                             0.5f, 0.5f, 0.3f,  0.1f, 0.1f, 0.9f, 0.75f, 0.8f,
1371                             0.6f, 0.3f, 0.95f, 0.1f, 0.5f, 0.5f, 0.3f,  0.1f});
1372   AddInputFromArray<int>(TensorShape({}), {3});
1373   // Total size per batch is more than size per class
1374   AddInputFromArray<int>(TensorShape({}), {5});
1375   AddInputFromArray<float>(TensorShape({}), {.5f});
1376   AddInputFromArray<float>(TensorShape({}), {0.1f});
1377   TF_ASSERT_OK(RunOpKernel());
1378 
1379   // boxes
1380   Tensor expected_boxes(allocator(), DT_FLOAT, TensorShape({2, 5, 4}));
1381   test::FillValues<float>(
1382       &expected_boxes, {0,   0.11,  0.1, 0.2,   0,   0,     0.1, 0.1, 0, 0.01f,
1383                         0.1, 0.11f, 0,   0.12f, 0.1, 0.21f, 0,   0.3, 1, 0.4,
1384                         0,   0.21,  0.2, 0.3,   0,   0,     0.2, 0.2, 0, 0.02f,
1385                         0.2, 0.22f, 0,   0.22f, 0.2, 0.31f, 0,   0.4, 1, 0.5});
1386   test::ExpectTensorEqual<float>(expected_boxes, *GetOutput(0));
1387   // scores
1388   Tensor expected_scores(allocator(), DT_FLOAT, TensorShape({2, 5}));
1389   test::FillValues<float>(
1390       &expected_scores, {0.95, 0.9, 0.75, 0.5, 0.3, 0.95, 0.9, 0.75, 0.5, 0.3});
1391   test::ExpectTensorEqual<float>(expected_scores, *GetOutput(1));
1392   // classes
1393   Tensor expected_classes(allocator(), DT_FLOAT, TensorShape({2, 5}));
1394   test::FillValues<float>(&expected_classes, {0, 1, 0, 1, 0, 0, 1, 0, 1, 0});
1395   test::ExpectTensorEqual<float>(expected_classes, *GetOutput(2));
1396   // valid
1397   Tensor expected_valid_d(allocator(), DT_INT32, TensorShape({2}));
1398   test::FillValues<int>(&expected_valid_d, {5, 5});
1399   test::ExpectTensorEqual<int>(expected_valid_d, *GetOutput(3));
1400 }
1401 
TEST_F(CombinedNonMaxSuppressionOpTest,TestSelectFromTwoBatchesTwoClassesForBoxesAndScores)1402 TEST_F(CombinedNonMaxSuppressionOpTest,
1403        TestSelectFromTwoBatchesTwoClassesForBoxesAndScores) {
1404   MakeOp();
1405   AddInputFromArray<float>(
1406       TensorShape({2, 6, 2, 4}),
1407       // batch 0, box1 of class 1 should get selected
1408       {0, 0, 0.1, 0.1, 0, 0, 0.1, 0.1, 0, 0.01f, 0.1, 0.11f, 0, 0.6f, 0.1, 0.7f,
1409        0, -0.01, 0.1, 0.09f, 0, -0.01, 0.1, 0.09f, 0, 0.11, 0.1, 0.2, 0, 0.11,
1410        0.1, 0.2, 0, 0.12f, 0.1, 0.21f, 0, 0.12f, 0.1, 0.21f, 0, 0.3, 1, 0.4, 0,
1411        0.3, 1, 0.4,
1412        // batch 1, box1 of class 0 should get selected
1413        0, 0, 0.2, 0.2, 0, 0, 0.2, 0.2, 0, 0.02f, 0.2, 0.22f, 0, 0.02f, 0.2,
1414        0.22f, 0, -0.02, 0.2, 0.19f, 0, -0.02, 0.2, 0.19f, 0, 0.21, 0.2, 0.3, 0,
1415        0.21, 0.2, 0.3, 0, 0.22f, 0.2, 0.31f, 0, 0.22f, 0.2, 0.31f, 0, 0.4, 1,
1416        0.5, 0, 0.4, 1, 0.5});
1417 
1418   AddInputFromArray<float>(TensorShape({2, 6, 2}),
1419                            {0.1f, 0.9f, 0.75f, 0.8f, 0.6f, 0.3f, 0.95f, 0.1f,
1420                             0.5f, 0.5f, 0.3f,  0.1f, 0.1f, 0.9f, 0.75f, 0.8f,
1421                             0.6f, 0.3f, 0.95f, 0.1f, 0.5f, 0.5f, 0.3f,  0.1f});
1422   AddInputFromArray<int>(TensorShape({}), {3});
1423   AddInputFromArray<int>(TensorShape({}), {3});
1424   AddInputFromArray<float>(TensorShape({}), {.5f});
1425   AddInputFromArray<float>(TensorShape({}), {0.0f});
1426   TF_ASSERT_OK(RunOpKernel());
1427 
1428   // boxes
1429   Tensor expected_boxes(allocator(), DT_FLOAT, TensorShape({2, 3, 4}));
1430   test::FillValues<float>(
1431       &expected_boxes,
1432       {0, 0.11, 0.1, 0.2, 0, 0, 0.1, 0.1, 0, 0.6f,  0.1, 0.7f,
1433        0, 0.21, 0.2, 0.3, 0, 0, 0.2, 0.2, 0, 0.02f, 0.2, 0.22f});
1434   test::ExpectTensorEqual<float>(expected_boxes, *GetOutput(0));
1435   // scores
1436   Tensor expected_scores(allocator(), DT_FLOAT, TensorShape({2, 3}));
1437   test::FillValues<float>(&expected_scores, {0.95, 0.9, 0.8, 0.95, 0.9, 0.75});
1438   test::ExpectTensorEqual<float>(expected_scores, *GetOutput(1));
1439   // classes
1440   Tensor expected_classes(allocator(), DT_FLOAT, TensorShape({2, 3}));
1441   test::FillValues<float>(&expected_classes, {0, 1, 1, 0, 1, 0});
1442   test::ExpectTensorEqual<float>(expected_classes, *GetOutput(2));
1443   // valid
1444   Tensor expected_valid_d(allocator(), DT_INT32, TensorShape({2}));
1445   test::FillValues<int>(&expected_valid_d, {3, 3});
1446   test::ExpectTensorEqual<int>(expected_valid_d, *GetOutput(3));
1447 }
1448 
1449 }  // namespace tensorflow
1450