Merge pull request #14827 from YashasSamaga:cuda4dnn-csl-low
[platform/upstream/opencv.git] / modules / dnn / src / layers / slice_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 "../op_cuda.hpp"
45 #include "../op_inf_engine.hpp"
46 #include "layers_common.hpp"
47 #include <opencv2/dnn/shape_utils.hpp>
48
49 #ifdef HAVE_OPENCL
50 #include "opencl_kernels_dnn.hpp"
51 #endif
52
53 #ifdef HAVE_CUDA
54 #include "../cuda4dnn/primitives/slice.hpp"
55 using namespace cv::dnn::cuda4dnn;
56 #endif
57
58 namespace cv
59 {
60 namespace dnn
61 {
62
63 class SliceLayerImpl : public SliceLayer
64 {
65 public:
66     SliceLayerImpl(const LayerParams& params)
67     {
68         setParamsFrom(params);
69         axis = params.get<int>("axis", 1);
70         num_split = params.get<int>("num_split", 0);
71         if (params.has("slice_point"))
72         {
73             CV_Assert(!params.has("begin") && !params.has("size") && !params.has("end"));
74             const DictValue &indicesValue = params.get("slice_point");
75             sliceRanges.resize(indicesValue.size() + 1,
76                                std::vector<Range>(axis + 1, Range::all()));
77             int prevSlice = 0;
78             for (int i = 0; i < indicesValue.size(); ++i)
79             {
80                 sliceRanges[i][axis].start = prevSlice;
81                 sliceRanges[i][axis].end = indicesValue.get<int>(i);
82                 prevSlice = sliceRanges[i][axis].end;
83             }
84             sliceRanges.back()[axis].start = prevSlice;
85         }
86         else if (params.has("begin"))
87         {
88             CV_Assert(params.has("size") ^ params.has("end"));
89             const DictValue &begins = params.get("begin");
90             const DictValue &sizesOrEnds = params.has("size") ? params.get("size") : params.get("end");
91             CV_Assert(begins.size() == sizesOrEnds.size());
92
93             sliceRanges.resize(1);
94             sliceRanges[0].resize(begins.size(), Range::all());
95             for (int i = 0; i < begins.size(); ++i)
96             {
97                 int start = begins.get<int>(i);
98                 int sizeOrEnd = sizesOrEnds.get<int>(i);  // It may be negative to reverse indexation.
99                 CV_Assert(start >= 0);
100
101                 sliceRanges[0][i].start = start;
102                 if (params.has("size"))
103                 {
104                     int size = sizeOrEnd;
105                     CV_Assert(size == -1 || size > 0);  // -1 value means range [start, axis_size).
106                     sliceRanges[0][i].end = size > 0 ? (start + size) : -1;  // We'll finalize a negative value later.
107                 }
108                 else
109                 {
110                     int end = sizeOrEnd;
111                     CV_Assert(end < 0 || end > start);  // End index is excluded.
112                     sliceRanges[0][i].end = end;  // We'll finalize a negative value later.
113                 }
114             }
115         }
116     }
117
118     virtual bool supportBackend(int backendId) CV_OVERRIDE
119     {
120         return backendId == DNN_BACKEND_OPENCV ||
121                backendId == DNN_BACKEND_CUDA ||
122                (backendId == DNN_BACKEND_INFERENCE_ENGINE &&
123 #ifdef HAVE_INF_ENGINE
124                 INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2019R1) &&
125 #endif
126                 sliceRanges.size() == 1 && sliceRanges[0].size() == 4);
127     }
128
129     bool getMemoryShapes(const std::vector<MatShape> &inputs,
130                             const int requiredOutputs,
131                             std::vector<MatShape> &outputs,
132                             std::vector<MatShape> &internals) const CV_OVERRIDE
133     {
134         CV_Assert(inputs.size() == 1);
135         MatShape inpShape = inputs[0];
136
137         if (!sliceRanges.empty())
138         {
139             outputs.resize(sliceRanges.size(), inpShape);
140             for (int i = 0; i < outputs.size(); ++i)
141             {
142                 CV_Assert(sliceRanges[i].size() <= inpShape.size());
143                 for (int j = 0; j < sliceRanges[i].size(); ++j)
144                 {
145                     outputs[i][j] = clamp(sliceRanges[i][j], inpShape[j]).size();
146                 }
147             }
148         }
149         else  // Divide input blob on equal parts by axis.
150         {
151             CV_Assert(0 <= axis && axis < inpShape.size());
152             int splits = num_split ? num_split : requiredOutputs;
153             CV_Assert(splits > 0 && inpShape[axis] % splits == 0);
154             inpShape[axis] /= splits;
155             outputs.resize(splits, inpShape);
156         }
157         return false;
158     }
159
160     void finalize(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr) CV_OVERRIDE
161     {
162         std::vector<Mat> inputs, outputs;
163         inputs_arr.getMatVector(inputs);
164         outputs_arr.getMatVector(outputs);
165
166         CV_Assert(inputs.size() == 1);
167         const MatSize& inpShape = inputs[0].size;
168
169         if (sliceRanges.empty())
170         {
171             // Divide input blob on equal parts by axis.
172             int outAxisSize = inpShape[axis] / outputs.size();
173             sliceRanges.resize(outputs.size(),
174                                std::vector<Range>(axis + 1, Range::all()));
175             int prevSlice = 0;
176             for (int i = 0; i < outputs.size(); ++i)
177             {
178                 sliceRanges[i][axis].start = prevSlice;
179                 sliceRanges[i][axis].end = sliceRanges[i][axis].start + outAxisSize;
180                 prevSlice = sliceRanges[i][axis].end;
181             }
182         }
183         else
184             CV_Assert(outputs.size() == sliceRanges.size());
185
186         for (int i = 0; i < outputs.size(); ++i)
187         {
188             CV_Assert(sliceRanges[i].size() <= inpShape.dims());
189             // Fill the rest of ranges.
190             for (int j = sliceRanges[i].size(); j < inpShape.dims(); ++j)
191             {
192                 sliceRanges[i].push_back(Range::all());
193             }
194             // Clamp.
195             for (int j = 0; j < sliceRanges[i].size(); ++j)
196             {
197                 sliceRanges[i][j] = clamp(sliceRanges[i][j], inpShape[j]);
198             }
199         }
200     }
201
202 #ifdef HAVE_OPENCL
203     bool forward_ocl(InputArrayOfArrays inputs_, OutputArrayOfArrays outputs_, OutputArrayOfArrays internals_)
204     {
205         std::vector<UMat> inputs;
206         std::vector<UMat> outputs;
207
208         bool use_half = (inputs_.depth() == CV_16S);
209         inputs_.getUMatVector(inputs);
210         outputs_.getUMatVector(outputs);
211
212         if (inputs[0].dims < 4 || (total(shape(outputs[0]), 0, 2) % 4 != 0) ||
213             (total(shape(outputs[0]), 2) % 4 != 0))
214             return false;
215
216         String opts;
217         if (use_half)
218             opts = "-DDtype=half -DDtype4=half4 -DDtype8=half8";
219         else
220             opts = "-DDtype=float -DDtype4=float4 -DDtype8=float8";
221         const UMat& inpMat = inputs[0];
222         for (size_t i = 0; i < outputs.size(); i++)
223         {
224             int groups = outputs[i].size[0];
225             int channels = outputs[i].size[1];
226             int rows = outputs[i].size[2];
227             int cols = outputs[i].size[3];
228
229             ocl::Kernel kernel("slice", ocl::dnn::slice_oclsrc, opts);
230             size_t local[] = { 128 };
231             size_t global[] = { (size_t)groups * channels / 4 * local[0] };
232             int idx = 0;
233             kernel.set(idx++, ocl::KernelArg::PtrReadOnly(inpMat));
234             kernel.set(idx++, (int)(inpMat.size[2] * inpMat.size[3]));
235             kernel.set(idx++, (int)(rows * cols));
236             kernel.set(idx++, (int)inpMat.size[3]);
237             kernel.set(idx++, (int)cols);
238             kernel.set(idx++, (int)sliceRanges[i][2].start);
239             kernel.set(idx++, (int)sliceRanges[i][3].start);
240             kernel.set(idx++, ocl::KernelArg::PtrWriteOnly(outputs[i]));
241             bool ret = kernel.run(1, global, local, false);
242             if (!ret)
243                 return false;
244         }
245
246         return true;
247     }
248 #endif
249
250     void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE
251     {
252         CV_TRACE_FUNCTION();
253         CV_TRACE_ARG_VALUE(name, "name", name.c_str());
254
255         CV_OCL_RUN(IS_DNN_OPENCL_TARGET(preferableTarget),
256                    forward_ocl(inputs_arr, outputs_arr, internals_arr))
257
258         std::vector<Mat> inputs, outputs;
259         inputs_arr.getMatVector(inputs);
260         outputs_arr.getMatVector(outputs);
261
262         const Mat& inpMat = inputs[0];
263         CV_Assert(outputs.size() == sliceRanges.size());
264         for (size_t i = 0; i < outputs.size(); i++)
265         {
266             inpMat(sliceRanges[i]).copyTo(outputs[i]);
267         }
268     }
269
270 #ifdef HAVE_CUDA
271     Ptr<BackendNode> initCUDA(
272         void *context_,
273         const std::vector<Ptr<BackendWrapper>>& inputs,
274         const std::vector<Ptr<BackendWrapper>>& outputs
275     ) override
276     {
277         auto context = reinterpret_cast<csl::CSLContext*>(context_);
278
279         std::vector<std::vector<std::size_t>> offsets;
280         for (const auto& ranges : sliceRanges)
281         {
282             std::vector<std::size_t> offsets_i;
283             for (const auto& range : ranges)
284                 offsets_i.push_back(range.start);
285             offsets.push_back(std::move(offsets_i));
286         }
287
288         return make_cuda_node<cuda4dnn::SliceOp>(preferableTarget, std::move(context->stream), std::move(offsets));
289     }
290 #endif
291
292 #ifdef HAVE_INF_ENGINE
293 #if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2019R1)
294     virtual Ptr<BackendNode> initInfEngine(const std::vector<Ptr<BackendWrapper> >& inputs) CV_OVERRIDE
295     {
296         CV_Assert_N(sliceRanges.size() == 1, inputs.size() <= 2);
297
298         std::vector<size_t> axes, offsets, dims;
299         int from, to, step;
300         int numDims = sliceRanges[0].size();
301         if (preferableTarget == DNN_TARGET_MYRIAD)
302         {
303             from = axis;
304             to = numDims;
305             step = 1;
306         }
307         else
308         {
309             from = numDims - 1;
310             to = axis - 1;
311             step = -1;
312         }
313         for (int i = from; i != to; i += step)
314         {
315             axes.push_back(i);
316             offsets.push_back(sliceRanges[0][i].start);
317             dims.push_back(sliceRanges[0][i].size());
318         }
319
320         InferenceEngine::Builder::Layer ieLayer(name);
321         ieLayer.setName(name);
322         ieLayer.setType("Crop");
323         ieLayer.getParameters()["axis"] = axes;
324         ieLayer.getParameters()["dim"] = dims;
325         ieLayer.getParameters()["offset"] = offsets;
326         ieLayer.setInputPorts(std::vector<InferenceEngine::Port>(2));
327         ieLayer.setOutputPorts(std::vector<InferenceEngine::Port>(1));
328
329         if (inputs.size() != 2)
330         {
331             std::vector<size_t> outShape(numDims);
332             for (int i = 0; i < numDims; ++i)
333                 outShape[i] = sliceRanges[0][i].size();
334
335             ieLayer.getInputPorts()[1].setParameter("type", "weights");
336
337             auto shapeSource = InferenceEngine::make_shared_blob<float>({
338                                    InferenceEngine::Precision::FP32, outShape,
339                                    InferenceEngine::Layout::ANY
340                                });
341             shapeSource->allocate();
342             addConstantData("weights", shapeSource, ieLayer);
343         }
344         return Ptr<BackendNode>(new InfEngineBackendNode(ieLayer));
345     }
346 #endif
347 #endif
348 };
349
350 class CropLayerImpl CV_FINAL : public SliceLayerImpl
351 {
352 public:
353     CropLayerImpl(const LayerParams& params) : SliceLayerImpl(LayerParams())
354     {
355         setParamsFrom(params);
356         axis = params.get<int>("axis", 2);
357         const DictValue *paramOffset = params.ptr("offset");
358
359         if (paramOffset)
360         {
361             for (int i = 0; i < paramOffset->size(); i++)
362                 offset.push_back(paramOffset->get<int>(i));
363         }
364     }
365
366     bool getMemoryShapes(const std::vector<MatShape> &inputs,
367                          const int requiredOutputs,
368                          std::vector<MatShape> &outputs,
369                          std::vector<MatShape> &internals) const CV_OVERRIDE
370     {
371         CV_Assert(inputs.size() == 2);
372
373         MatShape dstShape = inputs[0];
374         int start = clamp(axis, dstShape);
375         for (int i = start; i < dstShape.size(); i++)
376         {
377             dstShape[i] = inputs[1][i];
378         }
379         outputs.resize(1, dstShape);
380         return false;
381     }
382
383     void finalize(InputArrayOfArrays inputs_arr, OutputArrayOfArrays) CV_OVERRIDE
384     {
385         std::vector<Mat> inputs;
386         inputs_arr.getMatVector(inputs);
387         CV_Assert(2 == inputs.size());
388
389         const Mat &inpBlob = inputs[0];
390         const Mat &inpSzBlob = inputs[1];
391
392         int dims = inpBlob.dims;
393         int start_axis = clamp(axis, dims);
394
395         std::vector<int> offset_final(dims, 0);
396         if (offset.size() == 1)
397         {
398             for (int i = start_axis; i < dims; i++)
399                 offset_final[i] = offset[0];
400         }
401         else if (offset.size() > 1)
402         {
403             if ((int)offset.size() != dims - start_axis)
404                 CV_Error(Error::StsBadArg, "number of offset values specified must be "
405                                            "equal to the number of dimensions following axis.");
406
407             for (int i = start_axis; i < dims; i++)
408                 offset_final[i] = offset[i - start_axis];
409         }
410
411         sliceRanges.resize(1);
412         sliceRanges[0].resize(dims);
413         for (int i = 0; i < start_axis; i++)
414         {
415             sliceRanges[0][i] = Range(0, inpBlob.size[i]);
416         }
417         for (int i = start_axis; i < dims; i++)
418         {
419             if (offset_final[i] < 0 || offset_final[i] + inpSzBlob.size[i] > inpBlob.size[i])
420                 CV_Error(Error::StsBadArg, "invalid crop parameters or blob sizes");
421
422             sliceRanges[0][i] = Range(offset_final[i], offset_final[i] + inpSzBlob.size[i]);
423         }
424     }
425
426 private:
427     std::vector<int> offset;
428 };
429
430 Ptr<SliceLayer> SliceLayer::create(const LayerParams& params)
431 {
432     return Ptr<SliceLayer>(new SliceLayerImpl(params));
433 }
434
435 Ptr<Layer> CropLayer::create(const LayerParams& params)
436 {
437     return Ptr<Layer>(new CropLayerImpl(params));
438 }
439
440 }
441 }