cffb84d692c4d6fa76fda5ae6ad1666efd577547
[platform/upstream/opencv.git] / modules / dnn / src / layers / padding_layer.cpp
1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html.
4
5 // Copyright (C) 2017, Intel Corporation, all rights reserved.
6 // Third party copyrights are property of their respective owners.
7
8 /*
9 Implementation of padding layer, which adds paddings to input blob.
10 */
11
12 #include "../precomp.hpp"
13 #include "layers_common.hpp"
14 #include "../op_halide.hpp"
15 #include "../op_inf_engine.hpp"
16 #include <vector>
17
18 namespace cv
19 {
20 namespace dnn
21 {
22
23 class PaddingLayerImpl CV_FINAL : public PaddingLayer
24 {
25 public:
26     PaddingLayerImpl(const LayerParams &params)
27     {
28         setParamsFrom(params);
29         paddingValue = params.get<float>("value", 0);
30         inputDims = params.get<int>("input_dims", -1);
31         paddingType = params.get<String>("type", "constant");
32
33         CV_Assert(params.has("paddings"));
34         const DictValue& paddingsParam = params.get("paddings");
35         CV_Assert((paddingsParam.size() & 1) == 0);
36
37         paddings.resize(paddingsParam.size() / 2);
38         for (int i = 0; i < paddings.size(); ++i)
39         {
40             paddings[i].first = paddingsParam.get<int>(i * 2);  // Pad before.
41             paddings[i].second = paddingsParam.get<int>(i * 2 + 1);  // Pad after.
42             CV_Assert_N(paddings[i].first >= 0, paddings[i].second >= 0);
43         }
44     }
45
46     bool getMemoryShapes(const std::vector<MatShape> &inputs,
47                          const int requiredOutputs,
48                          std::vector<MatShape> &outputs,
49                          std::vector<MatShape> &internals) const CV_OVERRIDE
50     {
51         CV_Assert(inputs.size() == 1);
52         const MatShape& inpShape = inputs[0];
53         CV_Assert(inpShape.size() >= paddings.size());
54         CV_Assert(inputDims == -1 || inpShape.size() == inputDims || inpShape.size() > paddings.size());
55
56         outputs.resize(1, inpShape);
57         int offset = (inputDims == -1 ? 0 : (inpShape.size() > inputDims ? 1 : 0));
58         for (int i = 0; i < paddings.size(); ++i)
59         {
60             outputs[0][offset + i] = inpShape[offset + i] + paddings[i].first + paddings[i].second;
61         }
62         return false;
63     }
64
65     void finalize(InputArrayOfArrays inputs_arr, OutputArrayOfArrays) CV_OVERRIDE
66     {
67         std::vector<Mat> inputs;
68         inputs_arr.getMatVector(inputs);
69
70         // Compute dstRanges.
71         const MatSize& inpShape = inputs[0].size;
72
73         if (inputDims != -1 && inputs[0].dims != inputDims)
74         {
75             paddings.insert(paddings.begin(), std::make_pair(0, 0));
76         }
77
78         dstRanges.resize(paddings.size());
79         for (int i = 0; i < paddings.size(); ++i)
80         {
81             dstRanges[i].start = paddings[i].first;
82             dstRanges[i].end = paddings[i].first + inpShape[i];
83         }
84
85         // Add the rest of dimensions.
86         for (int i = dstRanges.size(); i < inputs[0].dims; ++i)
87         {
88             dstRanges.push_back(Range::all());
89             paddings.push_back(std::make_pair(0, 0));
90         }
91         inputDims = -1;  // Next time paddings are filled for all the dimensions.
92     }
93
94     virtual bool supportBackend(int backendId) CV_OVERRIDE
95     {
96 #ifdef HAVE_INF_ENGINE
97         if (backendId == DNN_BACKEND_INFERENCE_ENGINE)
98             return INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2019R1) &&
99                    (preferableTarget != DNN_TARGET_MYRIAD ||
100                     (dstRanges.size() == 4 && paddings[0].first == 0 && paddings[0].second == 0));
101 #endif
102         return backendId == DNN_BACKEND_OPENCV ||
103                (backendId == DNN_BACKEND_HALIDE && haveHalide() && dstRanges.size() == 4);
104     }
105
106     void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE
107     {
108         CV_TRACE_FUNCTION();
109         CV_TRACE_ARG_VALUE(name, "name", name.c_str());
110
111         std::vector<Mat> inputs, outputs;
112         inputs_arr.getMatVector(inputs);
113         outputs_arr.getMatVector(outputs);
114
115         if (paddingType == "constant")
116         {
117             if (inputs_arr.depth() == CV_16S)
118             {
119                 std::vector<float> paddingValue_fp32(1, paddingValue);
120                 std::vector<int16_t> paddingValue_fp16(1);
121                 cv::convertFp16(paddingValue_fp32, paddingValue_fp16);
122                 outputs[0].setTo(paddingValue_fp16[0]);
123             }
124             else
125                 outputs[0].setTo(paddingValue);
126             inputs[0].copyTo(outputs[0](dstRanges));
127         }
128         else if (paddingType == "reflect")
129         {
130             CV_Assert(inputs.size() == 1);
131             CV_Assert(outputs.size() == 1);
132             CV_Assert(inputs[0].dims == 4);
133             CV_Assert(outputs[0].dims == 4);
134
135             if (inputs[0].size[0] != outputs[0].size[0] || inputs[0].size[1] != outputs[0].size[1])
136                 CV_Error(Error::StsNotImplemented, "Only spatial reflection padding is supported.");
137
138             const int inpHeight = inputs[0].size[2];
139             const int inpWidth = inputs[0].size[3];
140             const int outHeight = outputs[0].size[2];
141             const int outWidth = outputs[0].size[3];
142             const int padTop = dstRanges[2].start;
143             const int padBottom = outHeight - dstRanges[2].end;
144             const int padLeft = dstRanges[3].start;
145             const int padRight = outWidth - dstRanges[3].end;
146             CV_CheckLT(padTop, inpHeight, ""); CV_CheckLT(padBottom, inpHeight, "");
147             CV_CheckLT(padLeft, inpWidth, ""); CV_CheckLT(padRight, inpWidth, "");
148
149             for (size_t n = 0; n < inputs[0].size[0]; ++n)
150             {
151                 for (size_t ch = 0; ch < inputs[0].size[1]; ++ch)
152                 {
153                     copyMakeBorder(getPlane(inputs[0], n, ch),
154                                    getPlane(outputs[0], n, ch),
155                                    padTop, padBottom, padLeft, padRight,
156                                    BORDER_REFLECT_101);
157                 }
158             }
159         }
160         else
161             CV_Error(Error::StsNotImplemented, "Unknown padding type: " + paddingType);
162     }
163
164     virtual Ptr<BackendNode> initHalide(const std::vector<Ptr<BackendWrapper> > &inputs) CV_OVERRIDE
165     {
166 #ifdef HAVE_HALIDE
167         int inW, inH, inC, inN;
168         int minN = std::max(dstRanges[0].start, 0);
169         int minC = std::max(dstRanges[1].start, 0);
170         int minY = std::max(dstRanges[2].start, 0);
171         int minX = std::max(dstRanges[3].start, 0);
172         Halide::Buffer<float> inputBuffer = halideBuffer(inputs[0]);
173         getCanonicalSize(inputBuffer, &inW, &inH, &inC, &inN);
174
175         Halide::Var x("x"), y("y"), c("c"), n("n");
176         Halide::Func top = (name.empty() ? Halide::Func() : Halide::Func(name));
177         Halide::Func padded =
178             Halide::BoundaryConditions::constant_exterior(inputBuffer, paddingValue);
179         top(x, y, c, n) = padded(x - minX, y - minY, c - minC, n - minN);
180         return Ptr<BackendNode>(new HalideBackendNode(top));
181 #endif  // HAVE_HALIDE
182         return Ptr<BackendNode>();
183     }
184
185 #ifdef HAVE_INF_ENGINE
186     virtual Ptr<BackendNode> initInfEngine(const std::vector<Ptr<BackendWrapper> >&) CV_OVERRIDE
187     {
188         InferenceEngine::Builder::Layer ieLayer(name);
189         ieLayer.setName(name);
190         ieLayer.setType("Pad");
191
192         std::vector<int> begins(paddings.size(), 0), ends(paddings.size(), 0);
193         for (int i = 0; i < paddings.size(); ++i)
194         {
195             begins[i] = paddings[i].first;
196             ends[i] = paddings[i].second;
197         }
198         ieLayer.getParameters()["pads_begin"] = begins;
199         ieLayer.getParameters()["pads_end"] = ends;
200         ieLayer.getParameters()["pad_mode"] = paddingType;
201         if (paddingType == "constant")
202             ieLayer.getParameters()["pad_value"] = paddingValue;
203
204         ieLayer.setInputPorts(std::vector<InferenceEngine::Port>(1));
205         ieLayer.setOutputPorts(std::vector<InferenceEngine::Port>(1));
206         return Ptr<BackendNode>(new InfEngineBackendNode(ieLayer));
207     }
208 #endif
209
210 private:
211     std::vector<std::pair<int, int> > paddings;  // Pairs pad before, pad after.
212     std::vector<Range> dstRanges;
213     int inputDims;
214     float paddingValue;
215     std::string paddingType;
216 };
217
218 Ptr<PaddingLayer> PaddingLayer::create(const LayerParams &params)
219 {
220     return Ptr<PaddingLayer>(new PaddingLayerImpl(params));
221 }
222
223 }
224 }