Publishing 2019 R1 content
[platform/upstream/dldt.git] / inference-engine / tests / unit / opencv_test_gapi / common / gapi_core_tests_inl.hpp
1 // Copyright (C) 2018-2019 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 //
4
5 #ifndef OPENCV_GAPI_CORE_TESTS_INL_HPP
6 #define OPENCV_GAPI_CORE_TESTS_INL_HPP
7
8 #include "gapi_core_tests.hpp"
9
10 #include "blob_factory.hpp"
11 #include "blob_transform.hpp"
12 #include "ie_preprocess_data.hpp"
13
14 #include <opencv2/core.hpp>
15 #include <opencv2/imgproc.hpp>
16 #include <opencv2/gapi.hpp>
17
18 #include <cstdarg>
19 #include <cstdio>
20 #include <ctime>
21
22 #include <chrono>
23
24 #include <fluid_test_computations.hpp>
25
26 // Can be set externally (via CMake) if built with -DGAPI_TEST_PERF=ON
27 #ifndef PERF_TEST
28 #define PERF_TEST 0 // 1=test performance, 0=don't
29 #endif
30
31 namespace opencv_test
32 {
33
34 #if PERF_TEST
35 // performance test: iterate function, measure and print milliseconds per call
36 template<typename F> static void test_ms(F func, int iter, const char format[], ...)
37 {
38     using std::chrono::high_resolution_clock;
39
40     std::vector<high_resolution_clock::duration> samples(iter); samples.clear();
41     if (0 == iter)
42         return;
43
44     for (int i=0; i < iter; i++)
45     {
46         auto start = high_resolution_clock::now();
47         func(); // iterate calls
48         samples.push_back(high_resolution_clock::now() - start);
49     }
50
51     std::sort(samples.begin(), samples.end());
52
53     auto median = samples[samples.size() / 2];
54
55     double median_ms = std::chrono::duration_cast<std::chrono::microseconds>(median).count() * 0.001; // convert to milliseconds
56
57     printf("Performance(ms): %lg ", median_ms);
58
59     va_list args;
60     va_start(args, format);
61     vprintf(format, args);
62     va_end(args);
63
64     printf("\n");
65 }
66
67 static cv::String interpToString(int interp)
68 {
69     switch(interp)
70     {
71     case cv::INTER_AREA   : return "INTER_AREA";
72     case cv::INTER_LINEAR : return "INTER_LINEAR";
73     case cv::INTER_NEAREST: return "INTER_NEAREST";
74     }
75     CV_Assert(!"ERROR: unsupported interpolation!");
76     return nullptr;
77 }
78
79 static cv::String depthToString(int depth)
80 {
81     switch(depth)
82     {
83     case CV_8U  : return "CV_8U";
84     case CV_32F : return "CV_32F";
85     }
86     CV_Assert(!"ERROR: unsupported depth!");
87     return nullptr;
88 }
89
90 static cv::String typeToString(int type)
91 {
92     switch(type)
93     {
94     case CV_8UC1  : return "CV_8UC1";
95     case CV_8UC2  : return "CV_8UC2";
96     case CV_8UC3  : return "CV_8UC3";
97     case CV_8UC4  : return "CV_8UC4";
98     case CV_32FC1 : return "CV_32FC1";
99     case CV_32FC2 : return "CV_32FC2";
100     case CV_32FC3 : return "CV_32FC3";
101     case CV_32FC4 : return "CV_32FC4";
102     }
103     CV_Assert(!"ERROR: unsupported type!");
104     return nullptr;
105 }
106 #endif  // PERF_TEST
107
108 namespace {
109
110 test::Mat to_test(cv::Mat& mat) { return {mat.rows, mat.cols, mat.type(), mat.data}; }
111 std::vector<test::Mat> to_test(std::vector<cv::Mat>& mats)
112 {
113     std::vector<test::Mat> test_mats(mats.size());
114     for (int i = 0; i < mats.size(); i++) {
115         test_mats[i] = to_test(mats[i]);
116     }
117     return test_mats;
118 }
119
120 } // anonymous namespace
121
122 TEST_P(ResizeTestGAPI, AccuracyTest)
123 {
124     int type = 0, interp = 0;
125     cv::Size sz_in, sz_out;
126     double tolerance = 0.0;
127     std::pair<cv::Size, cv::Size> sizes;
128     std::tie(type, interp, sizes, tolerance) = GetParam();
129     std::tie(sz_in, sz_out) = sizes;
130
131     cv::Mat in_mat1 (sz_in, type );
132     cv::Scalar mean = cv::Scalar::all(127);
133     cv::Scalar stddev = cv::Scalar::all(40.f);
134
135     cv::randn(in_mat1, mean, stddev);
136
137     cv::Mat out_mat(sz_out, type);
138     cv::Mat out_mat_ocv(sz_out, type);
139
140     // G-API code //////////////////////////////////////////////////////////////
141     FluidResizeComputation rc(to_test(in_mat1), to_test(out_mat), interp);
142     rc.warmUp();
143
144 #if PERF_TEST
145     // iterate testing, and print performance
146     test_ms([&](){ rc.apply(); },
147             100, "Resize GAPI %s %s %dx%d -> %dx%d",
148             interpToString(interp).c_str(), typeToString(type).c_str(),
149             sz_in.width, sz_in.height, sz_out.width, sz_out.height);
150 #endif
151
152     // OpenCV code /////////////////////////////////////////////////////////////
153     {
154         cv::resize(in_mat1, out_mat_ocv, sz_out, 0, 0, interp);
155     }
156     // Comparison //////////////////////////////////////////////////////////////
157     {
158         cv::Mat absDiff;
159         cv::absdiff(out_mat, out_mat_ocv, absDiff);
160         EXPECT_EQ(0, cv::countNonZero(absDiff > tolerance));
161     }
162 }
163
164 TEST_P(SplitTestGAPI, AccuracyTest)
165 {
166     const auto params = GetParam();
167     int planes  = std::get<0>(params);
168     int depth   = std::get<1>(params);
169     cv::Size sz = std::get<2>(params);
170
171     int srcType = CV_MAKE_TYPE(depth, planes);
172     int dstType = CV_MAKE_TYPE(depth, 1);
173
174     cv::Mat in_mat(sz, srcType);
175     cv::randn(in_mat, cv::Scalar::all(127), cv::Scalar::all(40.f));
176
177     std::vector<cv::Mat> out_mats_gapi(planes, cv::Mat::zeros(sz, dstType));
178     std::vector<cv::Mat> out_mats_ocv (planes, cv::Mat::zeros(sz, dstType));
179
180     // G-API code //////////////////////////////////////////////////////////////
181     FluidSplitComputation sc(to_test(in_mat), to_test(out_mats_gapi));
182     sc.warmUp();
183
184 #if PERF_TEST
185     // iterate testing, and print performance
186     test_ms([&](){ sc.apply(); },
187         400, "Split GAPI %s %dx%d", typeToString(srcType).c_str(), sz.width, sz.height);
188 #endif
189
190     // OpenCV code /////////////////////////////////////////////////////////////
191     {
192         cv::split(in_mat, out_mats_ocv);
193     }
194     // Comparison //////////////////////////////////////////////////////////////
195     {
196         for (int p = 0; p < planes; p++) {
197             EXPECT_EQ(0, cv::countNonZero(out_mats_ocv[p]  != out_mats_gapi[p]));
198         }
199     }
200 }
201
202 TEST_P(MergeTestGAPI, AccuracyTest)
203 {
204     const auto params = GetParam();
205     int planes  = std::get<0>(params);
206     int depth   = std::get<1>(params);
207     cv::Size sz = std::get<2>(params);
208
209     int srcType = CV_MAKE_TYPE(depth, 1);
210     int dstType = CV_MAKE_TYPE(depth, planes);
211
212     std::vector<cv::Mat> in_mats(planes, cv::Mat(sz, srcType));
213     for (int p = 0; p < planes; p++) {
214         cv::randn(in_mats[p], cv::Scalar::all(127), cv::Scalar::all(40.f));
215     }
216
217     cv::Mat out_mat_ocv  = cv::Mat::zeros(sz, dstType);
218     cv::Mat out_mat_gapi = cv::Mat::zeros(sz, dstType);
219
220     // G-API code //////////////////////////////////////////////////////////////
221     FluidMergeComputation mc(to_test(in_mats), to_test(out_mat_gapi));
222     mc.warmUp();
223
224 #if PERF_TEST
225     // iterate testing, and print performance
226     test_ms([&](){ mc.apply(); },
227         400, "Merge GAPI %s %dx%d", typeToString(dstType).c_str(), sz.width, sz.height);
228 #endif
229
230     // OpenCV code /////////////////////////////////////////////////////////////
231     {
232         cv::merge(in_mats, out_mat_ocv);
233     }
234     // Comparison //////////////////////////////////////////////////////////////
235     {
236         EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
237     }
238 }
239
240 //----------------------------------------------------------------------
241
242 TEST_P(ResizeTestIE, AccuracyTest)
243 {
244     int type = 0, interp = 0;
245     cv::Size sz_in, sz_out;
246     double tolerance = 0.0;
247     std::pair<cv::Size, cv::Size> sizes;
248     std::tie(type, interp, sizes, tolerance) = GetParam();
249     std::tie(sz_in, sz_out) = sizes;
250
251     cv::Mat in_mat1(sz_in, type );
252     cv::Scalar mean = cv::Scalar::all(127);
253     cv::Scalar stddev = cv::Scalar::all(40.f);
254
255     cv::randn(in_mat1, mean, stddev);
256
257     cv::Mat out_mat(sz_out, type);
258     cv::Mat out_mat_ocv(sz_out, type);
259
260     // Inference Engine code ///////////////////////////////////////////////////
261
262     size_t channels = out_mat.channels();
263     CV_Assert(1 == channels || 3 == channels);
264
265     int depth = CV_MAT_DEPTH(type);
266     CV_Assert(CV_8U == depth || CV_32F == depth);
267
268     CV_Assert(cv::INTER_AREA == interp || cv::INTER_LINEAR == interp);
269
270     ASSERT_TRUE(in_mat1.isContinuous() && out_mat.isContinuous());
271
272     using namespace InferenceEngine;
273
274     size_t  in_height = in_mat1.rows,  in_width = in_mat1.cols;
275     size_t out_height = out_mat.rows, out_width = out_mat.cols;
276     InferenceEngine::SizeVector  in_sv = { 1, channels,  in_height,  in_width };
277     InferenceEngine::SizeVector out_sv = { 1, channels, out_height, out_width };
278
279     // HWC blob: channels are interleaved
280     Precision precision = CV_8U == depth ? Precision::U8 : Precision::FP32;
281     TensorDesc  in_desc(precision,  in_sv, Layout::NHWC);
282     TensorDesc out_desc(precision, out_sv, Layout::NHWC);
283
284     Blob::Ptr in_blob, out_blob;
285     in_blob  = make_blob_with_precision(in_desc , in_mat1.data);
286     out_blob = make_blob_with_precision(out_desc, out_mat.data);
287
288     PreProcessData preprocess;
289     preprocess.setRoiBlob(in_blob);
290
291     ResizeAlgorithm algorithm = cv::INTER_AREA == interp ? RESIZE_AREA : RESIZE_BILINEAR;
292
293     // test once to warm-up cache
294     preprocess.execute(out_blob, algorithm, false);
295
296 #if PERF_TEST
297     // iterate testing, and print performance
298     test_ms([&](){ preprocess.execute(out_blob, algorithm, false); },
299             100, "Resize IE %s %s %dx%d -> %dx%d",
300             interpToString(interp).c_str(), typeToString(type).c_str(),
301             sz_in.width, sz_in.height, sz_out.width, sz_out.height);
302 #endif
303
304     // OpenCV code /////////////////////////////////////////////////////////////
305     {
306         cv::resize(in_mat1, out_mat_ocv, sz_out, 0, 0, interp);
307     }
308     // Comparison //////////////////////////////////////////////////////////////
309     {
310         cv::Mat absDiff;
311         cv::absdiff(out_mat, out_mat_ocv, absDiff);
312         EXPECT_EQ(0, cv::countNonZero(absDiff > tolerance));
313     }
314 }
315
316 TEST_P(SplitTestIE, AccuracyTest)
317 {
318     int type = std::get<0>(GetParam());
319     cv::Size size = std::get<1>(GetParam());
320
321     int depth = CV_MAT_DEPTH(type);
322     CV_Assert(CV_8U == depth || CV_32F == depth);
323
324     int type1 = CV_MAKE_TYPE(depth, 1);
325     int type4 = CV_MAKE_TYPE(depth, 4);
326
327     cv::Scalar mean = cv::Scalar::all(127);
328     cv::Scalar stddev = cv::Scalar::all(40.f);
329
330     cv::Mat in_mat(size, type);
331     cv::randn(in_mat, mean, stddev);
332
333     int channels = in_mat.channels();
334     CV_Assert(2 == channels || 3 == channels || 4 == channels);
335
336     size_t elemsize1 = in_mat.elemSize1();
337     int    total     = in_mat.total();
338
339     cv::Mat out_mat(size, type4);
340     CV_Assert(in_mat.isContinuous() && out_mat.isContinuous());
341
342     cv::Mat out_mat0(size, type1, out_mat.data + 0*total*elemsize1);
343     cv::Mat out_mat1(size, type1, out_mat.data + 1*total*elemsize1);
344     cv::Mat out_mat2(size, type1, out_mat.data + 2*total*elemsize1);
345     cv::Mat out_mat3(size, type1, out_mat.data + 3*total*elemsize1);
346
347     cv::Mat out_mats[] = {out_mat0, out_mat1, out_mat2, out_mat3};
348
349     std::vector<cv::Mat> out_mats_ocv(channels);
350
351     // Inference Engine code ///////////////////////////////////////////////////
352
353     using namespace InferenceEngine;
354
355     size_t width  = size.width;
356     size_t height = size.height;
357     InferenceEngine::SizeVector sv = { 1, (size_t)channels, height,  width };
358
359     Precision precision = CV_8U == depth ? Precision::U8 : Precision::FP32;
360     TensorDesc  in_desc(precision, sv, Layout::NHWC); // interleaved
361     TensorDesc out_desc(precision, sv, Layout::NCHW); // color planes
362
363     Blob::Ptr in_blob, out_blob;
364     in_blob  = make_blob_with_precision( in_desc,  in_mat.data);
365     out_blob = make_blob_with_precision(out_desc, out_mat.data);
366
367     // test once
368     blob_copy(in_blob, out_blob);
369
370 #if PERF_TEST
371     // iterate testing, and print performance
372     test_ms([&]() { blob_copy(in_blob, out_blob); },
373         400, "Split IE %s %dx%d", typeToString(type).c_str(), size.width, size.height);
374 #endif
375
376     // OpenCV code /////////////////////////////////////////////////////////////
377
378     cv::split(in_mat, out_mats_ocv);
379
380     // Comparison //////////////////////////////////////////////////////////////
381
382     for (int i = 0; i < channels; i++)
383     {
384         EXPECT_EQ(0, cv::countNonZero(out_mats[i] != out_mats_ocv[i]));
385     }
386 }
387
388 TEST_P(MergeTestIE, AccuracyTest)
389 {
390     int type = std::get<0>(GetParam());
391     cv::Size size = std::get<1>(GetParam());
392
393     int depth = CV_MAT_DEPTH(type);
394     CV_Assert(CV_8U == depth || CV_32F == depth);
395
396     int type1 = CV_MAKE_TYPE(depth, 1);
397     int type4 = CV_MAKE_TYPE(depth, 4);
398
399     cv::Mat out_mat(size, type), out_mat_ocv;
400
401     cv::Mat in_mat(size, type4);
402
403     int channels = out_mat.channels();
404     CV_Assert(2 == channels || 3 == channels || 4 == channels);
405
406     size_t elemsize1 = out_mat.elemSize1();
407     int    total     = out_mat.total();
408
409     cv::Mat in_mat0(size, type1, in_mat.data + 0*total*elemsize1);
410     cv::Mat in_mat1(size, type1, in_mat.data + 1*total*elemsize1);
411     cv::Mat in_mat2(size, type1, in_mat.data + 2*total*elemsize1);
412     cv::Mat in_mat3(size, type1, in_mat.data + 3*total*elemsize1);
413
414     cv::Mat in_mats[] = { in_mat0, in_mat1, in_mat2, in_mat3 };
415
416     cv::Scalar mean = cv::Scalar::all(127);
417     cv::Scalar stddev = cv::Scalar::all(40.f);
418
419     for (int i = 0; i < 4 ; i++)
420     {
421         cv::randn(in_mats[i], mean, stddev);
422     }
423
424     CV_Assert(in_mat.isContinuous() && out_mat.isContinuous());
425
426     // Inference Engine code ///////////////////////////////////////////////////
427
428     using namespace InferenceEngine;
429
430     size_t width  = size.width;
431     size_t height = size.height;
432     InferenceEngine::SizeVector sv = { 1, (size_t)channels, height,  width };
433
434     Precision precision = CV_8U == depth ? Precision::U8 : Precision::FP32;
435     TensorDesc  in_desc(precision, sv, Layout::NCHW); // color planes
436     TensorDesc out_desc(precision, sv, Layout::NHWC); // interleaved
437
438     Blob::Ptr in_blob, out_blob;
439     in_blob  = make_blob_with_precision( in_desc,  in_mat.data);
440     out_blob = make_blob_with_precision(out_desc, out_mat.data);
441
442     // test once
443     blob_copy(in_blob, out_blob);
444
445 #if PERF_TEST
446     // iterate testing, and print performance
447     test_ms([&]() { blob_copy(in_blob, out_blob); },
448         400, "Merge IE %s %dx%d", typeToString(type).c_str(), size.width, size.height);
449 #endif
450
451     // OpenCV code /////////////////////////////////////////////////////////////
452
453     cv::merge(in_mats, channels, out_mat_ocv);
454
455     // Comparison //////////////////////////////////////////////////////////////
456
457     EXPECT_EQ(0, cv::countNonZero(out_mat != out_mat_ocv));
458 }
459
460 namespace
461 {
462 // FIXME: Copy-paste from cropRoi tests
463 template <InferenceEngine::Precision::ePrecision PRC>
464 InferenceEngine::Blob::Ptr img2Blob(cv::Mat &img, InferenceEngine::Layout layout) {
465     using namespace InferenceEngine;
466     using data_t = typename PrecisionTrait<PRC>::value_type;
467
468     const size_t channels = img.channels();
469     const size_t height = img.size().height;
470     const size_t width = img.size().width;
471
472     CV_Assert(cv::DataType<data_t>::depth == img.depth());
473
474     SizeVector dims = {1, channels, height, width};
475     Blob::Ptr resultBlob = make_shared_blob<data_t>(TensorDesc(PRC, dims, layout));;
476     resultBlob->allocate();
477
478     data_t* blobData = resultBlob->buffer().as<data_t*>();
479
480     switch (layout) {
481         case Layout::NCHW: {
482             for (size_t c = 0; c < channels; c++) {
483                 for (size_t h = 0; h < height; h++) {
484                     for (size_t w = 0; w < width; w++) {
485                         blobData[c * width * height + h * width + w] = img.ptr<data_t>(h,w)[c];
486                     }
487                 }
488             }
489         }
490         break;
491         case Layout::NHWC: {
492             for (size_t h = 0; h < height; h++) {
493                 for (size_t w = 0; w < width; w++) {
494                     for (size_t c = 0; c < channels; c++) {
495                         blobData[h * width * channels + w * channels + c] = img.ptr<data_t>(h,w)[c];
496                     }
497                 }
498             }
499         }
500         break;
501         default:
502             THROW_IE_EXCEPTION << "Inconsistent input layout for image processing: " << layout;
503     }
504     return resultBlob;
505 }
506
507 template <InferenceEngine::Precision::ePrecision PRC>
508 void Blob2Img(const InferenceEngine::Blob::Ptr& blobP, cv::Mat& img, InferenceEngine::Layout layout) {
509     using namespace InferenceEngine;
510     using data_t = typename PrecisionTrait<PRC>::value_type;
511
512     const size_t channels = img.channels();
513     const size_t height = img.size().height;
514     const size_t width = img.size().width;
515
516     CV_Assert(cv::DataType<data_t>::depth == img.depth());
517
518     data_t* blobData = blobP->buffer().as<data_t*>();
519
520     switch (layout) {
521         case Layout::NCHW: {
522             for (size_t c = 0; c < channels; c++) {
523                 for (size_t h = 0; h < height; h++) {
524                     for (size_t w = 0; w < width; w++) {
525                         img.ptr<data_t>(h,w)[c] = blobData[c * width * height + h * width + w];
526                     }
527                 }
528             }
529         }
530         break;
531         case Layout::NHWC: {
532             for (size_t h = 0; h < height; h++) {
533                 for (size_t w = 0; w < width; w++) {
534                     for (size_t c = 0; c < channels; c++) {
535                         img.ptr<data_t>(h,w)[c] = blobData[h * width * channels + w * channels + c];
536                     }
537                 }
538             }
539         }
540         break;
541         default:
542             THROW_IE_EXCEPTION << "Inconsistent input layout for image processing: " << layout;
543     }
544 }
545 }  // namespace
546
547 TEST_P(PreprocTest, Performance)
548 {
549     using namespace InferenceEngine;
550     Precision prec;
551     ResizeAlgorithm interp;
552     Layout in_layout, out_layout;
553     int ocv_chan = -1;
554     std::pair<cv::Size, cv::Size> sizes;
555     std::tie(prec, interp, in_layout, out_layout, ocv_chan, sizes) = GetParam();
556     cv::Size in_size, out_size;
557     std::tie(in_size, out_size) = sizes;
558
559     const int ocv_depth = prec == Precision::U8 ? CV_8U :
560         prec == Precision::FP32 ? CV_32F : -1;
561     const int ocv_type = CV_MAKETYPE(ocv_depth, ocv_chan);
562     initMatrixRandU(ocv_type, in_size, ocv_type, false);
563
564     cv::Mat out_mat(out_size, ocv_type);
565
566     Blob::Ptr in_blob, out_blob;
567     switch (prec)
568     {
569     case Precision::U8:
570         in_blob = img2Blob<Precision::U8>(in_mat1, in_layout);
571         out_blob = img2Blob<Precision::U8>(out_mat, out_layout);
572         break;
573
574     case Precision::FP32:
575         in_blob = img2Blob<Precision::FP32>(in_mat1, in_layout);
576         out_blob = img2Blob<Precision::FP32>(out_mat, out_layout);
577         break;
578
579     default:
580         FAIL() << "Unsupported configuration";
581     }
582
583     PreProcessData preprocess;
584     preprocess.setRoiBlob(in_blob);
585
586     // test once to warm-up cache
587     preprocess.execute(out_blob, interp, false);
588
589     switch (prec)
590     {
591     case Precision::U8:   Blob2Img<Precision::U8>  (out_blob, out_mat, out_layout); break;
592     case Precision::FP32: Blob2Img<Precision::FP32>(out_blob, out_mat, out_layout); break;
593     default: FAIL() << "Unsupported configuration";
594     }
595
596     cv::Mat ocv_out_mat(out_size, ocv_type);
597     auto cv_interp = interp == RESIZE_AREA ? cv::INTER_AREA : cv::INTER_LINEAR;
598     cv::resize(in_mat1, ocv_out_mat, out_size, 0, 0, cv_interp);
599
600     cv::Mat absDiff;
601     cv::absdiff(ocv_out_mat, out_mat, absDiff);
602     EXPECT_EQ(cv::countNonZero(absDiff > 1), 0);
603
604 #if PERF_TEST
605     // iterate testing, and print performance
606     const auto type_str = depthToString(ocv_depth);
607     const auto interp_str = interp == RESIZE_AREA ? "AREA"
608         : interp == RESIZE_BILINEAR ? "BILINEAR" : "?";
609     const auto layout_to_str = [](const Layout &l) {
610         switch (l) {
611         case Layout::NCHW: return "NCHW";
612         case Layout::NHWC: return "NHWC";
613         default: return "?";
614         }
615     };
616     const auto in_layout_str = layout_to_str(in_layout);
617     const auto out_layout_str = layout_to_str(out_layout);
618
619     test_ms([&]() { preprocess.execute(out_blob, interp, false); },
620             300,
621             "Preproc %s %d %s %s %dx%d %s %dx%d",
622             type_str.c_str(),
623             ocv_chan,
624             interp_str,
625             in_layout_str, in_size.width, in_size.height,
626             out_layout_str, out_size.width, out_size.height);
627 #endif // PERF_TEST
628
629 }
630
631 } // opencv_test
632
633 #endif //OPENCV_GAPI_CORE_TESTS_INL_HPP