dnn(test): replace SkipTestException with tags
[platform/upstream/opencv.git] / modules / dnn / test / test_layers.cpp
1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                           License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2017, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 //   * Redistribution's of source code must retain the above copyright notice,
20 //     this list of conditions and the following disclaimer.
21 //
22 //   * Redistribution's in binary form must reproduce the above copyright notice,
23 //     this list of conditions and the following disclaimer in the documentation
24 //     and/or other materials provided with the distribution.
25 //
26 //   * The name of the copyright holders may not be used to endorse or promote products
27 //     derived from this software without specific prior written permission.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 //M*/
41
42 #include "test_precomp.hpp"
43 #include <opencv2/core/ocl.hpp>
44 #include "npy_blob.hpp"
45 #include <opencv2/dnn/shape_utils.hpp>
46 #include <opencv2/dnn/all_layers.hpp>
47 #include <opencv2/dnn/layer.details.hpp>  // CV_DNN_REGISTER_LAYER_CLASS
48
49 #ifdef HAVE_INF_ENGINE
50 #include <thread>
51 #endif
52
53 namespace opencv_test { namespace {
54
55 template<typename TString>
56 static String _tf(TString filename)
57 {
58     String basetestdir = getOpenCVExtraDir();
59     size_t len = basetestdir.size();
60     if(len > 0 && basetestdir[len-1] != '/' && basetestdir[len-1] != '\\')
61         return (basetestdir + "/dnn/layers") + filename;
62     return (basetestdir + "dnn/layers/") + filename;
63 }
64
65 void runLayer(Ptr<Layer> layer, std::vector<Mat> &inpBlobs, std::vector<Mat> &outBlobs)
66 {
67     size_t ninputs = inpBlobs.size();
68     std::vector<Mat> inp(ninputs), outp, intp;
69     std::vector<MatShape> inputs, outputs, internals;
70
71     for (size_t i = 0; i < ninputs; i++)
72     {
73         inp[i] = inpBlobs[i].clone();
74         inputs.push_back(shape(inp[i]));
75     }
76
77     layer->getMemoryShapes(inputs, 0, outputs, internals);
78     for (size_t i = 0; i < outputs.size(); i++)
79     {
80         outp.push_back(Mat(outputs[i], CV_32F));
81     }
82     for (size_t i = 0; i < internals.size(); i++)
83     {
84         intp.push_back(Mat(internals[i], CV_32F));
85     }
86
87     layer->finalize(inp, outp);
88     layer->forward(inp, outp, intp);
89
90     size_t noutputs = outp.size();
91     outBlobs.resize(noutputs);
92     for (size_t i = 0; i < noutputs; i++)
93         outBlobs[i] = outp[i];
94 }
95
96 class Test_Caffe_layers : public DNNTestLayer
97 {
98 public:
99     void testLayerUsingCaffeModels(const String& basename, bool useCaffeModel = false,
100                                    bool useCommonInputBlob = true, double l1 = 0.0,
101                                    double lInf = 0.0)
102     {
103         String prototxt = _tf(basename + ".prototxt");
104         String caffemodel = _tf(basename + ".caffemodel");
105
106         String inpfile = (useCommonInputBlob) ? _tf("blob.npy") : _tf(basename + ".input.npy");
107         String outfile = _tf(basename + ".npy");
108
109         Mat inp = blobFromNPY(inpfile);
110         Mat ref = blobFromNPY(outfile);
111         checkBackend(&inp, &ref);
112
113         Net net = readNetFromCaffe(prototxt, (useCaffeModel) ? caffemodel : String());
114         ASSERT_FALSE(net.empty());
115
116         net.setPreferableBackend(backend);
117         net.setPreferableTarget(target);
118
119         net.setInput(inp, "input");
120         Mat out = net.forward("output");
121
122         normAssert(ref, out, "", l1 ? l1 : default_l1, lInf ? lInf : default_lInf);
123     }
124 };
125
126 TEST_P(Test_Caffe_layers, Softmax)
127 {
128     testLayerUsingCaffeModels("layer_softmax");
129 }
130
131 TEST_P(Test_Caffe_layers, LRN)
132 {
133     testLayerUsingCaffeModels("layer_lrn_spatial");
134     testLayerUsingCaffeModels("layer_lrn_channels");
135 }
136
137 TEST_P(Test_Caffe_layers, Convolution)
138 {
139     testLayerUsingCaffeModels("layer_convolution", true);
140 }
141
142 TEST_P(Test_Caffe_layers, DeConvolution)
143 {
144     if (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_CPU)
145         applyTestTag(CV_TEST_TAG_DNN_SKIP_IE);  // TODO IE_CPU
146     testLayerUsingCaffeModels("layer_deconvolution", true, false);
147 }
148
149 TEST_P(Test_Caffe_layers, InnerProduct)
150 {
151     if (backend == DNN_BACKEND_INFERENCE_ENGINE)
152         applyTestTag(CV_TEST_TAG_DNN_SKIP_IE);
153     if (backend == DNN_BACKEND_OPENCV && target == DNN_TARGET_OPENCL_FP16)
154         applyTestTag(CV_TEST_TAG_DNN_SKIP_OPENCL_FP16);
155     testLayerUsingCaffeModels("layer_inner_product", true);
156 }
157
158 TEST_P(Test_Caffe_layers, Pooling_max)
159 {
160     testLayerUsingCaffeModels("layer_pooling_max");
161 }
162
163 TEST_P(Test_Caffe_layers, Pooling_ave)
164 {
165     testLayerUsingCaffeModels("layer_pooling_ave");
166 }
167
168 TEST_P(Test_Caffe_layers, MVN)
169 {
170     testLayerUsingCaffeModels("layer_mvn");
171 }
172
173 void testReshape(const MatShape& inputShape, const MatShape& targetShape,
174                  int axis = 0, int num_axes = -1,
175                  MatShape mask = MatShape())
176 {
177     LayerParams params;
178     params.set("axis", axis);
179     params.set("num_axes", num_axes);
180     if (!mask.empty())
181     {
182         params.set("dim", DictValue::arrayInt<int*>(&mask[0], mask.size()));
183     }
184
185     Mat inp(inputShape.size(), &inputShape[0], CV_32F);
186     std::vector<Mat> inpVec(1, inp);
187     std::vector<Mat> outVec, intVec;
188
189     Ptr<Layer> rl = LayerFactory::createLayerInstance("Reshape", params);
190     runLayer(rl, inpVec, outVec);
191
192     Mat& out = outVec[0];
193     MatShape shape(out.size.p, out.size.p + out.dims);
194     EXPECT_EQ(shape, targetShape);
195 }
196
197 TEST(Layer_Test_Reshape, Accuracy)
198 {
199     {
200         int inp[] = {4, 3, 1, 2};
201         int out[] = {4, 3, 2};
202         testReshape(MatShape(inp, inp + 4), MatShape(out, out + 3), 2, 1);
203     }
204     {
205         int inp[] = {1, 128, 4, 4};
206         int out[] = {1, 2048};
207         int mask[] = {-1, 2048};
208         testReshape(MatShape(inp, inp + 4), MatShape(out, out + 2), 0, -1,
209                     MatShape(mask, mask + 2));
210     }
211     {
212         int inp[] = {1, 2, 3};
213         int out[] = {3, 1, 2};
214         int mask[] = {3, 1, 2};
215         testReshape(MatShape(inp, inp + 3), MatShape(out, out + 3), 0, -1,
216                     MatShape(mask, mask + 3));
217     }
218 }
219
220 TEST_P(Test_Caffe_layers, BatchNorm)
221 {
222     testLayerUsingCaffeModels("layer_batch_norm", true);
223     testLayerUsingCaffeModels("layer_batch_norm_local_stats", true, false);
224 }
225
226 TEST_P(Test_Caffe_layers, ReLU)
227 {
228     testLayerUsingCaffeModels("layer_relu");
229 }
230
231 TEST_P(Test_Caffe_layers, Dropout)
232 {
233     testLayerUsingCaffeModels("layer_dropout");
234 }
235
236 TEST_P(Test_Caffe_layers, Concat)
237 {
238 #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_GE(2019010000)
239     if (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD)
240         applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_2019R1, CV_TEST_TAG_DNN_SKIP_IE_2019R1_1);
241 #endif
242     testLayerUsingCaffeModels("layer_concat");
243     testLayerUsingCaffeModels("layer_concat_optim", true, false);
244     testLayerUsingCaffeModels("layer_concat_shared_input", true, false);
245 }
246
247 TEST_P(Test_Caffe_layers, Fused_Concat)
248 {
249 #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_GE(2019010000)
250     if (backend == DNN_BACKEND_INFERENCE_ENGINE)  // Test is disabled for DLIE due negative_slope parameter
251         applyTestTag(CV_TEST_TAG_DNN_SKIP_IE, CV_TEST_TAG_DNN_SKIP_IE_2019R1, CV_TEST_TAG_DNN_SKIP_IE_2019R1_1);
252 #endif
253
254 #if defined(INF_ENGINE_RELEASE)
255     if (backend == DNN_BACKEND_INFERENCE_ENGINE && (target == DNN_TARGET_OPENCL || target == DNN_TARGET_OPENCL_FP16))
256         applyTestTag(target == DNN_TARGET_OPENCL ? CV_TEST_TAG_DNN_SKIP_IE_OPENCL : CV_TEST_TAG_DNN_SKIP_IE_OPENCL_FP16);
257 #endif
258
259     checkBackend();
260
261     // Test case
262     // input
263     //   |
264     //   v
265     // some_layer
266     // |   |
267     // v   v
268     // concat
269     Net net;
270     int interLayer;
271     {
272         LayerParams lp;
273         lp.type = "AbsVal";
274         lp.name = "someLayer";
275         interLayer = net.addLayerToPrev(lp.name, lp.type, lp);
276     }
277     {
278         LayerParams lp;
279         lp.set("axis", 1);
280         lp.type = "Concat";
281         lp.name = "testConcat";
282         int id = net.addLayer(lp.name, lp.type, lp);
283         net.connect(interLayer, 0, id, 0);
284         net.connect(interLayer, 0, id, 1);
285     }
286     int shape[] = {1, 2, 3, 4};
287     Mat input(4, shape, CV_32F);
288     randu(input, 0.0f, 1.0f);  // [0, 1] to make AbsVal an identity transformation.
289
290     net.setInput(input);
291     net.setPreferableBackend(backend);
292     net.setPreferableTarget(target);
293     Mat out = net.forward();
294
295     normAssert(slice(out, Range::all(), Range(0, 2), Range::all(), Range::all()), input, "", default_l1, default_lInf);
296     normAssert(slice(out, Range::all(), Range(2, 4), Range::all(), Range::all()), input, "", default_l1, default_lInf);
297 }
298
299 TEST_P(Test_Caffe_layers, Eltwise)
300 {
301     if (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD)
302         applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD);
303     testLayerUsingCaffeModels("layer_eltwise");
304 }
305
306 TEST_P(Test_Caffe_layers, PReLU)
307 {
308     testLayerUsingCaffeModels("layer_prelu", true);
309 }
310
311 // TODO: fix an unstable test case
312 TEST_P(Test_Caffe_layers, layer_prelu_fc)
313 {
314     if (backend == DNN_BACKEND_OPENCV && target == DNN_TARGET_OPENCL_FP16)
315         applyTestTag(CV_TEST_TAG_DNN_SKIP_OPENCL_FP16);
316     // Reference output values are in range [-0.0001, 10.3906]
317     double l1 = (target == DNN_TARGET_MYRIAD) ? 0.005 : 0.0;
318     double lInf = (target == DNN_TARGET_MYRIAD) ? 0.021 : 0.0;
319     testLayerUsingCaffeModels("layer_prelu_fc", true, false, l1, lInf);
320 }
321
322 //template<typename XMat>
323 //static void test_Layer_Concat()
324 //{
325 //    Matx21f a(1.f, 1.f), b(2.f, 2.f), c(3.f, 3.f);
326 //    std::vector<Blob> res(1), src = { Blob(XMat(a)), Blob(XMat(b)), Blob(XMat(c)) };
327 //    Blob ref(XMat(Matx23f(1.f, 2.f, 3.f, 1.f, 2.f, 3.f)));
328 //
329 //    runLayer(ConcatLayer::create(1), src, res);
330 //    normAssert(ref, res[0]);
331 //}
332 //TEST(Layer_Concat, Accuracy)
333 //{
334 //    test_Layer_Concat<Mat>());
335 //}
336 //OCL_TEST(Layer_Concat, Accuracy)
337 //{
338 //    OCL_ON(test_Layer_Concat<Mat>());
339 //    );
340 //}
341
342 TEST_P(Test_Caffe_layers, Reshape_Split_Slice)
343 {
344     if (backend == DNN_BACKEND_INFERENCE_ENGINE)
345         applyTestTag(CV_TEST_TAG_DNN_SKIP_IE);
346
347     Net net = readNetFromCaffe(_tf("reshape_and_slice_routines.prototxt"));
348     ASSERT_FALSE(net.empty());
349
350     net.setPreferableBackend(backend);
351     net.setPreferableTarget(target);
352
353     Mat input(6, 12, CV_32F);
354     RNG rng(0);
355     rng.fill(input, RNG::UNIFORM, -1, 1);
356
357     net.setInput(input, "input");
358     Mat output = net.forward("output");
359
360     normAssert(input, output, "", default_l1, default_lInf);
361 }
362
363 TEST_P(Test_Caffe_layers, Conv_Elu)
364 {
365 #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_RELEASE <= 2018050000
366     if (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD)
367         applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_2018R5);
368 #endif
369
370     Net net = readNetFromTensorflow(_tf("layer_elu_model.pb"));
371     ASSERT_FALSE(net.empty());
372
373     Mat inp = blobFromNPY(_tf("layer_elu_in.npy"));
374     Mat ref = blobFromNPY(_tf("layer_elu_out.npy"));
375
376     net.setInput(inp, "input");
377     net.setPreferableBackend(backend);
378     net.setPreferableTarget(target);
379     Mat out = net.forward();
380
381     normAssert(ref, out, "", default_l1, default_lInf);
382 }
383
384 class Layer_LSTM_Test : public ::testing::Test
385 {
386 public:
387     int numInp, numOut;
388     Mat Wh, Wx, b;
389     Ptr<LSTMLayer> layer;
390     std::vector<Mat> inputs, outputs;
391
392     Layer_LSTM_Test() {}
393
394     void init(const MatShape &inpShape_, const MatShape &outShape_,
395               bool produceCellOutput, bool useTimestampDim)
396     {
397         numInp = total(inpShape_);
398         numOut = total(outShape_);
399
400         Wh = Mat::ones(4 * numOut, numOut, CV_32F);
401         Wx = Mat::ones(4 * numOut, numInp, CV_32F);
402         b  = Mat::ones(4 * numOut, 1, CV_32F);
403
404         LayerParams lp;
405         lp.blobs.resize(3);
406         lp.blobs[0] = Wh;
407         lp.blobs[1] = Wx;
408         lp.blobs[2] = b;
409         lp.set<bool>("produce_cell_output", produceCellOutput);
410         lp.set<bool>("use_timestamp_dim", useTimestampDim);
411
412         layer = LSTMLayer::create(lp);
413         layer->setOutShape(outShape_);
414     }
415 };
416
417 TEST_F(Layer_LSTM_Test, get_set_test)
418 {
419     const int TN = 4;
420     MatShape inpShape = shape(5, 3, 2);
421     MatShape outShape = shape(3, 1, 2);
422     MatShape inpResShape = concat(shape(TN), inpShape);
423     MatShape outResShape = concat(shape(TN), outShape);
424
425     init(inpShape, outShape, true, false);
426     layer->setOutShape(outShape);
427
428     Mat C((int)outResShape.size(), &outResShape[0], CV_32F);
429     randu(C, -1., 1.);
430     Mat H = C.clone();
431     randu(H, -1., 1.);
432
433     Mat inp((int)inpResShape.size(), &inpResShape[0], CV_32F);
434     randu(inp, -1., 1.);
435
436     inputs.push_back(inp);
437     runLayer(layer, inputs, outputs);
438
439     EXPECT_EQ(2u, outputs.size());
440
441     print(outResShape, "outResShape");
442     print(shape(outputs[0]), "out0");
443     print(shape(outputs[0]), "out1");
444
445     EXPECT_EQ(outResShape, shape(outputs[0]));
446     EXPECT_EQ(outResShape, shape(outputs[1]));
447
448     EXPECT_EQ(0, layer->inputNameToIndex("x"));
449     EXPECT_EQ(0, layer->outputNameToIndex("h"));
450     EXPECT_EQ(1, layer->outputNameToIndex("c"));
451 }
452
453 TEST(Layer_LSTM_Test_Accuracy_with_, CaffeRecurrent)
454 {
455     LayerParams lp;
456     lp.blobs.resize(3);
457     lp.blobs[0] = blobFromNPY(_tf("lstm.prototxt.w_2.npy"));  // Wh
458     lp.blobs[1] = blobFromNPY(_tf("lstm.prototxt.w_0.npy"));  // Wx
459     lp.blobs[2] = blobFromNPY(_tf("lstm.prototxt.w_1.npy"));  // bias
460     Ptr<LSTMLayer> layer = LSTMLayer::create(lp);
461
462     Mat inp = blobFromNPY(_tf("recurrent.input.npy"));
463     std::vector<Mat> inputs(1, inp), outputs;
464     runLayer(layer, inputs, outputs);
465
466     Mat h_t_reference = blobFromNPY(_tf("lstm.prototxt.h_1.npy"));
467     normAssert(h_t_reference, outputs[0]);
468 }
469
470 TEST(Layer_RNN_Test_Accuracy_with_, CaffeRecurrent)
471 {
472     Ptr<RNNLayer> layer = RNNLayer::create(LayerParams());
473
474     layer->setWeights(
475                 blobFromNPY(_tf("rnn.prototxt.w_0.npy")),
476                 blobFromNPY(_tf("rnn.prototxt.w_1.npy")),
477                 blobFromNPY(_tf("rnn.prototxt.w_2.npy")),
478                 blobFromNPY(_tf("rnn.prototxt.w_3.npy")),
479                 blobFromNPY(_tf("rnn.prototxt.w_4.npy")) );
480
481     std::vector<Mat> output, input(1, blobFromNPY(_tf("recurrent.input.npy")));
482     runLayer(layer, input, output);
483
484     Mat h_ref = blobFromNPY(_tf("rnn.prototxt.h_1.npy"));
485     normAssert(h_ref, output[0]);
486 }
487
488
489 class Layer_RNN_Test : public ::testing::Test
490 {
491 public:
492     int nX, nH, nO, nT, nS;
493     Mat Whh, Wxh, bh, Who, bo;
494     Ptr<RNNLayer> layer;
495
496     std::vector<Mat> inputs, outputs;
497
498     Layer_RNN_Test()
499     {
500         nT = 3;
501         nS = 5;
502         nX = 31;
503         nH = 64;
504         nO = 100;
505
506         Whh = Mat::ones(nH, nH, CV_32F);
507         Wxh = Mat::ones(nH, nX, CV_32F);
508         bh  = Mat::ones(nH, 1, CV_32F);
509         Who = Mat::ones(nO, nH, CV_32F);
510         bo  = Mat::ones(nO, 1, CV_32F);
511
512         layer = RNNLayer::create(LayerParams());
513         layer->setProduceHiddenOutput(true);
514         layer->setWeights(Wxh, bh, Whh, Who, bo);
515     }
516 };
517
518 TEST_F(Layer_RNN_Test, get_set_test)
519 {
520     int sz[] = { nT, nS, 1, nX };
521     Mat inp(4, sz, CV_32F);
522     randu(inp, -1., 1.);
523     inputs.push_back(inp);
524     runLayer(layer, inputs, outputs);
525
526     EXPECT_EQ(outputs.size(), 2u);
527     EXPECT_EQ(shape(outputs[0]), shape(nT, nS, nO));
528     EXPECT_EQ(shape(outputs[1]), shape(nT, nS, nH));
529 }
530
531 TEST(Layer_Test_ROIPooling, Accuracy)
532 {
533     Net net = readNetFromCaffe(_tf("net_roi_pooling.prototxt"));
534
535     Mat inp = blobFromNPY(_tf("net_roi_pooling.input.npy"));
536     Mat rois = blobFromNPY(_tf("net_roi_pooling.rois.npy"));
537     Mat ref = blobFromNPY(_tf("net_roi_pooling.npy"));
538
539     net.setInput(inp, "input");
540     net.setInput(rois, "rois");
541     net.setPreferableBackend(DNN_BACKEND_OPENCV);
542
543     Mat out = net.forward();
544
545     normAssert(out, ref);
546 }
547
548 TEST_P(Test_Caffe_layers, FasterRCNN_Proposal)
549 {
550     if (backend == DNN_BACKEND_OPENCV && target == DNN_TARGET_OPENCL_FP16)
551         applyTestTag(CV_TEST_TAG_DNN_SKIP_OPENCL_FP16);
552     if (backend == DNN_BACKEND_INFERENCE_ENGINE)
553         applyTestTag(CV_TEST_TAG_DNN_SKIP_IE);
554
555     Net net = readNetFromCaffe(_tf("net_faster_rcnn_proposal.prototxt"));
556
557     Mat scores = blobFromNPY(_tf("net_faster_rcnn_proposal.scores.npy"));
558     Mat deltas = blobFromNPY(_tf("net_faster_rcnn_proposal.deltas.npy"));
559     Mat imInfo = (Mat_<float>(1, 3) << 600, 800, 1.6f);
560
561     net.setInput(scores, "rpn_cls_prob_reshape");
562     net.setInput(deltas, "rpn_bbox_pred");
563     net.setInput(imInfo, "im_info");
564
565     std::vector<Mat> outs;
566     net.setPreferableBackend(backend);
567     net.setPreferableTarget(target);
568     net.forward(outs, "output");
569
570     for (int i = 0; i < 2; ++i)
571     {
572         Mat ref = blobFromNPY(_tf(i == 0 ? "net_faster_rcnn_proposal.out_rois.npy" :
573                                            "net_faster_rcnn_proposal.out_scores.npy"));
574         const int numDets = ref.size[0];
575         EXPECT_LE(numDets, outs[i].size[0]);
576         normAssert(outs[i].rowRange(0, numDets), ref);
577
578         if (numDets < outs[i].size[0])
579         {
580             EXPECT_EQ(countNonZero(outs[i].rowRange(numDets, outs[i].size[0])), 0);
581         }
582     }
583 }
584
585 typedef testing::TestWithParam<tuple<Vec4i, Vec2i, bool> > Scale_untrainable;
586 TEST_P(Scale_untrainable, Accuracy)
587 {
588     Vec4i inpShapeVec = get<0>(GetParam());
589     int axis = get<1>(GetParam())[0];
590     int weightsDims = get<1>(GetParam())[1];
591     bool testFusion = get<2>(GetParam());
592     const int inpShape[] = {inpShapeVec[0], inpShapeVec[1], inpShapeVec[2], inpShapeVec[3]};
593
594     // Create a network with two inputs. Scale layer multiplies a first input to
595     // a second one. See http://caffe.berkeleyvision.org/tutorial/layers/scale.html
596     Net net;
597     // Check that this version of Scale layer won't be fused with Convolution layer.
598     if (testFusion)
599     {
600         LayerParams lp;
601         lp.set("kernel_size", 1);
602         lp.set("num_output", 3);
603         lp.set("group", 3);
604         lp.set("bias_term", false);
605         lp.type = "Convolution";
606         lp.name = "testConv";
607
608         std::vector<int> weightsShape(4);
609         weightsShape[0] = 3;  // #outChannels
610         weightsShape[1] = 1;  // #inpChannels / group
611         weightsShape[2] = 1;  // height
612         weightsShape[3] = 1;  // width
613         Mat weights(weightsShape, CV_32F);
614         weights.setTo(1);
615         lp.blobs.push_back(weights);
616         net.addLayerToPrev(lp.name, lp.type, lp);
617     }
618     LayerParams lp;
619     lp.type = "Scale";
620     lp.name = "testLayer";
621     lp.set("axis", axis);
622     int id = net.addLayerToPrev(lp.name, lp.type, lp);
623     net.connect(0, 1, id, 1);
624
625     Mat input(4, inpShape, CV_32F);
626     Mat weights(weightsDims, &inpShape[axis], CV_32F);
627     randu(input, -1, 1);
628     randu(weights, -1, 1);
629
630     std::vector<String> inpNames(2);
631     inpNames[0] = "scale_input";
632     inpNames[1] = "scale_weights";
633     net.setInputsNames(inpNames);
634     net.setInput(input, inpNames[0]);
635     net.setInput(weights, inpNames[1]);
636     net.setPreferableBackend(DNN_BACKEND_OPENCV);
637     Mat out = net.forward();
638
639     Mat ref(input.dims, input.size, CV_32F);
640     float* inpData = (float*)input.data;
641     float* refData = (float*)ref.data;
642     float* weightsData = (float*)weights.data;
643     int spatialSize = 1;
644     for (int i = axis + weightsDims; i < 4; ++i)
645         spatialSize *= inpShape[i];
646     for (int i = 0; i < ref.total(); ++i)
647     {
648         float w = weightsData[(i / spatialSize) % weights.total()];
649         refData[i] = inpData[i] * w;
650     }
651     normAssert(out, ref);
652 }
653
654 INSTANTIATE_TEST_CASE_P(Layer_Test, Scale_untrainable, Combine(
655 /*input size*/   Values(Vec4i(2, 3, 4, 5)),
656 /*axis, #dims*/  Values(Vec2i(0, 1), Vec2i(0, 2), Vec2i(0, 3), Vec2i(0, 4),
657                                      Vec2i(1, 1), Vec2i(1, 2), Vec2i(1, 3),
658                                                   Vec2i(2, 1), Vec2i(2, 2),
659                                                                Vec2i(3, 1)),
660 /*conv fusion*/  testing::Bool()
661 ));
662
663 typedef testing::TestWithParam<tuple<Vec4i, Vec4i, int, int, int> > Crop;
664 TEST_P(Crop, Accuracy)
665 {
666     Vec4i inpShapeVec = get<0>(GetParam());
667     Vec4i sizShapeVec = get<1>(GetParam());
668     int axis = get<2>(GetParam());
669     int numOffsets = get<3>(GetParam());
670     int offsetVal = get<4>(GetParam());
671     const int inpShape[] = {inpShapeVec[0], inpShapeVec[1], inpShapeVec[2], inpShapeVec[3]};
672     const int sizShape[] = {sizShapeVec[0], sizShapeVec[1], sizShapeVec[2], sizShapeVec[3]};
673
674     // Create a network with two inputs. Crop layer crops a first input to
675     // the size of a second one.
676     // See http://caffe.berkeleyvision.org/tutorial/layers/crop.html
677     Net net;
678
679     LayerParams lp;
680     lp.name = "testCrop";
681     lp.type = "Crop";
682     lp.set("axis", axis);
683     if (numOffsets > 0)
684     {
685         std::vector<int> offsets(numOffsets, offsetVal);
686         lp.set("offset", DictValue::arrayInt<int*>(&offsets[0], offsets.size()));
687     }
688     else
689         offsetVal = 0;
690     int id = net.addLayerToPrev(lp.name, lp.type, lp);
691     net.connect(0, 1, id, 1);
692
693     Mat inpImage(4, inpShape, CV_32F);
694     Mat sizImage(4, sizShape, CV_32F);
695     randu(inpImage, -1, 1);
696     randu(sizImage, -1, 1);
697
698     std::vector<String> inpNames(2);
699     inpNames[0] = "cropImage";
700     inpNames[1] = "sizImage";
701     net.setInputsNames(inpNames);
702     net.setInput(inpImage, inpNames[0]);
703     net.setInput(sizImage, inpNames[1]);
704     net.setPreferableBackend(DNN_BACKEND_OPENCV);
705
706     // There are a few conditions that represent invalid input to the crop
707     // layer, so in those cases we want to verify an exception is thrown.
708
709     bool shouldThrowException = false;
710     if (numOffsets > 1 && numOffsets != 4 - axis)
711         shouldThrowException = true;
712     else
713         for (int i = axis; i < 4; i++)
714             if (sizShape[i] + offsetVal > inpShape[i])
715                 shouldThrowException = true;
716
717     Mat out;
718     if (shouldThrowException)
719     {
720         ASSERT_ANY_THROW(out = net.forward());
721         return;
722     }
723     else
724         out = net.forward();
725
726     // Finally, compare the cropped output blob from the DNN layer (out)
727     // to a reference blob (ref) that we compute here.
728
729     std::vector<Range> crop_range;
730     crop_range.resize(4, Range::all());
731     for (int i = axis; i < 4; i++)
732         crop_range[i] = Range(offsetVal, sizShape[i] + offsetVal);
733
734     Mat ref(sizImage.dims, sizImage.size, CV_32F);
735     inpImage(&crop_range[0]).copyTo(ref);
736     normAssert(out, ref);
737 }
738
739 INSTANTIATE_TEST_CASE_P(Layer_Test, Crop, Combine(
740 /*input blob shape*/    Values(Vec4i(1, 3, 20, 30)),
741 /*cropsize blob shape*/ Values(Vec4i(1, 3, 10, 12)),
742 /*start axis*/          Values(0, 1, 2),
743 /*number of offsets*/   Values(0, 1, 2, 4),
744 /*offset value*/        Values(3, 4)
745 ));
746
747 // Check that by default average pooling layer should not count zero padded values
748 // into the normalization area.
749 TEST_P(Test_Caffe_layers, Average_pooling_kernel_area)
750 {
751     LayerParams lp;
752     lp.name = "testAvePool";
753     lp.type = "Pooling";
754     lp.set("kernel_size", 2);
755     lp.set("stride", 2);
756     lp.set("pool", "AVE");
757
758     Net net;
759     net.addLayerToPrev(lp.name, lp.type, lp);
760     // 1 2 | 3
761     // 4 5 | 6
762     // ----+--
763     // 7 8 | 9
764     Mat inp = (Mat_<float>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
765     Mat ref = (Mat_<float>(2, 2) << (1 + 2 + 4 + 5) / 4.f, (3 + 6) / 2.f, (7 + 8) / 2.f, 9);
766     Mat tmp = blobFromImage(inp);
767     net.setInput(blobFromImage(inp));
768     net.setPreferableBackend(backend);
769     net.setPreferableTarget(target);
770     Mat out = net.forward();
771     normAssert(out, blobFromImage(ref));
772 }
773
774 // Test PriorBoxLayer in case of no aspect ratios (just squared proposals).
775 TEST_P(Test_Caffe_layers, PriorBox_squares)
776 {
777     if (backend == DNN_BACKEND_INFERENCE_ENGINE)
778         applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD);
779
780     LayerParams lp;
781     lp.name = "testPriorBox";
782     lp.type = "PriorBox";
783     lp.set("min_size", 2);
784     lp.set("flip", true);
785     lp.set("clip", true);
786     float variance[] = {0.1f, 0.1f, 0.2f, 0.2f};
787     float aspectRatios[] = {1.0f};  // That should be ignored.
788     lp.set("variance", DictValue::arrayReal<float*>(&variance[0], 4));
789     lp.set("aspect_ratio", DictValue::arrayReal<float*>(&aspectRatios[0], 1));
790
791     Net net;
792     int id = net.addLayerToPrev(lp.name, lp.type, lp);
793     net.connect(0, 0, id, 1);  // The second input is an input image. Shapes are used for boxes normalization.
794     Mat inp(1, 2, CV_32F);
795     randu(inp, -1, 1);
796     net.setInput(blobFromImage(inp));
797     net.setPreferableBackend(backend);
798     net.setPreferableTarget(target);
799     Mat out = net.forward();
800
801     Mat ref = (Mat_<float>(4, 4) << 0.0, 0.0, 0.75, 1.0,
802                                        0.25, 0.0, 1.0, 1.0,
803                                        0.1f, 0.1f, 0.2f, 0.2f,
804                                        0.1f, 0.1f, 0.2f, 0.2f);
805     double l1 = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 2e-5 : 1e-5;
806     normAssert(out.reshape(1, 4), ref, "", l1);
807 }
808
809 typedef TestWithParam<tuple<int, int> > Layer_Test_DWconv_Prelu;
810 TEST_P(Layer_Test_DWconv_Prelu, Accuracy)
811 {
812     // Test case
813     // input       img size 3x16x16  value all 1
814     //   |
815     //   v
816     // dw_conv     weight[0]=-1 weight[1]=-2 weight[2]=-3   bias={1,2,3}
817     //   |
818     //   v
819     // prelu       weight={1,2,3}
820     //   |
821     //   v
822     // output      out size 3x14x14  if right: out[0]=-8 out[0]=-32 out[0]=-72
823     //             but current opencv output: out[0]=-24 out[0]=-48 out[0]=-72
824
825     const int num_input = get<0>(GetParam());   //inpChannels
826     const int group = 3;                        //outChannels=group when group>1
827     const int num_output = get<1>(GetParam());
828     const int kernel_depth = num_input/group;
829     CV_Assert_N(num_output >= group, num_output % group == 0, num_input % group == 0);
830
831     Net net;
832     //layer 1: dwconv
833     LayerParams lp;
834     lp.name = "dwconv";
835     lp.type = "Convolution";
836     lp.set("kernel_size", 3);
837     lp.set("num_output", num_output);
838     lp.set("pad", 0);
839     lp.set("group", group);
840     lp.set("stride", 1);
841     lp.set("engine", "CAFFE");
842     lp.set("bias_term", "true");
843
844     std::vector<int> weightsShape(4);
845     weightsShape[0] = num_output;   // #outChannels
846     weightsShape[1] = kernel_depth; // #inpChannels / group
847     weightsShape[2] = 3;            // height
848     weightsShape[3] = 3;            // width
849     Mat weights(weightsShape, CV_32F, Scalar(1));
850
851     //assign weights
852     for (int i = 0; i < weightsShape[0]; ++i)
853     {
854         for (int j = 0; j < weightsShape[1]; ++j)
855         {
856             for (int k = 0; k < weightsShape[2]; ++k)
857             {
858                 for (int l = 0; l < weightsShape[3]; ++l)
859                 {
860                     weights.ptr<float>(i, j, k)[l]=-1*(i+1);
861                 }
862             }
863         }
864     }
865     lp.blobs.push_back(weights);
866
867     //assign bias
868     Mat bias(1, num_output, CV_32F, Scalar(1));
869     for (int i = 0; i < 1; ++i)
870     {
871         for (int j = 0; j < num_output; ++j)
872         {
873             bias.ptr<float>(i)[j]=j+1;
874         }
875     }
876     lp.blobs.push_back(bias);
877     net.addLayerToPrev(lp.name, lp.type, lp);
878
879     //layer 2: prelu
880     LayerParams lpr;
881     lpr.name = "dw_relu";
882     lpr.type = "PReLU";
883     Mat weightsp(1, num_output, CV_32F, Scalar(1));
884
885     //assign weights
886     for (int i = 0; i < 1; ++i)
887     {
888         for (int j = 0; j < num_output; ++j)
889         {
890             weightsp.ptr<float>(i)[j]=j+1;
891         }
892     }
893
894     lpr.blobs.push_back(weightsp);
895     net.addLayerToPrev(lpr.name, lpr.type, lpr);
896
897     int shape[] = {1, num_input, 16, 16};
898     Mat in_blob(4, &shape[0], CV_32FC1, Scalar(1));
899
900     net.setPreferableBackend(DNN_BACKEND_OPENCV);
901     net.setInput(in_blob);
902     Mat out = net.forward();
903
904     //assign target
905     std::vector<int> outShape(4);
906     outShape[0] = 1;
907     outShape[1] = num_output;       // outChannels
908     outShape[2] = 14;          // height
909     outShape[3] = 14;          // width
910     Mat target(outShape, CV_32F, Scalar(1));
911     for (int i = 0; i < outShape[0]; ++i)
912     {
913         for (int j = 0; j < outShape[1]; ++j)
914         {
915             for (int k = 0; k < outShape[2]; ++k)
916             {
917                 for (int l = 0; l < outShape[3]; ++l)
918                 {
919                     target.ptr<float>(i, j, k)[l]=(-9*kernel_depth*(j+1)+j+1)*(j+1);
920                 }
921             }
922         }
923     }
924
925     normAssert(out, target);
926 }
927 INSTANTIATE_TEST_CASE_P(/**/, Layer_Test_DWconv_Prelu, Combine(Values(3, 6), Values(3, 6)));
928
929 #ifdef HAVE_INF_ENGINE
930 // Using Intel's Model Optimizer generate .xml and .bin files:
931 // ./ModelOptimizer -w /path/to/caffemodel -d /path/to/prototxt \
932 //                  -p FP32 -i -b ${batch_size} -o /path/to/output/folder
933 typedef testing::TestWithParam<Target> Layer_Test_Convolution_DLDT;
934 TEST_P(Layer_Test_Convolution_DLDT, Accuracy)
935 {
936     Target targetId = GetParam();
937
938     std::string suffix = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) ? "_fp16" : "";
939     Net netDefault = readNet(_tf("layer_convolution.caffemodel"), _tf("layer_convolution.prototxt"));
940     Net net = readNet(_tf("layer_convolution" + suffix + ".xml"), _tf("layer_convolution" + suffix + ".bin"));
941
942     Mat inp = blobFromNPY(_tf("blob.npy"));
943
944     netDefault.setInput(inp);
945     netDefault.setPreferableBackend(DNN_BACKEND_OPENCV);
946     Mat outDefault = netDefault.forward();
947
948     net.setInput(inp);
949     net.setPreferableTarget(targetId);
950
951     Mat out = net.forward();
952
953     double l1 = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) ? 1.5e-3 : 1e-5;
954     double lInf = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) ? 1.8e-2 : 1e-4;
955     normAssert(outDefault, out, "", l1, lInf);
956
957     std::vector<int> outLayers = net.getUnconnectedOutLayers();
958     ASSERT_EQ(net.getLayer(outLayers[0])->name, "output");
959     ASSERT_EQ(net.getLayer(outLayers[0])->type, "Convolution");
960 }
961
962 TEST_P(Layer_Test_Convolution_DLDT, setInput_uint8)
963 {
964     Target targetId = GetParam();
965     Mat inp = blobFromNPY(_tf("blob.npy"));
966
967     Mat inputs[] = {Mat(inp.dims, inp.size, CV_8U), Mat()};
968     randu(inputs[0], 0, 255);
969     inputs[0].convertTo(inputs[1], CV_32F);
970
971     std::string suffix = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) ? "_fp16" : "";
972
973     Mat outs[2];
974     for (int i = 0; i < 2; ++i)
975     {
976         Net net = readNet(_tf("layer_convolution" + suffix + ".xml"), _tf("layer_convolution" + suffix + ".bin"));
977         net.setPreferableTarget(targetId);
978         net.setInput(inputs[i]);
979         outs[i] = net.forward();
980         ASSERT_EQ(outs[i].type(), CV_32F);
981     }
982     if (targetId != DNN_TARGET_MYRIAD)
983         normAssert(outs[0], outs[1]);
984 }
985
986 TEST_P(Layer_Test_Convolution_DLDT, multithreading)
987 {
988     Target targetId = GetParam();
989     std::string suffix = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) ? "_fp16" : "";
990     std::string xmlPath = _tf("layer_convolution" + suffix + ".xml");
991     std::string binPath = _tf("layer_convolution" + suffix + ".bin");
992     Net firstNet = readNet(xmlPath, binPath);
993     Net secondNet = readNet(xmlPath, binPath);
994     Mat inp = blobFromNPY(_tf("blob.npy"));
995
996     firstNet.setInput(inp);
997     secondNet.setInput(inp);
998     firstNet.setPreferableTarget(targetId);
999     secondNet.setPreferableTarget(targetId);
1000
1001     Mat out1, out2;
1002     std::thread t1([&]{out1 = firstNet.forward();});
1003     std::thread t2([&]{out2 = secondNet.forward();});
1004
1005     t1.join();
1006     t2.join();
1007
1008     Mat ref = blobFromNPY(_tf("layer_convolution.npy"));
1009     double l1 = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) ? 1.5e-3 : 1e-5;
1010     double lInf = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) ? 1.8e-2 : 1e-4;
1011     normAssert(out1, ref, "first thread", l1, lInf);
1012     normAssert(out2, ref, "second thread", l1, lInf);
1013 }
1014
1015 INSTANTIATE_TEST_CASE_P(/**/, Layer_Test_Convolution_DLDT,
1016     testing::ValuesIn(getAvailableTargets(DNN_BACKEND_INFERENCE_ENGINE)));
1017
1018 // 1. Create a .prototxt file with the following network:
1019 // layer {
1020 //   type: "Input" name: "data" top: "data"
1021 //   input_param { shape { dim: 1 dim: 2 dim: 3 } }
1022 // }
1023 // layer {
1024 //   type: "Input" name: "second_input" top: "second_input"
1025 //   input_param { shape { dim: 1 dim: 2 dim: 3 } }
1026 // }
1027 // layer {
1028 //  type: "Eltwise" name: "output" top: "output"
1029 //  bottom: "data" bottom: "second_input"
1030 //  eltwise_param { operation: SUM }
1031 // }
1032 //
1033 // 2. Create a .caffemodel file using Caffe:
1034 //
1035 // import caffe
1036 // net = caffe.Net('/path/to/prototxt', caffe.TEST)
1037 // net.save('/path/to/caffemodel')
1038 //
1039 // 3. Convert using ModelOptimizer.
1040 typedef testing::TestWithParam<tuple<int, int, Target, std::vector<int> > > Test_DLDT_two_inputs_3dim;
1041 TEST_P(Test_DLDT_two_inputs_3dim, as_IR)
1042 {
1043     int firstInpType = get<0>(GetParam());
1044     int secondInpType = get<1>(GetParam());
1045     Target targetId = get<2>(GetParam());
1046
1047     std::string suffix = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) ? "_fp16" : "";
1048     Net net = readNet(_tf("net_two_inputs" + suffix + ".xml"), _tf("net_two_inputs.bin"));
1049     std::vector<int> inpSize = get<3>(GetParam());
1050     Mat firstInp(3, inpSize.data(), firstInpType);
1051     Mat secondInp(3, inpSize.data(), secondInpType);
1052     randu(firstInp, 0, 255);
1053     randu(secondInp, 0, 255);
1054
1055     net.setInput(firstInp, "data");
1056     net.setInput(secondInp, "second_input");
1057     net.setPreferableTarget(targetId);
1058
1059     double l1 = ((targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) &&
1060                  (firstInpType == CV_32F || secondInpType == CV_32F)) ? 0.06 : 0.0;
1061     double lInf = ((targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) &&
1062                    (firstInpType == CV_32F || secondInpType == CV_32F)) ? 0.23 : 0.0;
1063
1064     Mat out = net.forward();
1065
1066     Mat ref;
1067     cv::add(firstInp, secondInp, ref, Mat(), CV_32F);
1068     normAssert(out, ref, "", l1, lInf);
1069 }
1070
1071 std::vector< std::vector<int> > list_sizes{ {1, 2, 3}, {3, 2, 1}, {5, 5, 5}, {13, 7, 11} };
1072
1073 INSTANTIATE_TEST_CASE_P(/*nothing*/, Test_DLDT_two_inputs_3dim, Combine(
1074   Values(CV_8U, CV_32F), Values(CV_8U, CV_32F),
1075   testing::ValuesIn(getAvailableTargets(DNN_BACKEND_INFERENCE_ENGINE)),
1076   testing::ValuesIn(list_sizes)
1077 ));
1078
1079 typedef testing::TestWithParam<tuple<int, int, Target> > Test_DLDT_two_inputs;
1080 TEST_P(Test_DLDT_two_inputs, as_backend)
1081 {
1082     static const float kScale = 0.5f;
1083     static const float kScaleInv = 1.0f / kScale;
1084
1085     Target targetId = get<2>(GetParam());
1086
1087     Net net;
1088     LayerParams lp;
1089     lp.type = "Eltwise";
1090     lp.name = "testLayer";
1091     lp.set("operation", "sum");
1092     int eltwiseId = net.addLayerToPrev(lp.name, lp.type, lp);  // connect to a first input
1093     net.connect(0, 1, eltwiseId, 1);  // connect to a second input
1094
1095     int inpSize[] = {1, 2, 3, 4};
1096     Mat firstInp(4, &inpSize[0], get<0>(GetParam()));
1097     Mat secondInp(4, &inpSize[0], get<1>(GetParam()));
1098     randu(firstInp, 0, 255);
1099     randu(secondInp, 0, 255);
1100
1101     net.setInputsNames({"data", "second_input"});
1102     net.setInput(firstInp, "data", kScale);
1103     net.setInput(secondInp, "second_input", kScaleInv);
1104     net.setPreferableBackend(DNN_BACKEND_INFERENCE_ENGINE);
1105     net.setPreferableTarget(targetId);
1106     Mat out = net.forward();
1107
1108     Mat ref;
1109     addWeighted(firstInp, kScale, secondInp, kScaleInv, 0, ref, CV_32F);
1110     // Output values are in range [0, 637.5].
1111     double l1 = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) ? 0.06 : 1e-6;
1112     double lInf = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) ? 0.3 : 1e-5;
1113     normAssert(out, ref, "", l1, lInf);
1114 }
1115
1116 INSTANTIATE_TEST_CASE_P(/*nothing*/, Test_DLDT_two_inputs, Combine(
1117   Values(CV_8U, CV_32F), Values(CV_8U, CV_32F),
1118   testing::ValuesIn(getAvailableTargets(DNN_BACKEND_INFERENCE_ENGINE))
1119 ));
1120
1121 class UnsupportedLayer : public Layer
1122 {
1123 public:
1124     UnsupportedLayer(const LayerParams &params) {}
1125
1126     static Ptr<Layer> create(const LayerParams& params)
1127     {
1128         return Ptr<Layer>(new UnsupportedLayer(params));
1129     }
1130
1131     virtual bool supportBackend(int backendId) CV_OVERRIDE
1132     {
1133         return backendId == DNN_BACKEND_OPENCV;
1134     }
1135
1136     virtual void forward(cv::InputArrayOfArrays inputs, cv::OutputArrayOfArrays outputs, cv::OutputArrayOfArrays internals) CV_OVERRIDE {}
1137 };
1138
1139 TEST(Test_DLDT, fused_output)
1140 {
1141     static const int kNumChannels = 3;
1142     CV_DNN_REGISTER_LAYER_CLASS(Unsupported, UnsupportedLayer);
1143     Net net;
1144     {
1145         LayerParams lp;
1146         lp.set("kernel_size", 1);
1147         lp.set("num_output", 3);
1148         lp.set("bias_term", false);
1149         lp.type = "Convolution";
1150         lp.name = "testConv";
1151         lp.blobs.push_back(Mat({kNumChannels, 1, 1, 1}, CV_32F, Scalar(1)));
1152         net.addLayerToPrev(lp.name, lp.type, lp);
1153     }
1154     {
1155         LayerParams lp;
1156         lp.set("bias_term", false);
1157         lp.type = "Scale";
1158         lp.name = "testScale";
1159         lp.blobs.push_back(Mat({kNumChannels}, CV_32F, Scalar(1)));
1160         net.addLayerToPrev(lp.name, lp.type, lp);
1161     }
1162     {
1163         LayerParams lp;
1164         net.addLayerToPrev("unsupported_layer", "Unsupported", lp);
1165     }
1166     net.setPreferableBackend(DNN_BACKEND_INFERENCE_ENGINE);
1167     net.setInput(Mat({1, 1, 1, 1}, CV_32FC1, Scalar(1)));
1168     ASSERT_NO_THROW(net.forward());
1169     LayerFactory::unregisterLayer("Unsupported");
1170 }
1171
1172 TEST(Test_DLDT, multiple_networks)
1173 {
1174     Net nets[2];
1175     for (int i = 0; i < 2; ++i)
1176     {
1177         nets[i].setInputsNames(std::vector<String>(1, format("input_%d", i)));
1178
1179         LayerParams lp;
1180         lp.set("kernel_size", 1);
1181         lp.set("num_output", 1);
1182         lp.set("bias_term", false);
1183         lp.type = "Convolution";
1184         lp.name = format("testConv_%d", i);
1185         lp.blobs.push_back(Mat({1, 1, 1, 1}, CV_32F, Scalar(1 + i)));
1186         nets[i].addLayerToPrev(lp.name, lp.type, lp);
1187         nets[i].setPreferableBackend(DNN_BACKEND_INFERENCE_ENGINE);
1188         nets[i].setInput(Mat({1, 1, 1, 1}, CV_32FC1, Scalar(1)));
1189     }
1190     Mat out_1 = nets[0].forward();
1191     Mat out_2 = nets[1].forward();
1192     // After the second model is initialized we try to receive an output from the first network again.
1193     out_1 = nets[0].forward();
1194     normAssert(2 * out_1, out_2);
1195 }
1196 #endif  // HAVE_INF_ENGINE
1197
1198 // Test a custom layer.
1199 class CustomInterpLayer CV_FINAL : public Layer
1200 {
1201 public:
1202     CustomInterpLayer(const LayerParams &params) : Layer(params)
1203     {
1204         zoomFactor = params.get<int>("zoom_factor", 0);
1205         outWidth = params.get<int>("width", 0);
1206         outHeight = params.get<int>("height", 0);
1207     }
1208
1209     static Ptr<Layer> create(LayerParams& params)
1210     {
1211         return Ptr<Layer>(new CustomInterpLayer(params));
1212     }
1213
1214     virtual bool getMemoryShapes(const std::vector<std::vector<int> > &inputs,
1215                                  const int requiredOutputs,
1216                                  std::vector<std::vector<int> > &outputs,
1217                                  std::vector<std::vector<int> > &internals) const CV_OVERRIDE
1218     {
1219         const int batchSize = inputs[0][0];
1220         const int numChannels = inputs[0][1];
1221         const int inpHeight = inputs[0][2];
1222         const int inpWidth = inputs[0][3];
1223
1224         std::vector<int> outShape(4);
1225         outShape[0] = batchSize;
1226         outShape[1] = numChannels;
1227         outShape[2] = outHeight != 0 ? outHeight : (inpHeight + (inpHeight - 1) * (zoomFactor - 1));
1228         outShape[3] = outWidth != 0 ? outWidth : (inpWidth + (inpWidth - 1) * (zoomFactor - 1));
1229         outputs.assign(1, outShape);
1230         return false;
1231     }
1232
1233     virtual void finalize(InputArrayOfArrays, OutputArrayOfArrays outputs_arr) CV_OVERRIDE
1234     {
1235         std::vector<Mat> outputs;
1236         outputs_arr.getMatVector(outputs);
1237
1238         if (!outWidth && !outHeight)
1239         {
1240             outHeight = outputs[0].size[2];
1241             outWidth = outputs[0].size[3];
1242         }
1243     }
1244
1245     // Implementation of this custom layer is based on https://github.com/cdmh/deeplab-public/blob/master/src/caffe/layers/interp_layer.cpp
1246     void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE
1247     {
1248         CV_TRACE_FUNCTION();
1249         CV_TRACE_ARG_VALUE(name, "name", name.c_str());
1250
1251         if (inputs_arr.depth() == CV_16S)
1252         {
1253             forward_fallback(inputs_arr, outputs_arr, internals_arr);
1254             return;
1255         }
1256
1257         std::vector<Mat> inputs, outputs;
1258         inputs_arr.getMatVector(inputs);
1259         outputs_arr.getMatVector(outputs);
1260
1261         Mat& inp = inputs[0];
1262         Mat& out = outputs[0];
1263         const float* inpData = (float*)inp.data;
1264         float* outData = (float*)out.data;
1265
1266         const int batchSize = inp.size[0];
1267         const int numChannels = inp.size[1];
1268         const int inpHeight = inp.size[2];
1269         const int inpWidth = inp.size[3];
1270
1271         const float rheight = (outHeight > 1) ? static_cast<float>(inpHeight - 1) / (outHeight - 1) : 0.f;
1272         const float rwidth = (outWidth > 1) ? static_cast<float>(inpWidth - 1) / (outWidth - 1) : 0.f;
1273         for (int h2 = 0; h2 < outHeight; ++h2)
1274         {
1275             const float h1r = rheight * h2;
1276             const int h1 = h1r;
1277             const int h1p = (h1 < inpHeight - 1) ? 1 : 0;
1278             const float h1lambda = h1r - h1;
1279             const float h0lambda = 1.f - h1lambda;
1280             for (int w2 = 0; w2 < outWidth; ++w2)
1281             {
1282                 const float w1r = rwidth * w2;
1283                 const int w1 = w1r;
1284                 const int w1p = (w1 < inpWidth - 1) ? 1 : 0;
1285                 const float w1lambda = w1r - w1;
1286                 const float w0lambda = 1.f - w1lambda;
1287                 const float* pos1 = inpData + h1 * inpWidth + w1;
1288                 float* pos2 = outData + h2 * outWidth + w2;
1289                 for (int c = 0; c < batchSize * numChannels; ++c)
1290                 {
1291                     pos2[0] =
1292                       h0lambda * (w0lambda * pos1[0] + w1lambda * pos1[w1p]) +
1293                       h1lambda * (w0lambda * pos1[h1p * inpWidth] + w1lambda * pos1[h1p * inpWidth + w1p]);
1294                     pos1 += inpWidth * inpHeight;
1295                     pos2 += outWidth * outHeight;
1296                 }
1297             }
1298         }
1299     }
1300
1301 private:
1302     int outWidth, outHeight, zoomFactor;
1303 };
1304
1305 #ifndef OPENCV_DNN_EXTERNAL_PROTOBUF
1306 TEST_P(Test_Caffe_layers, Interp)
1307 #else
1308 TEST_P(Test_Caffe_layers, DISABLED_Interp)  // requires patched protobuf (available in OpenCV source tree only)
1309 #endif
1310 {
1311     if (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD)
1312         applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD);
1313
1314     // Test a custom layer.
1315     CV_DNN_REGISTER_LAYER_CLASS(Interp, CustomInterpLayer);
1316     try
1317     {
1318         testLayerUsingCaffeModels("layer_interp", false, false);
1319     }
1320     catch (...)
1321     {
1322         LayerFactory::unregisterLayer("Interp");
1323         throw;
1324     }
1325     LayerFactory::unregisterLayer("Interp");
1326
1327     // Test an implemented layer.
1328     testLayerUsingCaffeModels("layer_interp", false, false);
1329 }
1330
1331 INSTANTIATE_TEST_CASE_P(/*nothing*/, Test_Caffe_layers, dnnBackendsAndTargets());
1332
1333 TEST(Layer_Test_PoolingIndices, Accuracy)
1334 {
1335     Net net;
1336
1337     LayerParams lp;
1338     lp.set("pool", "max");
1339     lp.set("kernel_w", 2);
1340     lp.set("kernel_h", 2);
1341     lp.set("stride_w", 2);
1342     lp.set("stride_h", 2);
1343     lp.set("pad_w", 0);
1344     lp.set("pad_h", 0);
1345     lp.name = "testLayer.name";  // This test also checks that OpenCV lets use names with dots.
1346     lp.type = "Pooling";
1347     net.addLayerToPrev(lp.name, lp.type, lp);
1348
1349     Mat inp(10, 10, CV_8U);
1350     randu(inp, 0, 255);
1351
1352     Mat maxValues(5, 5, CV_32F, Scalar(-1)), indices(5, 5, CV_32F, Scalar(-1));
1353     for (int y = 0; y < 10; ++y)
1354     {
1355         int dstY = y / 2;
1356         for (int x = 0; x < 10; ++x)
1357         {
1358             int dstX = x / 2;
1359             uint8_t val = inp.at<uint8_t>(y, x);
1360             if ((float)inp.at<uint8_t>(y, x) > maxValues.at<float>(dstY, dstX))
1361             {
1362                 maxValues.at<float>(dstY, dstX) = val;
1363                 indices.at<float>(dstY, dstX) = y * 10 + x;
1364             }
1365         }
1366     }
1367     net.setPreferableBackend(DNN_BACKEND_OPENCV);
1368     net.setInput(blobFromImage(inp));
1369
1370     std::vector<Mat> outputs;
1371     net.forward(outputs, lp.name);
1372     normAssert(maxValues, outputs[0].reshape(1, 5));
1373     normAssert(indices, outputs[1].reshape(1, 5));
1374 }
1375
1376 typedef testing::TestWithParam<tuple<Vec4i, int, tuple<Backend, Target> > > Layer_Test_ShuffleChannel;
1377 TEST_P(Layer_Test_ShuffleChannel, Accuracy)
1378 {
1379     Vec4i inpShapeVec = get<0>(GetParam());
1380     int group = get<1>(GetParam());
1381     ASSERT_EQ(inpShapeVec[1] % group, 0);
1382     const int groupSize = inpShapeVec[1] / group;
1383     int backendId = get<0>(get<2>(GetParam()));
1384     int targetId = get<1>(get<2>(GetParam()));
1385
1386     Net net;
1387     LayerParams lp;
1388     lp.set("group", group);
1389     lp.type = "ShuffleChannel";
1390     lp.name = "testLayer";
1391     net.addLayerToPrev(lp.name, lp.type, lp);
1392
1393     const int inpShape[] = {inpShapeVec[0], inpShapeVec[1], inpShapeVec[2], inpShapeVec[3]};
1394     Mat inp(4, inpShape, CV_32F);
1395     randu(inp, 0, 255);
1396
1397     net.setInput(inp);
1398     net.setPreferableBackend(backendId);
1399     net.setPreferableTarget(targetId);
1400     Mat out = net.forward();
1401
1402     double l1 = (targetId == DNN_TARGET_OPENCL_FP16) ? 5e-2 : 1e-5;
1403     double lInf = (targetId == DNN_TARGET_OPENCL_FP16) ? 7e-2 : 1e-4;
1404     for (int n = 0; n < inpShapeVec[0]; ++n)
1405     {
1406         for (int c = 0; c < inpShapeVec[1]; ++c)
1407         {
1408             Mat outChannel = getPlane(out, n, c);
1409             Mat inpChannel = getPlane(inp, n, groupSize * (c % group) + c / group);
1410             normAssert(outChannel, inpChannel, "", l1, lInf);
1411         }
1412     }
1413 }
1414 INSTANTIATE_TEST_CASE_P(/**/, Layer_Test_ShuffleChannel, Combine(
1415 /*input shape*/  Values(Vec4i(1, 6, 5, 7), Vec4i(3, 12, 1, 4)),
1416 /*group*/        Values(1, 2, 3, 6), dnnBackendsAndTargets(/*with IE*/ false)
1417 ));
1418
1419 // Check if relu is not fused to convolution if we requested it's output
1420 TEST(Layer_Test_Convolution, relu_fusion)
1421 {
1422     Net net;
1423     {
1424         LayerParams lp;
1425         lp.set("kernel_size", 1);
1426         lp.set("num_output", 1);
1427         lp.set("bias_term", false);
1428         lp.type = "Convolution";
1429         lp.name = "testConv";
1430
1431         int weightsShape[] = {1, 1, 1, 1};
1432         Mat weights(4, &weightsShape[0], CV_32F, Scalar(1));
1433         lp.blobs.push_back(weights);
1434         net.addLayerToPrev(lp.name, lp.type, lp);
1435     }
1436     {
1437         LayerParams lp;
1438         lp.type = "ReLU";
1439         lp.name = "testReLU";
1440         net.addLayerToPrev(lp.name, lp.type, lp);
1441     }
1442     int sz[] = {1, 1, 2, 3};
1443     Mat input(4, &sz[0], CV_32F);
1444     randu(input, -1.0, -0.1);
1445     net.setInput(input);
1446     net.setPreferableBackend(DNN_BACKEND_OPENCV);
1447     Mat output = net.forward("testConv");
1448     normAssert(input, output);
1449 }
1450
1451 }} // namespace