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/register_types.h"
21 #include "tensorflow/core/framework/tensor.h"
22 #include "tensorflow/core/framework/tensor_testutil.h"
23 #include "tensorflow/core/framework/tensor_util.h"
24 #include "tensorflow/core/framework/types.h"
25 #include "tensorflow/core/framework/types.pb.h"
26 #include "tensorflow/core/kernels/ops_testutil.h"
27 #include "tensorflow/core/kernels/ops_util.h"
28 #include "tensorflow/core/lib/core/status_test_util.h"
29 #include "tensorflow/core/lib/strings/str_util.h"
30 #include "tensorflow/core/platform/test.h"
31
32 namespace tensorflow {
33
34 class CropAndResizeOpTest : public OpsTestBase {
35 protected:
36 template <typename T>
MakeOp(float extrapolation_value,const string & method)37 void MakeOp(float extrapolation_value, const string& method) {
38 TF_EXPECT_OK(NodeDefBuilder("crop_and_resize_op", "CropAndResize")
39 .Input(FakeInput(DataTypeToEnum<T>::value))
40 .Input(FakeInput(DT_FLOAT))
41 .Input(FakeInput(DT_INT32))
42 .Input(FakeInput(DT_INT32))
43 .Attr("extrapolation_value", extrapolation_value)
44 .Attr("method", method)
45 .Finalize(node_def()));
46 TF_EXPECT_OK(InitOp());
47 }
48 };
49
50 #define REGISTER_TEST(T) \
51 TEST_F(CropAndResizeOpTest, TestCropAndResize##T) { \
52 MakeOp<T>(0, "bilinear"); \
53 AddInputFromArray<T>(TensorShape({1, 2, 2, 1}), {1, 2, 3, 4}); \
54 AddInputFromArray<float>(TensorShape({1, 4}), {0, 0, 1, 1}); \
55 AddInputFromArray<int32>(TensorShape({1}), {0}); \
56 AddInputFromArray<int32>(TensorShape({2}), {1, 1}); \
57 TF_ASSERT_OK(RunOpKernel()); \
58 \
59 Tensor expected(allocator(), DT_FLOAT, TensorShape({1, 1, 1, 1})); \
60 test::FillValues<float>(&expected, {2.5}); \
61 test::ExpectTensorEqual<float>(expected, *GetOutput(0)); \
62 } \
63 \
64 TEST_F(CropAndResizeOpTest, TestCropAndResize##T##nearest) { \
65 MakeOp<T>(0, "nearest"); \
66 AddInputFromArray<T>(TensorShape({1, 2, 2, 1}), {1, 2, 3, 4}); \
67 AddInputFromArray<float>(TensorShape({1, 4}), {0, 0, 1, 1}); \
68 AddInputFromArray<int32>(TensorShape({1}), {0}); \
69 AddInputFromArray<int32>(TensorShape({2}), {1, 1}); \
70 TF_ASSERT_OK(RunOpKernel()); \
71 \
72 Tensor expected(allocator(), DT_FLOAT, TensorShape({1, 1, 1, 1})); \
73 test::FillValues<float>(&expected, {4.0}); \
74 test::ExpectTensorEqual<float>(expected, *GetOutput(0)); \
75 }
76
77 REGISTER_TEST(float)
REGISTER_TEST(double)78 REGISTER_TEST(double)
79 REGISTER_TEST(uint8)
80 REGISTER_TEST(uint16)
81 REGISTER_TEST(int8)
82 REGISTER_TEST(int16)
83 REGISTER_TEST(int32)
84 REGISTER_TEST(int64)
85
86 #undef REGISTER_TEST
87
88 TEST_F(CropAndResizeOpTest, TestCropAndResize2x2To1x1Uint8) {
89 MakeOp<uint8>(0, "bilinear");
90 // Input:
91 // 1, 2
92 // 3, 4
93 AddInputFromArray<uint8>(TensorShape({1, 2, 2, 1}), {1, 2, 3, 4});
94 AddInputFromArray<float>(TensorShape({1, 4}), {0, 0, 1, 1});
95 AddInputFromArray<int32>(TensorShape({1}), {0});
96 AddInputFromArray<int32>(TensorShape({2}), {1, 1});
97 TF_ASSERT_OK(RunOpKernel());
98
99 Tensor expected(allocator(), DT_FLOAT, TensorShape({1, 1, 1, 1}));
100 test::FillValues<float>(&expected, {2.5});
101 test::ExpectTensorEqual<float>(expected, *GetOutput(0));
102 }
103
TEST_F(CropAndResizeOpTest,TestCropAndResize2x2To1x1Uint8NearestNeibor)104 TEST_F(CropAndResizeOpTest, TestCropAndResize2x2To1x1Uint8NearestNeibor) {
105 MakeOp<uint8>(0, "nearest");
106 // Input:
107 // 1, 2
108 // 3, 4
109 AddInputFromArray<uint8>(TensorShape({1, 2, 2, 1}), {1, 2, 3, 4});
110 AddInputFromArray<float>(TensorShape({1, 4}), {0, 0, 1, 1});
111 AddInputFromArray<int32>(TensorShape({1}), {0});
112 AddInputFromArray<int32>(TensorShape({2}), {1, 1});
113 TF_ASSERT_OK(RunOpKernel());
114
115 Tensor expected(allocator(), DT_FLOAT, TensorShape({1, 1, 1, 1}));
116 test::FillValues<float>(&expected, {4.0});
117 test::ExpectTensorEqual<float>(expected, *GetOutput(0));
118 }
119
TEST_F(CropAndResizeOpTest,TestCropAndResize2x2To1x1Flipped)120 TEST_F(CropAndResizeOpTest, TestCropAndResize2x2To1x1Flipped) {
121 MakeOp<float>(0, "bilinear");
122 // Input:
123 // 1, 2
124 // 3, 4
125 AddInputFromArray<float>(TensorShape({1, 2, 2, 1}), {1, 2, 3, 4});
126 AddInputFromArray<float>(TensorShape({1, 4}), {1, 1, 0, 0});
127 AddInputFromArray<int32>(TensorShape({1}), {0});
128 AddInputFromArray<int32>(TensorShape({2}), {1, 1});
129 TF_ASSERT_OK(RunOpKernel());
130
131 Tensor expected(allocator(), DT_FLOAT, TensorShape({1, 1, 1, 1}));
132 test::FillValues<float>(&expected, {2.5});
133 test::ExpectTensorEqual<float>(expected, *GetOutput(0));
134 }
135
TEST_F(CropAndResizeOpTest,TestCropAndResize2x2To1x1FlippedNearestNeighbor)136 TEST_F(CropAndResizeOpTest, TestCropAndResize2x2To1x1FlippedNearestNeighbor) {
137 MakeOp<float>(0, "nearest");
138 // Input:
139 // 1, 2
140 // 3, 4
141 AddInputFromArray<float>(TensorShape({1, 2, 2, 1}), {1, 2, 3, 4});
142 AddInputFromArray<float>(TensorShape({1, 4}), {1, 1, 0, 0});
143 AddInputFromArray<int32>(TensorShape({1}), {0});
144 AddInputFromArray<int32>(TensorShape({2}), {1, 1});
145 TF_ASSERT_OK(RunOpKernel());
146
147 Tensor expected(allocator(), DT_FLOAT, TensorShape({1, 1, 1, 1}));
148 test::FillValues<float>(&expected, {4.0});
149 test::ExpectTensorEqual<float>(expected, *GetOutput(0));
150 }
151
TEST_F(CropAndResizeOpTest,TestCropAndResize2x2To3x3)152 TEST_F(CropAndResizeOpTest, TestCropAndResize2x2To3x3) {
153 MakeOp<float>(0, "bilinear");
154 // Input:
155 // 1, 2
156 // 3, 4
157 AddInputFromArray<float>(TensorShape({1, 2, 2, 1}), {1, 2, 3, 4});
158 AddInputFromArray<float>(TensorShape({1, 4}), {0, 0, 1, 1});
159 AddInputFromArray<int32>(TensorShape({1}), {0});
160 AddInputFromArray<int32>(TensorShape({2}), {3, 3});
161 TF_ASSERT_OK(RunOpKernel());
162
163 Tensor expected(allocator(), DT_FLOAT, TensorShape({1, 3, 3, 1}));
164 // clang-format off
165 test::FillValues<float>(&expected,
166 {1, 1.5, 2,
167 2, 2.5, 3,
168 3, 3.5, 4});
169 // clang-format on
170 test::ExpectTensorEqual<float>(expected, *GetOutput(0));
171 }
172
TEST_F(CropAndResizeOpTest,TestCropAndResize2x2To3x3NearestNeighbor)173 TEST_F(CropAndResizeOpTest, TestCropAndResize2x2To3x3NearestNeighbor) {
174 MakeOp<float>(0, "nearest");
175 // Input:
176 // 1, 2
177 // 3, 4
178 AddInputFromArray<float>(TensorShape({1, 2, 2, 1}), {1, 2, 3, 4});
179 AddInputFromArray<float>(TensorShape({1, 4}), {0, 0, 1, 1});
180 AddInputFromArray<int32>(TensorShape({1}), {0});
181 AddInputFromArray<int32>(TensorShape({2}), {3, 3});
182 TF_ASSERT_OK(RunOpKernel());
183
184 Tensor expected(allocator(), DT_FLOAT, TensorShape({1, 3, 3, 1}));
185 // clang-format off
186 test::FillValues<float>(&expected,
187 {1, 2, 2,
188 3, 4, 4,
189 3, 4, 4});
190 // clang-format on
191 test::ExpectTensorEqual<float>(expected, *GetOutput(0));
192 }
193
TEST_F(CropAndResizeOpTest,TestCropAndResize2x2To3x3Flipped)194 TEST_F(CropAndResizeOpTest, TestCropAndResize2x2To3x3Flipped) {
195 MakeOp<float>(0, "bilinear");
196 // Input:
197 // 1, 2
198 // 3, 4
199 AddInputFromArray<float>(TensorShape({1, 2, 2, 1}), {1, 2, 3, 4});
200 AddInputFromArray<float>(TensorShape({1, 4}), {1, 1, 0, 0});
201 AddInputFromArray<int32>(TensorShape({1}), {0});
202 AddInputFromArray<int32>(TensorShape({2}), {3, 3});
203 TF_ASSERT_OK(RunOpKernel());
204
205 Tensor expected(allocator(), DT_FLOAT, TensorShape({1, 3, 3, 1}));
206 // clang-format off
207 test::FillValues<float>(&expected,
208 {4, 3.5, 3,
209 3, 2.5, 2,
210 2, 1.5, 1});
211 // clang-format on
212 test::ExpectTensorEqual<float>(expected, *GetOutput(0));
213 }
214
TEST_F(CropAndResizeOpTest,TestCropAndResize2x2To3x3FlippedNearestNeighbor)215 TEST_F(CropAndResizeOpTest, TestCropAndResize2x2To3x3FlippedNearestNeighbor) {
216 MakeOp<float>(0, "nearest");
217 // Input:
218 // 1, 2
219 // 3, 4
220 AddInputFromArray<float>(TensorShape({1, 2, 2, 1}), {1, 2, 3, 4});
221 AddInputFromArray<float>(TensorShape({1, 4}), {1, 1, 0, 0});
222 AddInputFromArray<int32>(TensorShape({1}), {0});
223 AddInputFromArray<int32>(TensorShape({2}), {3, 3});
224 TF_ASSERT_OK(RunOpKernel());
225
226 Tensor expected(allocator(), DT_FLOAT, TensorShape({1, 3, 3, 1}));
227 // clang-format off
228 test::FillValues<float>(&expected,
229 {4, 4, 3,
230 4, 4, 3,
231 2, 2, 1});
232 // clang-format on
233 test::ExpectTensorEqual<float>(expected, *GetOutput(0));
234 }
235
TEST_F(CropAndResizeOpTest,TestCropAndResize3x3To2x2)236 TEST_F(CropAndResizeOpTest, TestCropAndResize3x3To2x2) {
237 MakeOp<float>(0, "bilinear");
238 // Input:
239 // 1, 2, 3
240 // 4, 5, 6
241 // 7, 8, 9
242 AddInputFromArray<float>(TensorShape({1, 3, 3, 1}),
243 {1, 2, 3, 4, 5, 6, 7, 8, 9});
244 AddInputFromArray<float>(TensorShape({2, 4}), {0, 0, 1, 1, 0, 0, 0.5, 0.5});
245 AddInputFromArray<int32>(TensorShape({2}), {0, 0});
246 AddInputFromArray<int32>(TensorShape({2}), {2, 2});
247 TF_ASSERT_OK(RunOpKernel());
248
249 Tensor expected(allocator(), DT_FLOAT, TensorShape({2, 2, 2, 1}));
250
251 // clang-format off
252 test::FillValues<float>(&expected,
253 {1, 3,
254 7, 9,
255 1, 2,
256 4, 5});
257 // clang-format on
258 test::ExpectTensorEqual<float>(expected, *GetOutput(0));
259 }
260
TEST_F(CropAndResizeOpTest,TestCropAndResize3x3To2x2NearestNeighbor)261 TEST_F(CropAndResizeOpTest, TestCropAndResize3x3To2x2NearestNeighbor) {
262 MakeOp<float>(0, "nearest");
263 // Input:
264 // 1, 2, 3
265 // 4, 5, 6
266 // 7, 8, 9
267 AddInputFromArray<float>(TensorShape({1, 3, 3, 1}),
268 {1, 2, 3, 4, 5, 6, 7, 8, 9});
269 AddInputFromArray<float>(TensorShape({2, 4}), {0, 0, 1, 1, 0, 0, 0.5, 0.5});
270 AddInputFromArray<int32>(TensorShape({2}), {0, 0});
271 AddInputFromArray<int32>(TensorShape({2}), {2, 2});
272 TF_ASSERT_OK(RunOpKernel());
273
274 Tensor expected(allocator(), DT_FLOAT, TensorShape({2, 2, 2, 1}));
275
276 // clang-format off
277 test::FillValues<float>(&expected,
278 {1, 3,
279 7, 9,
280 1, 2,
281 4, 5});
282 // clang-format on
283 test::ExpectTensorEqual<float>(expected, *GetOutput(0));
284 }
285
TEST_F(CropAndResizeOpTest,TestCropAndResize3x3To2x2Flipped)286 TEST_F(CropAndResizeOpTest, TestCropAndResize3x3To2x2Flipped) {
287 MakeOp<float>(0, "bilinear");
288 // Input:
289 // 1, 2, 3
290 // 4, 5, 6
291 // 7, 8, 9
292 AddInputFromArray<float>(TensorShape({1, 3, 3, 1}),
293 {1, 2, 3, 4, 5, 6, 7, 8, 9});
294 AddInputFromArray<float>(TensorShape({2, 4}), {1, 1, 0, 0, 0.5, 0.5, 0, 0});
295 AddInputFromArray<int32>(TensorShape({2}), {0, 0});
296 AddInputFromArray<int32>(TensorShape({2}), {2, 2});
297 TF_ASSERT_OK(RunOpKernel());
298
299 Tensor expected(allocator(), DT_FLOAT, TensorShape({2, 2, 2, 1}));
300
301 // clang-format off
302 test::FillValues<float>(&expected,
303 {9, 7,
304 3, 1,
305 5, 4,
306 2, 1});
307 // clang-format on
308 test::ExpectTensorEqual<float>(expected, *GetOutput(0));
309 }
310
TEST_F(CropAndResizeOpTest,TestCropAndResize3x3To2x2FlippedNearestNeighbor)311 TEST_F(CropAndResizeOpTest, TestCropAndResize3x3To2x2FlippedNearestNeighbor) {
312 MakeOp<float>(0, "nearest");
313 // Input:
314 // 1, 2, 3
315 // 4, 5, 6
316 // 7, 8, 9
317 AddInputFromArray<float>(TensorShape({1, 3, 3, 1}),
318 {1, 2, 3, 4, 5, 6, 7, 8, 9});
319 AddInputFromArray<float>(TensorShape({2, 4}), {1, 1, 0, 0, 0.5, 0.5, 0, 0});
320 AddInputFromArray<int32>(TensorShape({2}), {0, 0});
321 AddInputFromArray<int32>(TensorShape({2}), {2, 2});
322 TF_ASSERT_OK(RunOpKernel());
323
324 Tensor expected(allocator(), DT_FLOAT, TensorShape({2, 2, 2, 1}));
325
326 // clang-format off
327 test::FillValues<float>(&expected,
328 {9, 7,
329 3, 1,
330 5, 4,
331 2, 1});
332 // clang-format on
333 test::ExpectTensorEqual<float>(expected, *GetOutput(0));
334 }
335
TEST_F(CropAndResizeOpTest,TestCropAndResize2x2To3x3Extrapolated)336 TEST_F(CropAndResizeOpTest, TestCropAndResize2x2To3x3Extrapolated) {
337 const float v = -1;
338 MakeOp<float>(v, "bilinear");
339 // Input:
340 // 1, 2
341 // 3, 4
342 AddInputFromArray<float>(TensorShape({1, 2, 2, 1}), {1, 2, 3, 4});
343 AddInputFromArray<float>(TensorShape({1, 4}), {-1, -1, 1, 1});
344 AddInputFromArray<int32>(TensorShape({1}), {0});
345 AddInputFromArray<int32>(TensorShape({2}), {3, 3});
346 TF_ASSERT_OK(RunOpKernel());
347
348 Tensor expected(allocator(), DT_FLOAT, TensorShape({1, 3, 3, 1}));
349 // clang-format off
350 test::FillValues<float>(&expected,
351 {v, v, v,
352 v, 1, 2,
353 v, 3, 4});
354 // clang-format on
355 test::ExpectTensorEqual<float>(expected, *GetOutput(0));
356 }
357
TEST_F(CropAndResizeOpTest,TestCropAndResize2x2To3x3NoCrop)358 TEST_F(CropAndResizeOpTest, TestCropAndResize2x2To3x3NoCrop) {
359 MakeOp<float>(0, "bilinear");
360 // Input:
361 // 1, 2
362 // 3, 4
363 AddInputFromArray<float>(TensorShape({1, 2, 2, 1}), {1, 2, 3, 4});
364 AddInputFromArray<float>(TensorShape({0, 4}), {});
365 AddInputFromArray<int32>(TensorShape({0}), {});
366 AddInputFromArray<int32>(TensorShape({2}), {3, 3});
367 TF_ASSERT_OK(RunOpKernel());
368
369 Tensor expected(allocator(), DT_FLOAT, TensorShape({0, 3, 3, 1}));
370 // clang-format off
371 test::FillValues<float>(&expected, {});
372 // clang-format on
373 test::ExpectTensorEqual<float>(expected, *GetOutput(0));
374 }
375
TEST_F(CropAndResizeOpTest,TestInvalidInputShape)376 TEST_F(CropAndResizeOpTest, TestInvalidInputShape) {
377 MakeOp<float>(0, "bilinear");
378 AddInputFromArray<float>(TensorShape({2, 2, 1}), {1, 2, 3, 4});
379 AddInputFromArray<float>(TensorShape({1, 4}), {0, 0, 1, 1});
380 AddInputFromArray<int32>(TensorShape({1}), {0});
381 AddInputFromArray<int32>(TensorShape({2}), {4, 4});
382 Status s = RunOpKernel();
383 ASSERT_FALSE(s.ok());
384 EXPECT_TRUE(absl::StrContains(s.ToString(), "input image must be 4-D")) << s;
385 }
386
TEST_F(CropAndResizeOpTest,TestInvalidBoxIndexShape)387 TEST_F(CropAndResizeOpTest, TestInvalidBoxIndexShape) {
388 MakeOp<float>(0, "bilinear");
389 AddInputFromArray<float>(TensorShape({1, 2, 2, 1}), {1, 2, 3, 4});
390 AddInputFromArray<float>(TensorShape({1, 4}), {0, 0, 1, 1});
391 AddInputFromArray<int32>(TensorShape({2}), {0, 0});
392 AddInputFromArray<int32>(TensorShape({2}), {4, 4});
393 Status s = RunOpKernel();
394 ASSERT_FALSE(s.ok());
395 EXPECT_TRUE(
396 absl::StrContains(s.ToString(), "box_index has incompatible shape"))
397 << s;
398 }
399
TEST_F(CropAndResizeOpTest,TestInvalidBoxIndex)400 TEST_F(CropAndResizeOpTest, TestInvalidBoxIndex) {
401 MakeOp<float>(0, "bilinear");
402 AddInputFromArray<float>(TensorShape({1, 2, 2, 1}), {1, 2, 3, 4});
403 AddInputFromArray<float>(TensorShape({1, 4}), {0, 0, 1, 1});
404 AddInputFromArray<int32>(TensorShape({1}), {1});
405 AddInputFromArray<int32>(TensorShape({2}), {3, 3});
406 Status s = RunOpKernel();
407 ASSERT_FALSE(s.ok());
408 EXPECT_TRUE(absl::StrContains(s.ToString(),
409 "box_index has values outside [0, batch_size)"))
410 << s;
411 }
412
TEST_F(CropAndResizeOpTest,TestWithSharding)413 TEST_F(CropAndResizeOpTest, TestWithSharding) {
414 MakeOp<float>(0, "bilinear");
415 // Generate a relatively large input (999x999) so that sharding happens.
416 const int kLength = 999; // Length of the input. Must use an odd number.
417 const int kHalf = (kLength + 1) / 2; // Half size for the cropped result.
418
419 // Input:
420 // 0, 1, 2, ..., 998
421 // 0, 1, 2, ..., 998
422 // ... (altogether 999 lines)
423 // 0, 1, 2, ..., 998
424 AddInput<float>(TensorShape({1, kLength, kLength, 1}),
425 [=](int i) -> float { return i % kLength; });
426 AddInputFromArray<float>(TensorShape({2, 4}),
427 {0, 0, 0.5, 0.5, 0.5, 0.5, 1, 1});
428 AddInputFromArray<int32>(TensorShape({2}), {0, 0});
429 AddInputFromArray<int32>(TensorShape({2}), {kHalf, kHalf});
430
431 TF_ASSERT_OK(RunOpKernel());
432
433 // Generate result tensor.
434 // Result 1:
435 // 0, 1, 2, ..., 499
436 // ... (altogether 500 lines)
437 // 0, 1, 2, ..., 499
438 Tensor result1(allocator(), DT_FLOAT, TensorShape({1, kHalf, kHalf, 1}));
439 test::FillFn<float>(&result1, [=](int i) -> float { return i % kHalf; });
440
441 // Result 2:
442 // 499, 500, 501, ..., 998
443 // ... (altogether 500 lines)
444 // 499, 500, 501, ..., 998
445 Tensor result2(allocator(), DT_FLOAT, TensorShape({1, kHalf, kHalf, 1}));
446 test::FillFn<float>(&result2,
447 [=](int i) -> float { return i % kHalf + kHalf - 1; });
448
449 // Expected result is the concat of the two tensors.
450 Tensor expected(allocator(), DT_FLOAT, TensorShape({2, kHalf, kHalf, 1}));
451 TF_ASSERT_OK(tensor::Concat({result1, result2}, &expected));
452
453 // Compare result.
454 test::ExpectTensorEqual<float>(expected, *GetOutput(0));
455 }
456
457 } // namespace tensorflow
458