6c0b53ffe926012e707ec28b9427d3dd71fe5580
[platform/upstream/opencv.git] / modules / dnn / src / layers / permute_layer.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_inf_engine.hpp"
46 #include "../op_vkcom.hpp"
47 #include <float.h>
48 #include <algorithm>
49
50 #ifdef HAVE_OPENCL
51 #include "opencl_kernels_dnn.hpp"
52 #endif
53
54 namespace cv
55 {
56 namespace dnn
57 {
58 class PermuteLayerImpl CV_FINAL : public PermuteLayer
59 {
60 public:
61     void checkNeedForPermutation()
62     {
63         _needsPermute = false;
64         for (size_t i = 0; i < _numAxes; ++i)
65         {
66             if (_order[i] != i)
67             {
68                 _needsPermute = true;
69                 break;
70             }
71         }
72     }
73
74     PermuteLayerImpl(const LayerParams &params)
75         : _count(0), _needsPermute(false), _numAxes(0)
76     {
77         if (!params.has("order"))
78         {
79             return;
80         }
81
82         DictValue paramOrder = params.get("order");
83         _numAxes = paramOrder.size();
84
85         for (size_t i = 0; i < _numAxes; i++)
86         {
87             int currentOrder = paramOrder.get<int>(i);
88             if (currentOrder < 0 || currentOrder > _numAxes)
89             {
90                 CV_Error(Error::StsBadArg,
91                          format("Orders of dimensions in Permute layer parameter"
92                                 "must be in [0...%zu]", _numAxes - 1));
93             }
94             if (std::find(_order.begin(), _order.end(), currentOrder) != _order.end())
95             {
96                 CV_Error(Error::StsBadArg,
97                          "Permute layer parameter contains duplicated orders.");
98             }
99             _order.push_back(currentOrder);
100         }
101
102         setParamsFrom(params);
103         checkNeedForPermutation();
104     }
105
106     virtual bool supportBackend(int backendId) CV_OVERRIDE
107     {
108         return backendId == DNN_BACKEND_OPENCV ||
109                (backendId == DNN_BACKEND_INFERENCE_ENGINE && haveInfEngine()) ||
110                (backendId == DNN_BACKEND_VKCOM && haveVulkan());
111     }
112
113     bool getMemoryShapes(const std::vector<MatShape> &inputs,
114                          const int requiredOutputs,
115                          std::vector<MatShape> &outputs,
116                          std::vector<MatShape> &internals) const CV_OVERRIDE
117     {
118         if(!_needsPermute)
119         {
120             Layer::getMemoryShapes(inputs, requiredOutputs, outputs, internals);
121             return true;
122         }
123
124         CV_Assert(inputs.size() > 0);
125         CV_Assert((int)_numAxes == inputs[0].size());
126
127         MatShape shapeBefore = inputs[0], shapeAfter;
128         for (size_t i = 0; i < _numAxes; i++)
129         {
130             shapeAfter.push_back(shapeBefore[_order[i]]);
131         }
132
133         outputs.clear();
134
135         for (size_t i = 0; i < inputs.size(); i++)
136         {
137             CV_Assert(total(inputs[i]) == total(shapeAfter));
138             outputs.push_back(shapeAfter);
139         }
140
141         return false;
142     }
143
144     void computeStrides(const MatShape &shapeBefore, const MatShape &shapeAfter)
145     {
146         _oldStride.resize(_numAxes);
147         _newStride.resize(_numAxes);
148
149         _oldStride[_numAxes - 1] = 1;
150         _newStride[_numAxes - 1] = 1;
151
152         for(int i = _numAxes - 2; i >= 0; i--)
153         {
154             _oldStride[i] = _oldStride[i + 1] * shapeBefore[i + 1];
155             _newStride[i] = _newStride[i + 1] * shapeAfter[i + 1];
156         }
157
158         _count = _oldStride[0] * shapeBefore[0];
159     }
160
161     void finalize(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr) CV_OVERRIDE
162     {
163         if(!_needsPermute)
164         {
165             return;
166         }
167         std::vector<Mat> inputs, outputs;
168         inputs_arr.getMatVector(inputs);
169         outputs_arr.getMatVector(outputs);
170
171         CV_Assert(inputs.size() > 0);
172         const Mat& inp0 = inputs[0];
173         CV_Assert((int)_numAxes == inp0.dims);
174
175         computeStrides(shape(inputs[0]), shape(outputs[0]));
176
177 #ifdef HAVE_OPENCL
178         if (uorder.empty())
179         {
180             std::vector<int> orderVec(_order.begin(), _order.end());;
181             Mat morder(1, orderVec.size(), CV_32SC1, &orderVec[0]);
182
183             std::vector<int> oldStrideVec(_oldStride.begin(), _oldStride.end());
184             Mat mold_stride(1, _oldStride.size(), CV_32SC1, &oldStrideVec[0]);
185
186             std::vector<int> newStrideVec(_newStride.begin(), _newStride.end());
187             Mat mnew_stride(1, newStrideVec.size(), CV_32SC1, &newStrideVec[0]);
188
189             morder.copyTo(uorder);
190             mold_stride.copyTo(uold_stride);
191             mnew_stride.copyTo(unew_stride);
192         }
193 #endif
194     }
195
196     class PermuteInvoker : public ParallelLoopBody
197     {
198     public:
199         const Mat* inp;
200         Mat* out;
201         const std::vector<size_t>* order;
202         int nstripes;
203
204         static void run(const Mat& inp, Mat& out, const std::vector<size_t>& order, int nstripes)
205         {
206             PermuteInvoker p;
207             p.inp = &inp;
208             p.out = &out;
209             p.order = &order;
210             p.nstripes = nstripes;
211
212             CV_Assert( out.size[0] == inp.size[order[0]] &&
213                       out.size[1] == inp.size[order[1]] &&
214                       out.size[2] == inp.size[order[2]] &&
215                       out.size[3] == inp.size[order[3]]);
216
217             parallel_for_(Range(0, nstripes), p, nstripes);
218         }
219
220         PermuteInvoker() : inp(0), out(0), order(0), nstripes(0) {}
221
222         void operator()(const Range& r) const CV_OVERRIDE
223         {
224             int n0 = out->size[0], n1 = out->size[1], n2 = out->size[2], n3 = out->size[3];
225
226             size_t orows = (size_t)n0*n1*n2;
227             size_t stripeSize = (orows + nstripes - 1)/nstripes;
228             size_t stripeStart = r.start*stripeSize;
229             size_t stripeEnd = std::min(r.end*stripeSize, orows);
230
231             const size_t esz = sizeof(float);
232             size_t ostep0 = out->step[0]/esz, ostep1 = out->step[1]/esz, ostep2 = out->step[2]/esz;
233             const size_t* ord = &order->at(0);
234             size_t istep0 = inp->step[ord[0]]/esz, istep1 = inp->step[ord[1]]/esz,
235             istep2 = inp->step[ord[2]]/esz, istep3 = inp->step[ord[3]]/esz;
236
237             size_t val = stripeStart;
238             int i2 = (int)(val % n2);
239             val /= n2;
240             int i1 = (int)(val % n1);
241             int i0 = (int)(val / n1);
242
243             const float* inptr_orig = inp->ptr<float>();
244             float* outptr_orig = out->ptr<float>();
245
246             for( size_t ofs = stripeStart; ofs < stripeEnd; ofs++ )
247             {
248                 const float* inptr = inptr_orig + i0*istep0 + i1*istep1 + i2*istep2;
249                 float* outptr = outptr_orig + i0*ostep0 + i1*ostep1 + i2*ostep2;
250
251                 for( int i3 = 0; i3 < n3; i3++ )
252                     outptr[i3] = inptr[i3*istep3];
253
254                 if( ++i2 >= n2 )
255                 {
256                     i2 = 0;
257                     if( ++i1 >= n1 )
258                     {
259                         i1 = 0;
260                         if( ++i0 >= n0 )
261                             break;
262                     }
263                 }
264             }
265         }
266     };
267
268 #ifdef HAVE_OPENCL
269     bool forward_ocl(InputArrayOfArrays inps, OutputArrayOfArrays outs, OutputArrayOfArrays internals)
270     {
271         std::vector<UMat> inputs;
272         std::vector<UMat> outputs;
273
274         inps.getUMatVector(inputs);
275         outs.getUMatVector(outputs);
276
277         if (!_needsPermute)
278             return false;
279
280         bool use_half = (inps.depth() == CV_16S);
281         String opts = format("-DDtype=%s", use_half ? "half" : "float");
282         for (size_t i = 0; i < inputs.size(); i++)
283         {
284             ocl::Kernel kernel("permute", ocl::dnn::permute_oclsrc, opts);
285
286             kernel.set(0, (int)_count);
287             kernel.set(1, ocl::KernelArg::PtrReadOnly(inputs[i]));
288             kernel.set(2, ocl::KernelArg::PtrReadOnly(uorder));
289             kernel.set(3, ocl::KernelArg::PtrReadOnly(uold_stride));
290             kernel.set(4, ocl::KernelArg::PtrReadOnly(unew_stride));
291             kernel.set(5, (int)_numAxes);
292             kernel.set(6, ocl::KernelArg::PtrWriteOnly(outputs[i]));
293
294             if (!kernel.run(1, &_count, NULL, false))
295                 return false;
296         }
297
298         return true;
299     }
300 #endif
301
302     void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE
303     {
304         CV_TRACE_FUNCTION();
305         CV_TRACE_ARG_VALUE(name, "name", name.c_str());
306
307         CV_OCL_RUN(IS_DNN_OPENCL_TARGET(preferableTarget),
308                    forward_ocl(inputs_arr, outputs_arr, internals_arr))
309
310         if (inputs_arr.depth() == CV_16S)
311         {
312             forward_fallback(inputs_arr, outputs_arr, internals_arr);
313             return;
314         }
315
316         std::vector<Mat> inputs, outputs;
317         inputs_arr.getMatVector(inputs);
318         outputs_arr.getMatVector(outputs);
319
320         size_t k, ninputs = inputs.size();
321         if(!_needsPermute)
322         {
323             for (k = 0; k < ninputs; k++)
324             {
325                 CV_Assert(outputs[k].total() == inputs[k].total());
326                 if (outputs[k].data != inputs[k].data)
327                     inputs[k].copyTo(outputs[k]);
328             }
329         }
330         else
331         {
332             size_t i, j, count = _count, numAxes = _numAxes;
333             const size_t* newStride = &_newStride[0];
334             const size_t* oldStride = &_oldStride[0];
335             const size_t* order = &_order[0];
336
337             for (k = 0; k < ninputs; k++)
338             {
339                 const Mat& inp = inputs[k];
340                 Mat& out = outputs[k];
341
342                 CV_Assert(inp.dims == numAxes && inp.size == inputs[0].size);
343                 CV_Assert(out.dims == numAxes && out.size == outputs[0].size);
344
345                 CV_Assert(inp.isContinuous() && out.isContinuous());
346                 CV_Assert(inp.type() == CV_32F && out.type() == CV_32F);
347
348                 if( numAxes == 4 )
349                 {
350                     int nstripes = getNumThreads();
351                     PermuteInvoker::run(inp, out, _order, nstripes);
352                 }
353                 else
354                 {
355                     const float *srcData = inp.ptr<float>();
356                     float *dstData = out.ptr<float>();
357
358                     for (i = 0; i < count; ++i)
359                     {
360                         size_t oldPosition = 0;
361                         size_t newPosition = i;
362
363                         for (j = 0; j < numAxes; ++j)
364                         {
365                             oldPosition += (newPosition / newStride[j]) * oldStride[order[j]];
366                             newPosition %= newStride[j];
367                         }
368                         dstData[i] = srcData[oldPosition];
369                     }
370                 }
371             }
372         }
373     }
374
375     virtual Ptr<BackendNode> initVkCom(const std::vector<Ptr<BackendWrapper> > &input) CV_OVERRIDE
376     {
377 #ifdef HAVE_VULKAN
378         CV_Assert(!_order.empty());
379         std::shared_ptr<vkcom::OpBase> op(new vkcom::OpPermute(_order));
380         return Ptr<BackendNode>(new VkComBackendNode(input, op));
381 #endif // HAVE_VULKAN
382         return Ptr<BackendNode>();
383     }
384
385 #ifdef HAVE_INF_ENGINE
386     virtual Ptr<BackendNode> initInfEngine(const std::vector<Ptr<BackendWrapper> >&) CV_OVERRIDE
387     {
388         InferenceEngine::Builder::PermuteLayer ieLayer(name);
389         ieLayer.setOrder(_order);
390         return Ptr<BackendNode>(new InfEngineBackendNode(ieLayer));
391     }
392 #endif  // HAVE_INF_ENGINE
393
394     size_t _count;
395     std::vector<size_t> _order;
396
397     std::vector<int> _oldDimensionSize;
398     std::vector<int> _newDimensionSize;
399
400     std::vector<size_t> _oldStride;
401     std::vector<size_t> _newStride;
402     bool _needsPermute;
403
404 #ifdef HAVE_OPENCL
405     UMat uorder, uold_stride, unew_stride;
406 #endif
407
408     size_t _numAxes;
409 };
410
411 Ptr<PermuteLayer> PermuteLayer::create(const LayerParams &params)
412 {
413     return Ptr<PermuteLayer>(new PermuteLayerImpl(params));
414 }
415
416 }
417 }