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/platform/test.h" 28 29 namespace tensorflow { 30 31 class NonMaxSuppressionOpTest : public OpsTestBase { 32 protected: 33 void MakeOp(float iou_threshold) { 34 TF_EXPECT_OK(NodeDefBuilder("non_max_suppression_op", "NonMaxSuppression") 35 .Input(FakeInput(DT_FLOAT)) 36 .Input(FakeInput(DT_FLOAT)) 37 .Input(FakeInput(DT_INT32)) 38 .Attr("iou_threshold", iou_threshold) 39 .Finalize(node_def())); 40 TF_EXPECT_OK(InitOp()); 41 } 42 }; 43 44 TEST_F(NonMaxSuppressionOpTest, TestSelectFromThreeClusters) { 45 MakeOp(.5); 46 AddInputFromArray<float>( 47 TensorShape({6, 4}), 48 {0, 0, 1, 1, 0, 0.1f, 1, 1.1f, 0, -0.1f, 1, 0.9f, 49 0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100, 1, 101}); 50 AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f}); 51 AddInputFromArray<int>(TensorShape({}), {3}); 52 TF_ASSERT_OK(RunOpKernel()); 53 54 Tensor expected(allocator(), DT_INT32, TensorShape({3})); 55 test::FillValues<int>(&expected, {3, 0, 5}); 56 test::ExpectTensorEqual<int>(expected, *GetOutput(0)); 57 } 58 59 TEST_F(NonMaxSuppressionOpTest, TestSelectFromThreeClustersFlippedCoordinates) { 60 MakeOp(.5); 61 AddInputFromArray<float>(TensorShape({6, 4}), 62 {1, 1, 0, 0, 0, 0.1f, 1, 1.1f, 0, .9f, 1, -0.1f, 63 0, 10, 1, 11, 1, 10.1f, 0, 11.1f, 1, 101, 0, 100}); 64 AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f}); 65 AddInputFromArray<int>(TensorShape({}), {3}); 66 TF_ASSERT_OK(RunOpKernel()); 67 68 Tensor expected(allocator(), DT_INT32, TensorShape({3})); 69 test::FillValues<int>(&expected, {3, 0, 5}); 70 test::ExpectTensorEqual<int>(expected, *GetOutput(0)); 71 } 72 73 TEST_F(NonMaxSuppressionOpTest, TestSelectAtMostTwoBoxesFromThreeClusters) { 74 MakeOp(.5); 75 AddInputFromArray<float>( 76 TensorShape({6, 4}), 77 {0, 0, 1, 1, 0, 0.1f, 1, 1.1f, 0, -0.1f, 1, 0.9f, 78 0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100, 1, 101}); 79 AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f}); 80 AddInputFromArray<int>(TensorShape({}), {2}); 81 TF_ASSERT_OK(RunOpKernel()); 82 83 Tensor expected(allocator(), DT_INT32, TensorShape({2})); 84 test::FillValues<int>(&expected, {3, 0}); 85 test::ExpectTensorEqual<int>(expected, *GetOutput(0)); 86 } 87 88 TEST_F(NonMaxSuppressionOpTest, TestSelectAtMostThirtyBoxesFromThreeClusters) { 89 MakeOp(.5); 90 AddInputFromArray<float>( 91 TensorShape({6, 4}), 92 {0, 0, 1, 1, 0, 0.1f, 1, 1.1f, 0, -0.1f, 1, 0.9f, 93 0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100, 1, 101}); 94 AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f}); 95 AddInputFromArray<int>(TensorShape({}), {30}); 96 TF_ASSERT_OK(RunOpKernel()); 97 98 Tensor expected(allocator(), DT_INT32, TensorShape({3})); 99 test::FillValues<int>(&expected, {3, 0, 5}); 100 test::ExpectTensorEqual<int>(expected, *GetOutput(0)); 101 } 102 103 TEST_F(NonMaxSuppressionOpTest, TestSelectSingleBox) { 104 MakeOp(.5); 105 AddInputFromArray<float>(TensorShape({1, 4}), {0, 0, 1, 1}); 106 AddInputFromArray<float>(TensorShape({1}), {.9f}); 107 AddInputFromArray<int>(TensorShape({}), {3}); 108 TF_ASSERT_OK(RunOpKernel()); 109 110 Tensor expected(allocator(), DT_INT32, TensorShape({1})); 111 test::FillValues<int>(&expected, {0}); 112 test::ExpectTensorEqual<int>(expected, *GetOutput(0)); 113 } 114 115 TEST_F(NonMaxSuppressionOpTest, TestSelectFromTenIdenticalBoxes) { 116 MakeOp(.5); 117 118 int num_boxes = 10; 119 std::vector<float> corners(num_boxes * 4); 120 std::vector<float> scores(num_boxes); 121 for (int i = 0; i < num_boxes; ++i) { 122 corners[i * 4 + 0] = 0; 123 corners[i * 4 + 1] = 0; 124 corners[i * 4 + 2] = 1; 125 corners[i * 4 + 3] = 1; 126 scores[i] = .9; 127 } 128 AddInputFromArray<float>(TensorShape({num_boxes, 4}), corners); 129 AddInputFromArray<float>(TensorShape({num_boxes}), scores); 130 AddInputFromArray<int>(TensorShape({}), {3}); 131 TF_ASSERT_OK(RunOpKernel()); 132 133 Tensor expected(allocator(), DT_INT32, TensorShape({1})); 134 test::FillValues<int>(&expected, {0}); 135 test::ExpectTensorEqual<int>(expected, *GetOutput(0)); 136 } 137 138 TEST_F(NonMaxSuppressionOpTest, TestInconsistentBoxAndScoreShapes) { 139 MakeOp(.5); 140 AddInputFromArray<float>( 141 TensorShape({6, 4}), 142 {0, 0, 1, 1, 0, 0.1f, 1, 1.1f, 0, -0.1f, 1, 0.9f, 143 0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100, 1, 101}); 144 AddInputFromArray<float>(TensorShape({5}), {.9f, .75f, .6f, .95f, .5f}); 145 AddInputFromArray<int>(TensorShape({}), {30}); 146 Status s = RunOpKernel(); 147 148 ASSERT_FALSE(s.ok()); 149 EXPECT_TRUE( 150 StringPiece(s.ToString()).contains("scores has incompatible shape")) 151 << s; 152 } 153 154 TEST_F(NonMaxSuppressionOpTest, TestInvalidIOUThreshold) { 155 MakeOp(1.2); 156 AddInputFromArray<float>(TensorShape({1, 4}), {0, 0, 1, 1}); 157 AddInputFromArray<float>(TensorShape({1}), {.9f}); 158 AddInputFromArray<int>(TensorShape({}), {3}); 159 Status s = RunOpKernel(); 160 161 ASSERT_FALSE(s.ok()); 162 EXPECT_TRUE( 163 StringPiece(s.ToString()).contains("iou_threshold must be in [0, 1]")) 164 << s; 165 } 166 167 TEST_F(NonMaxSuppressionOpTest, TestEmptyInput) { 168 MakeOp(.5); 169 AddInputFromArray<float>(TensorShape({0, 4}), {}); 170 AddInputFromArray<float>(TensorShape({0}), {}); 171 AddInputFromArray<int>(TensorShape({}), {30}); 172 TF_ASSERT_OK(RunOpKernel()); 173 174 Tensor expected(allocator(), DT_INT32, TensorShape({0})); 175 test::FillValues<int>(&expected, {}); 176 test::ExpectTensorEqual<int>(expected, *GetOutput(0)); 177 } 178 179 // 180 // NonMaxSuppressionV2Op Tests 181 // 182 183 class NonMaxSuppressionV2OpTest : public OpsTestBase { 184 protected: 185 void MakeOp() { 186 TF_EXPECT_OK(NodeDefBuilder("non_max_suppression_op", "NonMaxSuppressionV2") 187 .Input(FakeInput(DT_FLOAT)) 188 .Input(FakeInput(DT_FLOAT)) 189 .Input(FakeInput(DT_INT32)) 190 .Input(FakeInput(DT_FLOAT)) 191 .Finalize(node_def())); 192 TF_EXPECT_OK(InitOp()); 193 } 194 }; 195 196 TEST_F(NonMaxSuppressionV2OpTest, TestSelectFromThreeClusters) { 197 MakeOp(); 198 AddInputFromArray<float>( 199 TensorShape({6, 4}), 200 {0, 0, 1, 1, 0, 0.1f, 1, 1.1f, 0, -0.1f, 1, 0.9f, 201 0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100, 1, 101}); 202 AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f}); 203 AddInputFromArray<int>(TensorShape({}), {3}); 204 AddInputFromArray<float>(TensorShape({}), {.5f}); 205 TF_ASSERT_OK(RunOpKernel()); 206 207 Tensor expected(allocator(), DT_INT32, TensorShape({3})); 208 test::FillValues<int>(&expected, {3, 0, 5}); 209 test::ExpectTensorEqual<int>(expected, *GetOutput(0)); 210 } 211 212 TEST_F(NonMaxSuppressionV2OpTest, 213 TestSelectFromThreeClustersFlippedCoordinates) { 214 MakeOp(); 215 AddInputFromArray<float>(TensorShape({6, 4}), 216 {1, 1, 0, 0, 0, 0.1f, 1, 1.1f, 0, .9f, 1, -0.1f, 217 0, 10, 1, 11, 1, 10.1f, 0, 11.1f, 1, 101, 0, 100}); 218 AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f}); 219 AddInputFromArray<int>(TensorShape({}), {3}); 220 AddInputFromArray<float>(TensorShape({}), {.5f}); 221 TF_ASSERT_OK(RunOpKernel()); 222 223 Tensor expected(allocator(), DT_INT32, TensorShape({3})); 224 test::FillValues<int>(&expected, {3, 0, 5}); 225 test::ExpectTensorEqual<int>(expected, *GetOutput(0)); 226 } 227 228 TEST_F(NonMaxSuppressionV2OpTest, TestSelectAtMostTwoBoxesFromThreeClusters) { 229 MakeOp(); 230 AddInputFromArray<float>( 231 TensorShape({6, 4}), 232 {0, 0, 1, 1, 0, 0.1f, 1, 1.1f, 0, -0.1f, 1, 0.9f, 233 0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100, 1, 101}); 234 AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f}); 235 AddInputFromArray<int>(TensorShape({}), {2}); 236 AddInputFromArray<float>(TensorShape({}), {.5f}); 237 TF_ASSERT_OK(RunOpKernel()); 238 239 Tensor expected(allocator(), DT_INT32, TensorShape({2})); 240 test::FillValues<int>(&expected, {3, 0}); 241 test::ExpectTensorEqual<int>(expected, *GetOutput(0)); 242 } 243 244 TEST_F(NonMaxSuppressionV2OpTest, 245 TestSelectAtMostThirtyBoxesFromThreeClusters) { 246 MakeOp(); 247 AddInputFromArray<float>( 248 TensorShape({6, 4}), 249 {0, 0, 1, 1, 0, 0.1f, 1, 1.1f, 0, -0.1f, 1, 0.9f, 250 0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100, 1, 101}); 251 AddInputFromArray<float>(TensorShape({6}), {.9f, .75f, .6f, .95f, .5f, .3f}); 252 AddInputFromArray<int>(TensorShape({}), {30}); 253 AddInputFromArray<float>(TensorShape({}), {.5f}); 254 TF_ASSERT_OK(RunOpKernel()); 255 256 Tensor expected(allocator(), DT_INT32, TensorShape({3})); 257 test::FillValues<int>(&expected, {3, 0, 5}); 258 test::ExpectTensorEqual<int>(expected, *GetOutput(0)); 259 } 260 261 TEST_F(NonMaxSuppressionV2OpTest, TestSelectSingleBox) { 262 MakeOp(); 263 AddInputFromArray<float>(TensorShape({1, 4}), {0, 0, 1, 1}); 264 AddInputFromArray<float>(TensorShape({1}), {.9f}); 265 AddInputFromArray<int>(TensorShape({}), {3}); 266 AddInputFromArray<float>(TensorShape({}), {.5f}); 267 TF_ASSERT_OK(RunOpKernel()); 268 269 Tensor expected(allocator(), DT_INT32, TensorShape({1})); 270 test::FillValues<int>(&expected, {0}); 271 test::ExpectTensorEqual<int>(expected, *GetOutput(0)); 272 } 273 274 TEST_F(NonMaxSuppressionV2OpTest, TestSelectFromTenIdenticalBoxes) { 275 MakeOp(); 276 277 int num_boxes = 10; 278 std::vector<float> corners(num_boxes * 4); 279 std::vector<float> scores(num_boxes); 280 for (int i = 0; i < num_boxes; ++i) { 281 corners[i * 4 + 0] = 0; 282 corners[i * 4 + 1] = 0; 283 corners[i * 4 + 2] = 1; 284 corners[i * 4 + 3] = 1; 285 scores[i] = .9; 286 } 287 AddInputFromArray<float>(TensorShape({num_boxes, 4}), corners); 288 AddInputFromArray<float>(TensorShape({num_boxes}), scores); 289 AddInputFromArray<int>(TensorShape({}), {3}); 290 AddInputFromArray<float>(TensorShape({}), {.5f}); 291 TF_ASSERT_OK(RunOpKernel()); 292 293 Tensor expected(allocator(), DT_INT32, TensorShape({1})); 294 test::FillValues<int>(&expected, {0}); 295 test::ExpectTensorEqual<int>(expected, *GetOutput(0)); 296 } 297 298 TEST_F(NonMaxSuppressionV2OpTest, TestInconsistentBoxAndScoreShapes) { 299 MakeOp(); 300 AddInputFromArray<float>( 301 TensorShape({6, 4}), 302 {0, 0, 1, 1, 0, 0.1f, 1, 1.1f, 0, -0.1f, 1, 0.9f, 303 0, 10, 1, 11, 0, 10.1f, 1, 11.1f, 0, 100, 1, 101}); 304 AddInputFromArray<float>(TensorShape({5}), {.9f, .75f, .6f, .95f, .5f}); 305 AddInputFromArray<int>(TensorShape({}), {30}); 306 AddInputFromArray<float>(TensorShape({}), {.5f}); 307 Status s = RunOpKernel(); 308 309 ASSERT_FALSE(s.ok()); 310 EXPECT_TRUE( 311 StringPiece(s.ToString()).contains("scores has incompatible shape")) 312 << s; 313 } 314 315 TEST_F(NonMaxSuppressionV2OpTest, TestInvalidIOUThreshold) { 316 MakeOp(); 317 AddInputFromArray<float>(TensorShape({1, 4}), {0, 0, 1, 1}); 318 AddInputFromArray<float>(TensorShape({1}), {.9f}); 319 AddInputFromArray<int>(TensorShape({}), {3}); 320 AddInputFromArray<float>(TensorShape({}), {1.2f}); 321 Status s = RunOpKernel(); 322 323 ASSERT_FALSE(s.ok()); 324 EXPECT_TRUE( 325 StringPiece(s.ToString()).contains("iou_threshold must be in [0, 1]")) 326 << s; 327 } 328 329 TEST_F(NonMaxSuppressionV2OpTest, TestEmptyInput) { 330 MakeOp(); 331 AddInputFromArray<float>(TensorShape({0, 4}), {}); 332 AddInputFromArray<float>(TensorShape({0}), {}); 333 AddInputFromArray<int>(TensorShape({}), {30}); 334 AddInputFromArray<float>(TensorShape({}), {.5f}); 335 TF_ASSERT_OK(RunOpKernel()); 336 337 Tensor expected(allocator(), DT_INT32, TensorShape({0})); 338 test::FillValues<int>(&expected, {}); 339 test::ExpectTensorEqual<int>(expected, *GetOutput(0)); 340 } 341 342 } // namespace tensorflow 343