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