96dffced4b55daed88d3dd4bde16c086adff34ce
[platform/upstream/opencv.git] / modules / dnn / src / layers / elementwise_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) 2013, OpenCV Foundation, all rights reserved.
14 // Copyright (C) 2017, Intel Corporation, all rights reserved.
15 // Third party copyrights are property of their respective owners.
16 //
17 // Redistribution and use in source and binary forms, with or without modification,
18 // are permitted provided that the following conditions are met:
19 //
20 //   * Redistribution's of source code must retain the above copyright notice,
21 //     this list of conditions and the following disclaimer.
22 //
23 //   * Redistribution's in binary form must reproduce the above copyright notice,
24 //     this list of conditions and the following disclaimer in the documentation
25 //     and/or other materials provided with the distribution.
26 //
27 //   * The name of the copyright holders may not be used to endorse or promote products
28 //     derived from this software without specific prior written permission.
29 //
30 // This software is provided by the copyright holders and contributors "as is" and
31 // any express or implied warranties, including, but not limited to, the implied
32 // warranties of merchantability and fitness for a particular purpose are disclaimed.
33 // In no event shall the Intel Corporation or contributors be liable for any direct,
34 // indirect, incidental, special, exemplary, or consequential damages
35 // (including, but not limited to, procurement of substitute goods or services;
36 // loss of use, data, or profits; or business interruption) however caused
37 // and on any theory of liability, whether in contract, strict liability,
38 // or tort (including negligence or otherwise) arising in any way out of
39 // the use of this software, even if advised of the possibility of such damage.
40 //
41 //M*/
42
43 #include "../precomp.hpp"
44 #include "layers_common.hpp"
45 #include "../op_halide.hpp"
46 #include "../op_inf_engine.hpp"
47 #include "../op_vkcom.hpp"
48 #include <opencv2/dnn/shape_utils.hpp>
49 #include <iostream>
50
51 #ifdef HAVE_OPENCL
52 #include "opencl_kernels_dnn.hpp"
53 #endif
54
55 namespace cv
56 {
57 namespace dnn
58 {
59
60 using std::abs;
61 using std::exp;
62 using std::tanh;
63 using std::pow;
64
65 template<typename Func>
66 class ElementWiseLayer : public Func::Layer
67 {
68 public:
69     class PBody : public cv::ParallelLoopBody
70     {
71     public:
72         const Func* func_;
73         const Mat* src_;
74         Mat* dst_;
75         int nstripes_;
76
77         PBody(const Func &func, const Mat &src, Mat& dst, int nstripes)
78         {
79             func_ = &func;
80             src_ = &src;
81             dst_ = &dst;
82             nstripes_ = nstripes;
83         }
84
85         void operator()(const Range &r) const CV_OVERRIDE
86         {
87             int nstripes = nstripes_, nsamples = 1, outCn = 1;
88             size_t planeSize = 1;
89
90             if (src_->dims > 1)
91             {
92                 nsamples = src_->size[0];
93                 outCn = src_->size[1];
94             }
95             else
96                 outCn = src_->size[0];
97
98             for (int i = 2; i < src_->dims; ++i)
99                 planeSize *= src_->size[i];
100
101             size_t stripeSize = (planeSize + nstripes - 1)/nstripes;
102             size_t stripeStart = r.start*stripeSize;
103             size_t stripeEnd = std::min(r.end*stripeSize, planeSize);
104
105             for( int i = 0; i < nsamples; i++ )
106             {
107                 const float* srcptr = src_->ptr<float>(i) + stripeStart;
108                 float* dstptr = dst_->ptr<float>(i) + stripeStart;
109                 func_->apply(srcptr, dstptr, (int)(stripeEnd - stripeStart), planeSize, 0, outCn);
110             }
111         }
112     };
113
114     ElementWiseLayer(const Func &f=Func()) : run_parallel(false) { func = f; }
115
116     virtual bool supportBackend(int backendId) CV_OVERRIDE
117     {
118         return func.supportBackend(backendId, this->preferableTarget);
119     }
120
121     virtual Ptr<BackendNode> tryAttach(const Ptr<BackendNode>& node) CV_OVERRIDE
122     {
123         switch (node->backendId)
124         {
125             case DNN_BACKEND_HALIDE:
126             {
127 #ifdef HAVE_HALIDE
128                 auto base = node.dynamicCast<HalideBackendNode>();
129                 Halide::Func& input = base->funcs.back();
130                 Halide::Var x("x"), y("y"), c("c"), n("n");
131                 Halide::Func top = (this->name.empty() ? Halide::Func() : Halide::Func(this->name));
132                 func.attachHalide(input(x, y, c, n), top);
133                 return Ptr<BackendNode>(new HalideBackendNode(base, top));
134 #endif  // HAVE_HALIDE
135                 break;
136             }
137         }
138         return Ptr<BackendNode>();
139     }
140
141     virtual Ptr<BackendNode> initHalide(const std::vector<Ptr<BackendWrapper> > &inputs) CV_OVERRIDE
142     {
143 #ifdef HAVE_HALIDE
144         Halide::Buffer<float> input = halideBuffer(inputs[0]);
145         Halide::Var x("x"), y("y"), c("c"), n("n");
146         Halide::Func top = (this->name.empty() ? Halide::Func() : Halide::Func(this->name));
147         func.attachHalide(input(x, y, c, n), top);
148         return Ptr<BackendNode>(new HalideBackendNode(top));
149 #endif  // HAVE_HALIDE
150         return Ptr<BackendNode>();
151     }
152
153 #ifdef HAVE_INF_ENGINE
154     virtual Ptr<BackendNode> initInfEngine(const std::vector<Ptr<BackendWrapper> >&) CV_OVERRIDE
155     {
156         InferenceEngine::Builder::Layer ieLayer = func.initInfEngineBuilderAPI();
157         ieLayer.setName(this->name);
158         return Ptr<BackendNode>(new InfEngineBackendNode(ieLayer));
159     }
160 #endif  // HAVE_INF_ENGINE
161
162     virtual Ptr<BackendNode> initVkCom(const std::vector<Ptr<BackendWrapper> >& inputs) CV_OVERRIDE
163     {
164 #ifdef HAVE_VULKAN
165         return Ptr<BackendNode>(new VkComBackendNode(inputs, func.initVkCom()));
166 #endif  // HAVE_VULKAN
167         return Ptr<BackendNode>();
168     }
169
170     virtual bool tryFuse(Ptr<dnn::Layer>& top) CV_OVERRIDE
171     {
172         return func.tryFuse(top);
173     }
174
175     void getScaleShift(Mat& scale_, Mat& shift_) const CV_OVERRIDE
176     {
177         func.getScaleShift(scale_, shift_);
178     }
179
180     bool getMemoryShapes(const std::vector<MatShape> &inputs,
181                          const int requiredOutputs,
182                          std::vector<MatShape> &outputs,
183                          std::vector<MatShape> &internals) const CV_OVERRIDE
184     {
185         Layer::getMemoryShapes(inputs, requiredOutputs, outputs, internals);
186         return true;
187     }
188
189     void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE
190     {
191         CV_TRACE_FUNCTION();
192
193         CV_OCL_RUN(IS_DNN_OPENCL_TARGET(this->preferableTarget),
194                    func.applyOCL(inputs_arr, outputs_arr, internals_arr))
195
196         if (inputs_arr.depth() == CV_16S)
197         {
198             Layer::forward_fallback(inputs_arr, outputs_arr, internals_arr);
199             return;
200         }
201
202         std::vector<Mat> inputs, outputs;
203         inputs_arr.getMatVector(inputs);
204         outputs_arr.getMatVector(outputs);
205
206         for (size_t i = 0; i < inputs.size(); i++)
207         {
208             const Mat &src = inputs[i];
209             Mat &dst = outputs[i];
210             CV_Assert(src.size == dst.size && src.type() == dst.type() &&
211                       src.isContinuous() && dst.isContinuous() && src.type() == CV_32F);
212
213             const int nstripes = getNumThreads();
214             PBody body(func, src, dst, nstripes);
215             parallel_for_(Range(0, nstripes), body, nstripes);
216         }
217     }
218
219     void forwardSlice(const float* src, float* dst, int len, size_t planeSize, int cn0, int cn1) const CV_OVERRIDE
220     {
221         func.apply(src, dst, len, planeSize, cn0, cn1);
222     }
223
224     virtual int64 getFLOPS(const std::vector<MatShape> &inputs,
225                            const std::vector<MatShape> &outputs) const CV_OVERRIDE
226     {
227         long flops = 0;
228         for (int i = 0; i < outputs.size(); i++)
229         {
230             flops += total(outputs[i]) * func.getFLOPSPerElement();
231         }
232         return flops;
233     }
234
235     Func func;
236     bool run_parallel;
237 };
238
239 #ifdef HAVE_OPENCL
240 static String oclGetTMacro(const UMat &m)
241 {
242     String str_name = ocl::typeToStr(m.type());
243
244     if (str_name == "short")
245         str_name = "half";
246
247     return format("-DT=%s -Dconvert_T=convert_%s ", str_name.c_str(), str_name.c_str());
248 }
249 #endif
250
251 struct ReLUFunctor
252 {
253     typedef ReLULayer Layer;
254     float slope;
255
256     explicit ReLUFunctor(float slope_=1.f) : slope(slope_) {}
257
258     bool supportBackend(int backendId, int)
259     {
260 #ifdef HAVE_INF_ENGINE
261         if (backendId == DNN_BACKEND_INFERENCE_ENGINE)
262             return slope >= 0 || !INF_ENGINE_VER_MAJOR_EQ(INF_ENGINE_RELEASE_2019R1);
263 #endif
264         return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE ||
265                backendId == DNN_BACKEND_VKCOM;
266     }
267
268     void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
269     {
270         float s = slope;
271         for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
272         {
273             int i = 0;
274 #if CV_SIMD128
275             v_float32x4 s4 = v_setall_f32(s), z = v_setzero_f32();
276             for( ; i <= len - 16; i += 16 )
277             {
278                 v_float32x4 x0 = v_load(srcptr + i);
279                 v_float32x4 x1 = v_load(srcptr + i + 4);
280                 v_float32x4 x2 = v_load(srcptr + i + 8);
281                 v_float32x4 x3 = v_load(srcptr + i + 12);
282                 x0 = v_select(x0 >= z, x0, x0*s4);
283                 x1 = v_select(x1 >= z, x1, x1*s4);
284                 x2 = v_select(x2 >= z, x2, x2*s4);
285                 x3 = v_select(x3 >= z, x3, x3*s4);
286                 v_store(dstptr + i, x0);
287                 v_store(dstptr + i + 4, x1);
288                 v_store(dstptr + i + 8, x2);
289                 v_store(dstptr + i + 12, x3);
290             }
291 #endif
292             for( ; i < len; i++ )
293             {
294                 float x = srcptr[i];
295                 dstptr[i] = x >= 0.f ? x : s*x;
296             }
297         }
298     }
299
300 #ifdef HAVE_OPENCL
301     bool initKernel(ocl::Kernel &ker, const UMat &src) const
302     {
303         const char *buildoptSlope = (slope == 0) ? "-DRELU_NO_SLOPE" : "";
304         String buildopt = oclGetTMacro(src) + buildoptSlope;
305
306         if (!ker.create("ReLUForward", ocl::dnn::activations_oclsrc, buildopt))
307             return false;
308
309         if (slope != 0)
310             ker.set(3, (float)slope);
311
312         return true;
313     }
314
315     bool applyOCL(InputArrayOfArrays inps, OutputArrayOfArrays outs, OutputArrayOfArrays internals)
316     {
317         std::vector<UMat> inputs;
318         std::vector<UMat> outputs;
319
320         inps.getUMatVector(inputs);
321         outs.getUMatVector(outputs);
322
323         for (size_t i = 0; i < inputs.size(); i++)
324         {
325             UMat& src = inputs[i];
326             UMat& dst = outputs[i];
327             CV_Assert(src.isContinuous() && dst.isContinuous() && !src.offset && !dst.offset);
328
329             ocl::Kernel kernel;
330             CV_Assert(initKernel(kernel, src));
331             kernel.set(0, (int)src.total());
332             kernel.set(1, ocl::KernelArg::PtrReadOnly(src));
333             kernel.set(2, ocl::KernelArg::PtrWriteOnly(dst));
334
335             size_t gSize = src.total();
336             CV_Assert(kernel.run(1, &gSize, NULL, false));
337         }
338
339         return true;
340     }
341 #endif
342
343 #ifdef HAVE_HALIDE
344     void attachHalide(const Halide::Expr& input, Halide::Func& top)
345     {
346         Halide::Var x("x"), y("y"), c("c"), n("n");
347         if (slope)
348         {
349             top(x, y, c, n) = select(input >= 0.0f, input, slope * input);
350         }
351         else
352         {
353             top(x, y, c, n) = max(input, 0.0f);
354         }
355     }
356 #endif  // HAVE_HALIDE
357
358 #ifdef HAVE_INF_ENGINE
359     InferenceEngine::Builder::Layer initInfEngineBuilderAPI()
360     {
361         return InferenceEngine::Builder::ReLULayer("").setNegativeSlope(slope);
362     }
363 #endif  // HAVE_INF_ENGINE
364
365 #ifdef HAVE_VULKAN
366     std::shared_ptr<vkcom::OpBase> initVkCom()
367     {
368         std::shared_ptr<vkcom::OpBase> op(new vkcom::OpReLU(slope));
369         return op;
370     }
371 #endif  // HAVE_VULKAN
372
373
374
375     bool tryFuse(Ptr<dnn::Layer>&) { return false; }
376
377     void getScaleShift(Mat&, Mat&) const {}
378
379     int64 getFLOPSPerElement() const { return 1; }
380 };
381
382 struct ReLU6Functor
383 {
384     typedef ReLU6Layer Layer;
385     float minValue, maxValue;
386
387     ReLU6Functor(float minValue_ = 0.0f, float maxValue_ = 6.0f)
388         : minValue(minValue_), maxValue(maxValue_)
389     {
390         CV_Assert(minValue <= maxValue);
391     }
392
393     bool supportBackend(int backendId, int)
394     {
395         return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE ||
396                backendId == DNN_BACKEND_INFERENCE_ENGINE;
397     }
398
399     void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
400     {
401         for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
402         {
403             int i = 0;
404 #if CV_SIMD128
405             v_float32x4 minV = v_setall_f32(minValue), maxV = v_setall_f32(maxValue);
406             for( ; i <= len - 16; i += 16 )
407             {
408                 v_float32x4 x0 = v_load(srcptr + i);
409                 v_float32x4 x1 = v_load(srcptr + i + 4);
410                 v_float32x4 x2 = v_load(srcptr + i + 8);
411                 v_float32x4 x3 = v_load(srcptr + i + 12);
412                 x0 = v_min(v_max(minV, x0), maxV);
413                 x1 = v_min(v_max(minV, x1), maxV);
414                 x2 = v_min(v_max(minV, x2), maxV);
415                 x3 = v_min(v_max(minV, x3), maxV);
416                 v_store(dstptr + i, x0);
417                 v_store(dstptr + i + 4, x1);
418                 v_store(dstptr + i + 8, x2);
419                 v_store(dstptr + i + 12, x3);
420             }
421 #endif
422             for( ; i < len; i++ )
423             {
424                 float x = srcptr[i];
425                 if (x >= minValue)
426                     dstptr[i] = x <= maxValue ? x : maxValue;
427                 else
428                     dstptr[i] = minValue;
429             }
430         }
431     }
432
433 #ifdef HAVE_OPENCL
434     bool applyOCL(InputArrayOfArrays inps, OutputArrayOfArrays outs, OutputArrayOfArrays internals)
435     {
436         std::vector<UMat> inputs;
437         std::vector<UMat> outputs;
438
439         inps.getUMatVector(inputs);
440         outs.getUMatVector(outputs);
441         String buildopt = oclGetTMacro(inputs[0]);
442
443         for (size_t i = 0; i < inputs.size(); i++)
444         {
445             UMat& src = inputs[i];
446             UMat& dst = outputs[i];
447
448             ocl::Kernel kernel("ReLU6Forward", ocl::dnn::activations_oclsrc, buildopt);
449             kernel.set(0, (int)src.total());
450             kernel.set(1, ocl::KernelArg::PtrReadOnly(src));
451             kernel.set(2, ocl::KernelArg::PtrWriteOnly(dst));
452             kernel.set(3, (float)minValue);
453             kernel.set(4, (float)maxValue);
454
455             size_t gSize = src.total();
456             CV_Assert(kernel.run(1, &gSize, NULL, false));
457         }
458
459         return true;
460     }
461 #endif
462
463 #ifdef HAVE_HALIDE
464     void attachHalide(const Halide::Expr& input, Halide::Func& top)
465     {
466         Halide::Var x("x"), y("y"), c("c"), n("n");
467         top(x, y, c, n) = clamp(input, minValue, maxValue);
468     }
469 #endif  // HAVE_HALIDE
470
471 #ifdef HAVE_INF_ENGINE
472     InferenceEngine::Builder::Layer initInfEngineBuilderAPI()
473     {
474         return InferenceEngine::Builder::ClampLayer("").setMinValue(minValue).setMaxValue(maxValue);
475     }
476 #endif  // HAVE_INF_ENGINE
477
478 #ifdef HAVE_VULKAN
479     std::shared_ptr<vkcom::OpBase> initVkCom()
480     {
481         // TODO: add vkcom implementation
482         return std::shared_ptr<vkcom::OpBase>();
483     }
484 #endif  // HAVE_VULKAN
485
486     bool tryFuse(Ptr<dnn::Layer>&) { return false; }
487
488     void getScaleShift(Mat&, Mat&) const {}
489
490     int64 getFLOPSPerElement() const { return 2; }
491 };
492
493 struct TanHFunctor
494 {
495     typedef TanHLayer Layer;
496
497     bool supportBackend(int backendId, int)
498     {
499         return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE ||
500                backendId == DNN_BACKEND_INFERENCE_ENGINE;
501     }
502
503     void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
504     {
505         for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
506         {
507             for( int i = 0; i < len; i++ )
508             {
509                 float x = srcptr[i];
510                 dstptr[i] = tanh(x);
511             }
512         }
513     }
514
515 #ifdef HAVE_OPENCL
516     bool applyOCL(InputArrayOfArrays inps, OutputArrayOfArrays outs, OutputArrayOfArrays internals)
517     {
518         std::vector<UMat> inputs;
519         std::vector<UMat> outputs;
520
521         inps.getUMatVector(inputs);
522         outs.getUMatVector(outputs);
523         String buildopt = oclGetTMacro(inputs[0]);
524
525         for (size_t i = 0; i < inputs.size(); i++)
526         {
527             UMat& src = inputs[i];
528             UMat& dst = outputs[i];
529
530             ocl::Kernel kernel("TanHForward", ocl::dnn::activations_oclsrc, buildopt);
531             kernel.set(0, (int)src.total());
532             kernel.set(1, ocl::KernelArg::PtrReadOnly(src));
533             kernel.set(2, ocl::KernelArg::PtrWriteOnly(dst));
534
535             size_t gSize = src.total();
536             CV_Assert(kernel.run(1, &gSize, NULL, false));
537         }
538
539         return true;
540     }
541 #endif
542
543 #ifdef HAVE_HALIDE
544     void attachHalide(const Halide::Expr& input, Halide::Func& top)
545     {
546         Halide::Var x("x"), y("y"), c("c"), n("n");
547         top(x, y, c, n) = tanh(input);
548     }
549 #endif  // HAVE_HALIDE
550
551 #ifdef HAVE_INF_ENGINE
552     InferenceEngine::Builder::Layer initInfEngineBuilderAPI()
553     {
554         return InferenceEngine::Builder::TanHLayer("");
555     }
556 #endif  // HAVE_INF_ENGINE
557
558 #ifdef HAVE_VULKAN
559     std::shared_ptr<vkcom::OpBase> initVkCom()
560     {
561         // TODO: add vkcom implementation
562         return std::shared_ptr<vkcom::OpBase>();
563     }
564 #endif  // HAVE_VULKAN
565
566     bool tryFuse(Ptr<dnn::Layer>&) { return false; }
567
568     void getScaleShift(Mat&, Mat&) const {}
569
570     int64 getFLOPSPerElement() const { return 1; }
571 };
572
573 struct SigmoidFunctor
574 {
575     typedef SigmoidLayer Layer;
576
577     bool supportBackend(int backendId, int)
578     {
579         return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE ||
580                backendId == DNN_BACKEND_INFERENCE_ENGINE;
581     }
582
583     void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
584     {
585         for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
586         {
587             for( int i = 0; i < len; i++ )
588             {
589                 float x = srcptr[i];
590                 dstptr[i] = 1.f/(1.f + exp(-x));
591             }
592         }
593     }
594
595 #ifdef HAVE_OPENCL
596     bool applyOCL(InputArrayOfArrays inps, OutputArrayOfArrays outs, OutputArrayOfArrays internals)
597     {
598         std::vector<UMat> inputs;
599         std::vector<UMat> outputs;
600
601         inps.getUMatVector(inputs);
602         outs.getUMatVector(outputs);
603         String buildopt = oclGetTMacro(inputs[0]);
604
605         for (size_t i = 0; i < inputs.size(); i++)
606         {
607             UMat& src = inputs[i];
608             UMat& dst = outputs[i];
609
610             ocl::Kernel kernel("SigmoidForward", ocl::dnn::activations_oclsrc, buildopt);
611             kernel.set(0, (int)src.total());
612             kernel.set(1, ocl::KernelArg::PtrReadOnly(src));
613             kernel.set(2, ocl::KernelArg::PtrWriteOnly(dst));
614
615             size_t gSize = src.total();
616             CV_Assert(kernel.run(1, &gSize, NULL, false));
617         }
618
619         return true;
620     }
621 #endif
622
623 #ifdef HAVE_HALIDE
624     void attachHalide(const Halide::Expr& input, Halide::Func& top)
625     {
626         Halide::Var x("x"), y("y"), c("c"), n("n");
627         top(x, y, c, n) = 1.0f / (1.0f + exp(-input));
628     }
629 #endif  // HAVE_HALIDE
630
631 #ifdef HAVE_INF_ENGINE
632     InferenceEngine::Builder::Layer initInfEngineBuilderAPI()
633     {
634         return InferenceEngine::Builder::SigmoidLayer("");
635     }
636 #endif  // HAVE_INF_ENGINE
637
638 #ifdef HAVE_VULKAN
639     std::shared_ptr<vkcom::OpBase> initVkCom()
640     {
641         // TODO: add vkcom implementation
642         return std::shared_ptr<vkcom::OpBase>();
643     }
644 #endif  // HAVE_VULKAN
645
646     bool tryFuse(Ptr<dnn::Layer>&) { return false; }
647
648     void getScaleShift(Mat&, Mat&) const {}
649
650     int64 getFLOPSPerElement() const { return 3; }
651 };
652
653 struct ELUFunctor
654 {
655     typedef ELULayer Layer;
656
657     explicit ELUFunctor() {}
658
659     bool supportBackend(int backendId, int)
660     {
661         return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE ||
662                backendId == DNN_BACKEND_INFERENCE_ENGINE;
663     }
664
665     void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
666     {
667         for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
668         {
669             for(int i = 0; i < len; i++ )
670             {
671                 float x = srcptr[i];
672                 dstptr[i] = x >= 0.f ? x : exp(x) - 1;
673             }
674         }
675     }
676
677 #ifdef HAVE_OPENCL
678     bool applyOCL(InputArrayOfArrays inps, OutputArrayOfArrays outs, OutputArrayOfArrays internals)
679     {
680         std::vector<UMat> inputs;
681         std::vector<UMat> outputs;
682
683         inps.getUMatVector(inputs);
684         outs.getUMatVector(outputs);
685         String buildopt = oclGetTMacro(inputs[0]);
686
687         for (size_t i = 0; i < inputs.size(); i++)
688         {
689             UMat& src = inputs[i];
690             UMat& dst = outputs[i];
691
692             ocl::Kernel kernel("ELUForward", ocl::dnn::activations_oclsrc, buildopt);
693             kernel.set(0, (int)src.total());
694             kernel.set(1, ocl::KernelArg::PtrReadOnly(src));
695             kernel.set(2, ocl::KernelArg::PtrWriteOnly(dst));
696
697             size_t gSize = src.total();
698             CV_Assert(kernel.run(1, &gSize, NULL, false));
699         }
700
701         return true;
702     }
703 #endif
704
705 #ifdef HAVE_HALIDE
706     void attachHalide(const Halide::Expr& input, Halide::Func& top)
707     {
708         Halide::Var x("x"), y("y"), c("c"), n("n");
709         top(x, y, c, n) = select(input >= 0.0f, input, exp(input) - 1);
710     }
711 #endif  // HAVE_HALIDE
712
713 #ifdef HAVE_INF_ENGINE
714     InferenceEngine::Builder::Layer initInfEngineBuilderAPI()
715     {
716         return InferenceEngine::Builder::ELULayer("");
717     }
718 #endif  // HAVE_INF_ENGINE
719
720 #ifdef HAVE_VULKAN
721     std::shared_ptr<vkcom::OpBase> initVkCom()
722     {
723         // TODO: add vkcom implementation
724         return std::shared_ptr<vkcom::OpBase>();
725     }
726 #endif  // HAVE_VULKAN
727
728     bool tryFuse(Ptr<dnn::Layer>&) { return false; }
729
730     void getScaleShift(Mat&, Mat&) const {}
731
732     int64 getFLOPSPerElement() const { return 2; }
733 };
734
735 struct AbsValFunctor
736 {
737     typedef AbsLayer Layer;
738
739     bool supportBackend(int backendId, int)
740     {
741 #ifdef HAVE_INF_ENGINE
742         if (backendId == DNN_BACKEND_INFERENCE_ENGINE)
743             return !INF_ENGINE_VER_MAJOR_EQ(INF_ENGINE_RELEASE_2019R1);
744 #endif
745         return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE;
746     }
747
748     void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
749     {
750         for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
751         {
752             for( int i = 0; i < len; i++ )
753             {
754                 float x = srcptr[i];
755                 dstptr[i] = abs(x);
756             }
757         }
758     }
759
760 #ifdef HAVE_OPENCL
761     bool applyOCL(InputArrayOfArrays inps, OutputArrayOfArrays outs, OutputArrayOfArrays internals)
762     {
763         std::vector<UMat> inputs;
764         std::vector<UMat> outputs;
765
766         inps.getUMatVector(inputs);
767         outs.getUMatVector(outputs);
768         String buildopt = oclGetTMacro(inputs[0]);
769
770         for (size_t i = 0; i < inputs.size(); i++)
771         {
772             UMat& src = inputs[i];
773             UMat& dst = outputs[i];
774
775             ocl::Kernel kernel("AbsValForward", ocl::dnn::activations_oclsrc, buildopt);
776             kernel.set(0, (int)src.total());
777             kernel.set(1, ocl::KernelArg::PtrReadOnly(src));
778             kernel.set(2, ocl::KernelArg::PtrWriteOnly(dst));
779
780             size_t gSize = src.total();
781             CV_Assert(kernel.run(1, &gSize, NULL, false));
782         }
783
784         return true;
785     }
786 #endif
787
788 #ifdef HAVE_HALIDE
789     void attachHalide(const Halide::Expr& input, Halide::Func& top)
790     {
791         Halide::Var x("x"), y("y"), c("c"), n("n");
792         top(x, y, c, n) = abs(input);
793     }
794 #endif  // HAVE_HALIDE
795
796 #ifdef HAVE_INF_ENGINE
797     InferenceEngine::Builder::Layer initInfEngineBuilderAPI()
798     {
799         return InferenceEngine::Builder::ReLULayer("").setNegativeSlope(-0.999999f);
800     }
801 #endif  // HAVE_INF_ENGINE
802
803 #ifdef HAVE_VULKAN
804     std::shared_ptr<vkcom::OpBase> initVkCom()
805     {
806         // TODO: add vkcom implementation
807         return std::shared_ptr<vkcom::OpBase>();
808     }
809 #endif  // HAVE_VULKAN
810
811     bool tryFuse(Ptr<dnn::Layer>&) { return false; }
812
813     void getScaleShift(Mat&, Mat&) const {}
814
815     int64 getFLOPSPerElement() const { return 1; }
816 };
817
818 struct BNLLFunctor
819 {
820     typedef BNLLLayer Layer;
821
822     bool supportBackend(int backendId, int)
823     {
824         return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE;
825     }
826
827     void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
828     {
829         for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
830         {
831             for( int i = 0; i < len; i++ )
832             {
833                 float x = srcptr[i];
834                 // https://github.com/BVLC/caffe/blame/1.0/src/caffe/layers/bnll_layer.cpp#L17
835                 dstptr[i] = x > 0 ? x + log(1. + exp(-x)) : log(1. + exp(x));
836             }
837         }
838     }
839
840 #ifdef HAVE_OPENCL
841     bool applyOCL(InputArrayOfArrays inps, OutputArrayOfArrays outs, OutputArrayOfArrays internals)
842     {
843         std::vector<UMat> inputs;
844         std::vector<UMat> outputs;
845
846         inps.getUMatVector(inputs);
847         outs.getUMatVector(outputs);
848         String buildopt = oclGetTMacro(inputs[0]);
849
850         for (size_t i = 0; i < inputs.size(); i++)
851         {
852             UMat& src = inputs[i];
853             UMat& dst = outputs[i];
854
855             ocl::Kernel kernel("BNLLForward", ocl::dnn::activations_oclsrc, buildopt);
856             kernel.set(0, (int)src.total());
857             kernel.set(1, ocl::KernelArg::PtrReadOnly(src));
858             kernel.set(2, ocl::KernelArg::PtrWriteOnly(dst));
859
860             size_t gSize = src.total();
861             CV_Assert(kernel.run(1, &gSize, NULL, false));
862         }
863
864         return true;
865     }
866 #endif
867
868 #ifdef HAVE_HALIDE
869     void attachHalide(const Halide::Expr& input, Halide::Func& top)
870     {
871         Halide::Var x("x"), y("y"), c("c"), n("n");
872         // https://github.com/BVLC/caffe/blame/1.0/src/caffe/layers/bnll_layer.cpp#L17
873         top(x, y, c, n) = max(input, 0) + log(1.0f + exp(-abs(input)));
874     }
875 #endif  // HAVE_HALIDE
876
877 #ifdef HAVE_INF_ENGINE
878     InferenceEngine::Builder::Layer initInfEngineBuilderAPI()
879     {
880         CV_Error(Error::StsNotImplemented, "");
881     }
882 #endif  // HAVE_INF_ENGINE
883
884 #ifdef HAVE_VULKAN
885     std::shared_ptr<vkcom::OpBase> initVkCom()
886     {
887         // TODO: add vkcom implementation
888         return std::shared_ptr<vkcom::OpBase>();
889     }
890 #endif  // HAVE_VULKAN
891
892     bool tryFuse(Ptr<dnn::Layer>&) { return false; }
893
894     void getScaleShift(Mat&, Mat&) const {}
895
896     int64 getFLOPSPerElement() const { return 5; }
897 };
898
899 struct PowerFunctor
900 {
901     typedef PowerLayer Layer;
902
903     float power;
904     float scale;
905     float shift;
906
907     explicit PowerFunctor(float power_ = 1.f, float scale_ = 1.f, float shift_ = 0.f)
908         : power(power_), scale(scale_), shift(shift_) {}
909
910     bool supportBackend(int backendId, int targetId)
911     {
912         if (backendId == DNN_BACKEND_INFERENCE_ENGINE)
913             return (targetId != DNN_TARGET_OPENCL && targetId != DNN_TARGET_OPENCL_FP16) || power == 1.0 || power == 0.5;
914         else
915             return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE;
916     }
917
918     void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
919     {
920         float a = scale, b = shift, p = power;
921         if( p == 1.f )
922         {
923             for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
924             {
925                 for( int i = 0; i < len; i++ )
926                 {
927                     float x = srcptr[i];
928                     dstptr[i] = a*x + b;
929                 }
930             }
931         }
932         else
933         {
934             for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
935             {
936                 for( int i = 0; i < len; i++ )
937                 {
938                     float x = srcptr[i];
939                     dstptr[i] = pow(a*x + b, p);
940                 }
941             }
942         }
943     }
944
945 #ifdef HAVE_OPENCL
946     bool applyOCL(InputArrayOfArrays inps, OutputArrayOfArrays outs, OutputArrayOfArrays internals)
947     {
948         std::vector<UMat> inputs;
949         std::vector<UMat> outputs;
950
951         inps.getUMatVector(inputs);
952         outs.getUMatVector(outputs);
953         String buildopt = oclGetTMacro(inputs[0]);
954
955         for (size_t i = 0; i < inputs.size(); i++)
956         {
957             UMat& src = inputs[i];
958             UMat& dst = outputs[i];
959
960             ocl::Kernel kernel("PowForward", ocl::dnn::activations_oclsrc, buildopt);
961             kernel.set(0, (int)src.total());
962             kernel.set(1, ocl::KernelArg::PtrReadOnly(src));
963             kernel.set(2, ocl::KernelArg::PtrWriteOnly(dst));
964             kernel.set(3, (float)power);
965             kernel.set(4, (float)scale);
966             kernel.set(5, (float)shift);
967
968             size_t gSize = src.total();
969             CV_Assert(kernel.run(1, &gSize, NULL, false));
970         }
971
972         return true;
973     }
974 #endif
975
976 #ifdef HAVE_HALIDE
977     void attachHalide(const Halide::Expr& input, Halide::Func& top)
978     {
979         Halide::Var x("x"), y("y"), c("c"), n("n");
980         Halide::Expr topExpr = (scale == 1.0f ? input : input * scale);
981         if (shift)
982         {
983             topExpr += shift;
984         }
985         if (power != 1.0f)
986         {
987             topExpr = pow(topExpr, power);
988         }
989         top(x, y, c, n) = topExpr;
990     }
991 #endif  // HAVE_HALIDE
992
993 #ifdef HAVE_INF_ENGINE
994     InferenceEngine::Builder::Layer initInfEngineBuilderAPI()
995     {
996         return InferenceEngine::Builder::PowerLayer("").setPower(power)
997                                                        .setScale(scale)
998                                                        .setShift(shift);
999     }
1000 #endif  // HAVE_INF_ENGINE
1001
1002 #ifdef HAVE_VULKAN
1003     std::shared_ptr<vkcom::OpBase> initVkCom()
1004     {
1005         // TODO: add vkcom implementation
1006         return std::shared_ptr<vkcom::OpBase>();
1007     }
1008 #endif  // HAVE_VULKAN
1009
1010     bool tryFuse(Ptr<dnn::Layer>& top)
1011     {
1012         if (power != 1.0f && shift != 0.0f)
1013             return false;
1014
1015         Mat w, b;
1016         top->getScaleShift(w, b);
1017         if ((w.empty() && b.empty()) || w.total() > 1 || b.total() > 1)
1018             return false;
1019
1020         float nextScale = w.empty() ? 1.0f : w.at<float>(0);
1021         float nextShift = b.empty() ? 0.0f : b.at<float>(0);
1022         scale = std::pow(scale, power) * nextScale;
1023         shift = nextScale * shift + nextShift;
1024         return true;
1025     }
1026
1027     void getScaleShift(Mat& _scale, Mat& _shift) const
1028     {
1029         if (power == 1.0f)
1030         {
1031             _scale = Mat(1, 1, CV_32F, Scalar(scale));
1032             _shift = Mat(1, 1, CV_32F, Scalar(shift));
1033         }
1034     }
1035
1036     int64 getFLOPSPerElement() const { return power == 1 ? 2 : 10; }
1037 };
1038
1039
1040 struct ChannelsPReLUFunctor
1041 {
1042     typedef ChannelsPReLULayer Layer;
1043     Mat scale;
1044 #ifdef HAVE_OPENCL
1045     UMat scale_umat;
1046 #endif
1047
1048     explicit ChannelsPReLUFunctor(const Mat& scale_=Mat()) : scale(scale_)
1049     {
1050     }
1051
1052     bool supportBackend(int backendId, int)
1053     {
1054         return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE ||
1055                backendId == DNN_BACKEND_INFERENCE_ENGINE;
1056     }
1057
1058     void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
1059     {
1060         CV_Assert(scale.isContinuous() && scale.type() == CV_32F);
1061
1062         const float* scaleptr = scale.ptr<float>();
1063         CV_Assert( 0 <= cn0 && cn0 < cn1 && cn1 <= (int)scale.total() );
1064
1065         for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
1066         {
1067             float s = scaleptr[cn];
1068             int i = 0;
1069         #if CV_SIMD128
1070             v_float32x4 s4 = v_setall_f32(s), z = v_setzero_f32();
1071             for( ; i <= len - 16; i += 16 )
1072             {
1073                 v_float32x4 x0 = v_load(srcptr + i);
1074                 v_float32x4 x1 = v_load(srcptr + i + 4);
1075                 v_float32x4 x2 = v_load(srcptr + i + 8);
1076                 v_float32x4 x3 = v_load(srcptr + i + 12);
1077                 x0 = v_select(x0 >= z, x0, x0*s4);
1078                 x1 = v_select(x1 >= z, x1, x1*s4);
1079                 x2 = v_select(x2 >= z, x2, x2*s4);
1080                 x3 = v_select(x3 >= z, x3, x3*s4);
1081                 v_store(dstptr + i, x0);
1082                 v_store(dstptr + i + 4, x1);
1083                 v_store(dstptr + i + 8, x2);
1084                 v_store(dstptr + i + 12, x3);
1085             }
1086         #endif
1087             for( ; i < len; i++ )
1088             {
1089                 float x = srcptr[i];
1090                 dstptr[i] = x >= 0.f ? x : s*x;
1091             }
1092         }
1093     }
1094
1095 #ifdef HAVE_OPENCL
1096     bool applyOCL(InputArrayOfArrays inps, OutputArrayOfArrays outs, OutputArrayOfArrays internals)
1097     {
1098         if (scale_umat.empty())
1099             scale.copyTo(scale_umat);
1100
1101         std::vector<UMat> inputs;
1102         std::vector<UMat> outputs;
1103
1104         inps.getUMatVector(inputs);
1105         outs.getUMatVector(outputs);
1106         String buildopt = oclGetTMacro(inputs[0]);
1107
1108         for (size_t i = 0; i < inputs.size(); i++)
1109         {
1110             UMat& src = inputs[i];
1111             UMat& dst = outputs[i];
1112
1113             ocl::Kernel kernel("PReLUForward", ocl::dnn::activations_oclsrc, buildopt);
1114             kernel.set(0, (int)src.total());
1115             kernel.set(1, (int)src.size[1]);
1116             kernel.set(2, (int)total(shape(src), 2));
1117             kernel.set(3, ocl::KernelArg::PtrReadOnly(src));
1118             kernel.set(4, ocl::KernelArg::PtrWriteOnly(dst));
1119             kernel.set(5, ocl::KernelArg::PtrReadOnly(scale_umat));
1120
1121             size_t gSize = src.total();
1122             CV_Assert(kernel.run(1, &gSize, NULL, false));
1123         }
1124
1125         return true;
1126     }
1127 #endif
1128
1129 #ifdef HAVE_HALIDE
1130     void attachHalide(const Halide::Expr& input, Halide::Func& top)
1131     {
1132         Halide::Var x("x"), y("y"), c("c"), n("n");
1133         auto weights = wrapToHalideBuffer(scale, {(int)scale.total()});
1134         top(x, y, c, n) = select(input >= 0.0f, input, weights(c) * input);
1135     }
1136 #endif  // HAVE_HALIDE
1137
1138 #ifdef HAVE_INF_ENGINE
1139     InferenceEngine::Builder::Layer initInfEngineBuilderAPI()
1140     {
1141         InferenceEngine::Builder::Layer l = InferenceEngine::Builder::PReLULayer("");
1142         const size_t numChannels = scale.total();
1143         addConstantData("weights", wrapToInfEngineBlob(scale, {numChannels}, InferenceEngine::Layout::C), l);
1144         return l;
1145     }
1146 #endif  // HAVE_INF_ENGINE
1147
1148 #ifdef HAVE_VULKAN
1149     std::shared_ptr<vkcom::OpBase> initVkCom()
1150     {
1151         // TODO: add vkcom implementation
1152         return std::shared_ptr<vkcom::OpBase>();
1153     }
1154 #endif  // HAVE_VULKAN
1155
1156     bool tryFuse(Ptr<dnn::Layer>&) { return false; }
1157
1158     void getScaleShift(Mat&, Mat&) const {}
1159
1160     int64 getFLOPSPerElement() const { return 1; }
1161 };
1162
1163 #define ACTIVATION_CREATOR_FOR(_Layer, _Functor, ...) \
1164 Ptr<_Layer> _Layer::create() { \
1165     return return Ptr<_Layer>( new ElementWiseLayer<_Functor>(_Functor()) ); }
1166
1167
1168 Ptr<ReLULayer> ReLULayer::create(const LayerParams& params)
1169 {
1170     float negativeSlope = params.get<float>("negative_slope", 0.f);
1171     Ptr<ReLULayer> l(new ElementWiseLayer<ReLUFunctor>(ReLUFunctor(negativeSlope)));
1172     l->setParamsFrom(params);
1173     l->negativeSlope = negativeSlope;
1174
1175     return l;
1176 }
1177
1178 Ptr<ReLU6Layer> ReLU6Layer::create(const LayerParams& params)
1179 {
1180     float minValue = params.get<float>("min_value", 0.0f);
1181     float maxValue = params.get<float>("max_value", 6.0f);
1182     Ptr<ReLU6Layer> l(new ElementWiseLayer<ReLU6Functor>(ReLU6Functor(minValue, maxValue)));
1183     l->setParamsFrom(params);
1184     l->minValue = minValue;
1185     l->maxValue = maxValue;
1186
1187     return l;
1188 }
1189
1190 Ptr<TanHLayer> TanHLayer::create(const LayerParams& params)
1191 {
1192     Ptr<TanHLayer> l(new ElementWiseLayer<TanHFunctor>());
1193     l->setParamsFrom(params);
1194
1195     return l;
1196 }
1197
1198 Ptr<SigmoidLayer> SigmoidLayer::create(const LayerParams& params)
1199 {
1200     Ptr<SigmoidLayer> l(new ElementWiseLayer<SigmoidFunctor>());
1201     l->setParamsFrom(params);
1202
1203     return l;
1204 }
1205
1206 Ptr<ELULayer> ELULayer::create(const LayerParams& params)
1207 {
1208     Ptr<ELULayer> l(new ElementWiseLayer<ELUFunctor>(ELUFunctor()));
1209     l->setParamsFrom(params);
1210
1211     return l;
1212 }
1213
1214 Ptr<AbsLayer> AbsLayer::create(const LayerParams& params)
1215 {
1216     Ptr<AbsLayer> l(new ElementWiseLayer<AbsValFunctor>());
1217     l->setParamsFrom(params);
1218
1219     return l;
1220 }
1221
1222 Ptr<BNLLLayer> BNLLLayer::create(const LayerParams& params)
1223 {
1224     Ptr<BNLLLayer> l(new ElementWiseLayer<BNLLFunctor>());
1225     l->setParamsFrom(params);
1226
1227     return l;
1228 }
1229
1230 Ptr<PowerLayer> PowerLayer::create(const LayerParams& params)
1231 {
1232     float power = params.get<float>("power", 1.0f);
1233     float scale = params.get<float>("scale", 1.0f);
1234     float shift = params.get<float>("shift", 0.0f);
1235     Ptr<PowerLayer> l(new ElementWiseLayer<PowerFunctor>(PowerFunctor(power, scale, shift)));
1236     l->setParamsFrom(params);
1237     l->power = power;
1238     l->scale = scale;
1239     l->shift = shift;
1240
1241     return l;
1242 }
1243
1244 Ptr<Layer> ChannelsPReLULayer::create(const LayerParams& params)
1245 {
1246     CV_Assert(params.blobs.size() == 1);
1247     if (params.blobs[0].total() == 1)
1248     {
1249         LayerParams reluParams = params;
1250         reluParams.set("negative_slope", params.blobs[0].at<float>(0));
1251         return ReLULayer::create(reluParams);
1252     }
1253     Ptr<ChannelsPReLULayer> l(new ElementWiseLayer<ChannelsPReLUFunctor>(ChannelsPReLUFunctor(params.blobs[0])));
1254     l->setParamsFrom(params);
1255
1256     return l;
1257 }
1258
1259 }
1260 }