1 // Copyright (C) 2018-2019 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
5 #ifndef OPENCV_GAPI_CORE_TESTS_INL_HPP
6 #define OPENCV_GAPI_CORE_TESTS_INL_HPP
8 #include "gapi_core_tests.hpp"
10 #include "blob_factory.hpp"
11 #include "blob_transform.hpp"
12 #include "ie_preprocess_data.hpp"
14 #include <opencv2/core.hpp>
15 #include <opencv2/imgproc.hpp>
16 #include <opencv2/gapi.hpp>
24 #include <fluid_test_computations.hpp>
26 // Can be set externally (via CMake) if built with -DGAPI_TEST_PERF=ON
28 #define PERF_TEST 0 // 1=test performance, 0=don't
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[], ...)
38 using std::chrono::high_resolution_clock;
40 std::vector<high_resolution_clock::duration> samples(iter); samples.clear();
44 for (int i=0; i < iter; i++)
46 auto start = high_resolution_clock::now();
47 func(); // iterate calls
48 samples.push_back(high_resolution_clock::now() - start);
51 std::sort(samples.begin(), samples.end());
53 auto median = samples[samples.size() / 2];
55 double median_ms = std::chrono::duration_cast<std::chrono::microseconds>(median).count() * 0.001; // convert to milliseconds
57 printf("Performance(ms): %lg ", median_ms);
60 va_start(args, format);
61 vprintf(format, args);
67 static cv::String interpToString(int interp)
71 case cv::INTER_AREA : return "INTER_AREA";
72 case cv::INTER_LINEAR : return "INTER_LINEAR";
73 case cv::INTER_NEAREST: return "INTER_NEAREST";
75 CV_Assert(!"ERROR: unsupported interpolation!");
79 static cv::String depthToString(int depth)
83 case CV_8U : return "CV_8U";
84 case CV_32F : return "CV_32F";
86 CV_Assert(!"ERROR: unsupported depth!");
90 static cv::String typeToString(int type)
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";
103 CV_Assert(!"ERROR: unsupported type!");
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)
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]);
120 } // anonymous namespace
122 TEST_P(ResizeTestGAPI, AccuracyTest)
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;
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);
135 cv::randn(in_mat1, mean, stddev);
137 cv::Mat out_mat(sz_out, type);
138 cv::Mat out_mat_ocv(sz_out, type);
140 // G-API code //////////////////////////////////////////////////////////////
141 FluidResizeComputation rc(to_test(in_mat1), to_test(out_mat), interp);
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);
152 // OpenCV code /////////////////////////////////////////////////////////////
154 cv::resize(in_mat1, out_mat_ocv, sz_out, 0, 0, interp);
156 // Comparison //////////////////////////////////////////////////////////////
159 cv::absdiff(out_mat, out_mat_ocv, absDiff);
160 EXPECT_EQ(0, cv::countNonZero(absDiff > tolerance));
164 TEST_P(SplitTestGAPI, AccuracyTest)
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);
171 int srcType = CV_MAKE_TYPE(depth, planes);
172 int dstType = CV_MAKE_TYPE(depth, 1);
174 cv::Mat in_mat(sz, srcType);
175 cv::randn(in_mat, cv::Scalar::all(127), cv::Scalar::all(40.f));
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));
180 // G-API code //////////////////////////////////////////////////////////////
181 FluidSplitComputation sc(to_test(in_mat), to_test(out_mats_gapi));
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);
190 // OpenCV code /////////////////////////////////////////////////////////////
192 cv::split(in_mat, out_mats_ocv);
194 // Comparison //////////////////////////////////////////////////////////////
196 for (int p = 0; p < planes; p++) {
197 EXPECT_EQ(0, cv::countNonZero(out_mats_ocv[p] != out_mats_gapi[p]));
202 TEST_P(MergeTestGAPI, AccuracyTest)
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);
209 int srcType = CV_MAKE_TYPE(depth, 1);
210 int dstType = CV_MAKE_TYPE(depth, planes);
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));
217 cv::Mat out_mat_ocv = cv::Mat::zeros(sz, dstType);
218 cv::Mat out_mat_gapi = cv::Mat::zeros(sz, dstType);
220 // G-API code //////////////////////////////////////////////////////////////
221 FluidMergeComputation mc(to_test(in_mats), to_test(out_mat_gapi));
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);
230 // OpenCV code /////////////////////////////////////////////////////////////
232 cv::merge(in_mats, out_mat_ocv);
234 // Comparison //////////////////////////////////////////////////////////////
236 EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi));
240 //----------------------------------------------------------------------
242 TEST_P(ResizeTestIE, AccuracyTest)
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;
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);
255 cv::randn(in_mat1, mean, stddev);
257 cv::Mat out_mat(sz_out, type);
258 cv::Mat out_mat_ocv(sz_out, type);
260 // Inference Engine code ///////////////////////////////////////////////////
262 size_t channels = out_mat.channels();
263 CV_Assert(1 == channels || 3 == channels);
265 int depth = CV_MAT_DEPTH(type);
266 CV_Assert(CV_8U == depth || CV_32F == depth);
268 CV_Assert(cv::INTER_AREA == interp || cv::INTER_LINEAR == interp);
270 ASSERT_TRUE(in_mat1.isContinuous() && out_mat.isContinuous());
272 using namespace InferenceEngine;
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 };
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);
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);
288 PreProcessData preprocess;
289 preprocess.setRoiBlob(in_blob);
291 ResizeAlgorithm algorithm = cv::INTER_AREA == interp ? RESIZE_AREA : RESIZE_BILINEAR;
293 // test once to warm-up cache
294 preprocess.execute(out_blob, algorithm, false);
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);
304 // OpenCV code /////////////////////////////////////////////////////////////
306 cv::resize(in_mat1, out_mat_ocv, sz_out, 0, 0, interp);
308 // Comparison //////////////////////////////////////////////////////////////
311 cv::absdiff(out_mat, out_mat_ocv, absDiff);
312 EXPECT_EQ(0, cv::countNonZero(absDiff > tolerance));
316 TEST_P(SplitTestIE, AccuracyTest)
318 int type = std::get<0>(GetParam());
319 cv::Size size = std::get<1>(GetParam());
321 int depth = CV_MAT_DEPTH(type);
322 CV_Assert(CV_8U == depth || CV_32F == depth);
324 int type1 = CV_MAKE_TYPE(depth, 1);
325 int type4 = CV_MAKE_TYPE(depth, 4);
327 cv::Scalar mean = cv::Scalar::all(127);
328 cv::Scalar stddev = cv::Scalar::all(40.f);
330 cv::Mat in_mat(size, type);
331 cv::randn(in_mat, mean, stddev);
333 int channels = in_mat.channels();
334 CV_Assert(2 == channels || 3 == channels || 4 == channels);
336 size_t elemsize1 = in_mat.elemSize1();
337 int total = in_mat.total();
339 cv::Mat out_mat(size, type4);
340 CV_Assert(in_mat.isContinuous() && out_mat.isContinuous());
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);
347 cv::Mat out_mats[] = {out_mat0, out_mat1, out_mat2, out_mat3};
349 std::vector<cv::Mat> out_mats_ocv(channels);
351 // Inference Engine code ///////////////////////////////////////////////////
353 using namespace InferenceEngine;
355 size_t width = size.width;
356 size_t height = size.height;
357 InferenceEngine::SizeVector sv = { 1, (size_t)channels, height, width };
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
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);
368 blob_copy(in_blob, out_blob);
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);
376 // OpenCV code /////////////////////////////////////////////////////////////
378 cv::split(in_mat, out_mats_ocv);
380 // Comparison //////////////////////////////////////////////////////////////
382 for (int i = 0; i < channels; i++)
384 EXPECT_EQ(0, cv::countNonZero(out_mats[i] != out_mats_ocv[i]));
388 TEST_P(MergeTestIE, AccuracyTest)
390 int type = std::get<0>(GetParam());
391 cv::Size size = std::get<1>(GetParam());
393 int depth = CV_MAT_DEPTH(type);
394 CV_Assert(CV_8U == depth || CV_32F == depth);
396 int type1 = CV_MAKE_TYPE(depth, 1);
397 int type4 = CV_MAKE_TYPE(depth, 4);
399 cv::Mat out_mat(size, type), out_mat_ocv;
401 cv::Mat in_mat(size, type4);
403 int channels = out_mat.channels();
404 CV_Assert(2 == channels || 3 == channels || 4 == channels);
406 size_t elemsize1 = out_mat.elemSize1();
407 int total = out_mat.total();
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);
414 cv::Mat in_mats[] = { in_mat0, in_mat1, in_mat2, in_mat3 };
416 cv::Scalar mean = cv::Scalar::all(127);
417 cv::Scalar stddev = cv::Scalar::all(40.f);
419 for (int i = 0; i < 4 ; i++)
421 cv::randn(in_mats[i], mean, stddev);
424 CV_Assert(in_mat.isContinuous() && out_mat.isContinuous());
426 // Inference Engine code ///////////////////////////////////////////////////
428 using namespace InferenceEngine;
430 size_t width = size.width;
431 size_t height = size.height;
432 InferenceEngine::SizeVector sv = { 1, (size_t)channels, height, width };
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
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);
443 blob_copy(in_blob, out_blob);
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);
451 // OpenCV code /////////////////////////////////////////////////////////////
453 cv::merge(in_mats, channels, out_mat_ocv);
455 // Comparison //////////////////////////////////////////////////////////////
457 EXPECT_EQ(0, cv::countNonZero(out_mat != out_mat_ocv));
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;
468 const size_t channels = img.channels();
469 const size_t height = img.size().height;
470 const size_t width = img.size().width;
472 CV_Assert(cv::DataType<data_t>::depth == img.depth());
474 SizeVector dims = {1, channels, height, width};
475 Blob::Ptr resultBlob = make_shared_blob<data_t>(TensorDesc(PRC, dims, layout));;
476 resultBlob->allocate();
478 data_t* blobData = resultBlob->buffer().as<data_t*>();
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];
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];
502 THROW_IE_EXCEPTION << "Inconsistent input layout for image processing: " << layout;
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;
512 const size_t channels = img.channels();
513 const size_t height = img.size().height;
514 const size_t width = img.size().width;
516 CV_Assert(cv::DataType<data_t>::depth == img.depth());
518 data_t* blobData = blobP->buffer().as<data_t*>();
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];
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];
542 THROW_IE_EXCEPTION << "Inconsistent input layout for image processing: " << layout;
547 TEST_P(PreprocTest, Performance)
549 using namespace InferenceEngine;
551 ResizeAlgorithm interp;
552 Layout in_layout, out_layout;
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;
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);
564 cv::Mat out_mat(out_size, ocv_type);
566 Blob::Ptr in_blob, out_blob;
570 in_blob = img2Blob<Precision::U8>(in_mat1, in_layout);
571 out_blob = img2Blob<Precision::U8>(out_mat, out_layout);
574 case Precision::FP32:
575 in_blob = img2Blob<Precision::FP32>(in_mat1, in_layout);
576 out_blob = img2Blob<Precision::FP32>(out_mat, out_layout);
580 FAIL() << "Unsupported configuration";
583 PreProcessData preprocess;
584 preprocess.setRoiBlob(in_blob);
586 // test once to warm-up cache
587 preprocess.execute(out_blob, interp, false);
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";
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);
601 cv::absdiff(ocv_out_mat, out_mat, absDiff);
602 EXPECT_EQ(cv::countNonZero(absDiff > 1), 0);
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) {
611 case Layout::NCHW: return "NCHW";
612 case Layout::NHWC: return "NHWC";
616 const auto in_layout_str = layout_to_str(in_layout);
617 const auto out_layout_str = layout_to_str(out_layout);
619 test_ms([&]() { preprocess.execute(out_blob, interp, false); },
621 "Preproc %s %d %s %s %dx%d %s %dx%d",
625 in_layout_str, in_size.width, in_size.height,
626 out_layout_str, out_size.width, out_size.height);
633 #endif //OPENCV_GAPI_CORE_TESTS_INL_HPP