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