Compute Library
 19.08
CLPadLayer.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018-2019 ARM Limited.
3  *
4  * SPDX-License-Identifier: MIT
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
25 
27 #include "arm_compute/core/Types.h"
30 
31 namespace arm_compute
32 {
34  : _copy_kernel(), _mode(), _padding(), _memset_kernel(), _num_dimensions(0), _slice_functions(), _concat_functions(), _slice_results(), _concat_results()
35 {
36 }
37 
38 void CLPadLayer::configure_constant_mode(ICLTensor *input, ICLTensor *output, const PaddingList &padding, const PixelValue constant_value)
39 {
40  // Set the pages of the output to the constant_value.
41  _memset_kernel.configure(output, constant_value);
42 
43  // Fill out padding list with zeroes.
44  PaddingList padding_extended = padding;
45  for(size_t i = padding.size(); i < TensorShape::num_max_dimensions; i++)
46  {
47  padding_extended.emplace_back(PaddingInfo{ 0, 0 });
48  }
49 
50  // Create a window within the output tensor where the input will be copied.
51  Window copy_window = Window();
52  for(uint32_t i = 0; i < output->info()->num_dimensions(); ++i)
53  {
54  copy_window.set(i, Window::Dimension(padding_extended[i].first, padding_extended[i].first + input->info()->dimension(i), 1));
55  }
56  // Copy the input to the output, leaving the padding filled with the constant_value.
57  _copy_kernel.configure(input, output, PaddingList(), &copy_window);
58 }
59 
60 void CLPadLayer::configure_reflect_symmetric_mode(ICLTensor *input, ICLTensor *output)
61 {
62  int64_t last_padding_dimension = _padding.size() - 1;
63  // Reflecting can be performed by effectively unfolding the input as follows:
64  // For each dimension starting at DimX:
65  // Create a before and after slice, which values depend on the selected padding mode
66  // Concatenate the before and after padding with the tensor to be padded
67 
68  // Two strided slice functions will be required for each dimension padded as well as a
69  // concatenate function and the tensors to hold the temporary results.
70  _slice_functions.resize(2 * _num_dimensions);
71  _slice_results.resize(2 * _num_dimensions);
72  _concat_functions.resize(_num_dimensions);
73  _concat_results.resize(_num_dimensions - 1);
74 
75  Coordinates starts_before{};
76  Coordinates ends_before{};
77  Coordinates starts_after{};
78  Coordinates ends_after{};
79  Coordinates strides{};
80  ICLTensor *prev = input;
81  for(uint32_t i = 0; i < _num_dimensions; ++i)
82  {
83  // Values in strides from the previous dimensions need to be set to 1 to avoid reversing again.
84  if(i > 0)
85  {
86  strides.set(i - 1, 1);
87  }
88 
89  if(_padding[i].first > 0 || _padding[i].second > 0)
90  {
91  // Set the starts, ends, and strides values for the current dimension.
92  // Due to the bit masks passed to strided slice, the values below the current dimension in
93  // starts and ends will be ignored so do not need to be modified.
94  if(_mode == PaddingMode::REFLECT)
95  {
96  starts_before.set(i, _padding[i].first);
97  ends_before.set(i, 0);
98  starts_after.set(i, input->info()->dimension(i) - 2);
99  ends_after.set(i, input->info()->dimension(i) - _padding[i].second - 2);
100  strides.set(i, -1);
101  }
102  else
103  {
104  starts_before.set(i, _padding[i].first - 1);
105  ends_before.set(i, -1);
106  starts_after.set(i, input->info()->dimension(i) - 1);
107  ends_after.set(i, input->info()->dimension(i) - _padding[i].second - 1);
108  strides.set(i, -1);
109  }
110 
111  // Strided slice wraps negative indexes around to the end of the range,
112  // instead this should indicate use of the full range and so the bit mask will be modified.
113  const int32_t begin_mask_before = starts_before[i] < 0 ? ~0 : ~(1u << i);
114  const int32_t end_mask_before = ends_before[i] < 0 ? ~0 : ~(1u << i);
115  const int32_t begin_mask_after = starts_after[i] < 0 ? ~0 : ~(1u << i);
116  const int32_t end_mask_after = ends_after[i] < 0 ? ~0 : ~(1u << i);
117 
118  // Reflect the input values for the padding before and after the input.
119  std::vector<ICLTensor *> concat_vector;
120  if(_padding[i].first > 0)
121  {
122  if(i < prev->info()->num_dimensions())
123  {
124  _slice_functions[2 * i].configure(prev, &_slice_results[2 * i], starts_before, ends_before, strides, begin_mask_before, end_mask_before);
125  concat_vector.push_back(&_slice_results[2 * i]);
126  }
127  else
128  {
129  // Performing the slice is unnecessary if the result would simply be a copy of the tensor.
130  concat_vector.push_back(prev);
131  }
132  }
133  concat_vector.push_back(prev);
134  if(_padding[i].second > 0)
135  {
136  if(i < prev->info()->num_dimensions())
137  {
138  _slice_functions[2 * i + 1].configure(prev, &_slice_results[2 * i + 1], starts_after, ends_after, strides, begin_mask_after, end_mask_after);
139  concat_vector.push_back(&_slice_results[2 * i + 1]);
140  }
141  else
142  {
143  // Performing the slice is unnecessary if the result would simply be a copy of the tensor.
144  concat_vector.push_back(prev);
145  }
146  }
147  // Concatenate the padding before and after with the input.
148  ICLTensor *out = (static_cast<int32_t>(i) == last_padding_dimension) ? output : &_concat_results[i];
149  _concat_functions[i].configure(concat_vector, out, i);
150  prev = out;
151  }
152  }
153  for(uint32_t i = 0; i < _num_dimensions; ++i)
154  {
155  if((static_cast<int32_t>(i) != last_padding_dimension))
156  {
157  _concat_results[i].allocator()->allocate();
158  }
159  _slice_results[2 * i].allocator()->allocate();
160  _slice_results[2 * i + 1].allocator()->allocate();
161  }
162 }
163 
164 void CLPadLayer::configure(ICLTensor *input, ICLTensor *output, const PaddingList &padding, PixelValue constant_value, PaddingMode mode)
165 {
166  ARM_COMPUTE_ERROR_THROW_ON(validate(input->info(), output->info(), padding, constant_value, mode));
167 
168  _padding = padding;
169  _mode = mode;
170 
171  TensorShape padded_shape = misc::shape_calculator::compute_padded_shape(input->info()->tensor_shape(), _padding);
172 
173  auto_init_if_empty(*output->info(), input->info()->clone()->set_tensor_shape(padded_shape));
174 
175  // Find the last dimension requiring padding so that it is known when to write to output and whether any padding is applied.
176  int64_t last_padding_dimension = _padding.size() - 1;
177  for(; last_padding_dimension >= 0; --last_padding_dimension)
178  {
179  if(_padding[last_padding_dimension].first > 0 || _padding[last_padding_dimension].second > 0)
180  {
181  break;
182  }
183  }
184  _num_dimensions = last_padding_dimension + 1;
185  if(_num_dimensions > 0)
186  {
187  switch(_mode)
188  {
190  {
191  configure_constant_mode(input, output, padding, constant_value);
192  break;
193  }
196  {
197  configure_reflect_symmetric_mode(input, output);
198  break;
199  }
200  default:
201  ARM_COMPUTE_ERROR("Padding mode not supported.");
202  }
203  }
204  else
205  {
206  // Copy the input to the whole output if no padding is applied
207  _copy_kernel.configure(input, output);
208  }
209 }
210 
211 Status CLPadLayer::validate(const ITensorInfo *input, const ITensorInfo *output, const PaddingList &padding, PixelValue constant_value, PaddingMode mode)
212 {
214 
216 
217  // Use CLCopyKernel and CLMemsetKernel to validate all padding modes as this includes all of the shape and info validation.
218  PaddingList padding_extended = padding;
219  for(size_t i = padding.size(); i < TensorShape::num_max_dimensions; i++)
220  {
221  padding_extended.emplace_back(PaddingInfo{ 0, 0 });
222  }
223 
224  Window copy_window = Window();
225  for(uint32_t i = 0; i < padded_shape.num_dimensions(); ++i)
226  {
227  copy_window.set(i, Window::Dimension(padding_extended[i].first, padding_extended[i].first + input->dimension(i), 1));
228  }
229  if(output->total_size() > 0)
230  {
233  ARM_COMPUTE_RETURN_ON_ERROR(CLCopyKernel::validate(input, output, PaddingList(), &copy_window));
234  ARM_COMPUTE_RETURN_ON_ERROR(CLMemsetKernel::validate(output, constant_value));
235  }
236  else
237  {
238  ARM_COMPUTE_RETURN_ON_ERROR(CLCopyKernel::validate(input, &input->clone()->set_tensor_shape(padded_shape), PaddingList(), &copy_window));
239  ARM_COMPUTE_RETURN_ON_ERROR(CLMemsetKernel::validate(&input->clone()->set_tensor_shape(padded_shape), constant_value));
240  }
241 
242  switch(mode)
243  {
245  {
246  break;
247  }
250  {
251  for(uint32_t i = 0; i < padding.size(); ++i)
252  {
253  if(mode == PaddingMode::REFLECT)
254  {
255  ARM_COMPUTE_RETURN_ERROR_ON(padding[i].first >= input->dimension(i));
256  ARM_COMPUTE_RETURN_ERROR_ON(padding[i].second >= input->dimension(i));
257  }
258  else
259  {
260  ARM_COMPUTE_RETURN_ERROR_ON(padding[i].first > input->dimension(i));
261  ARM_COMPUTE_RETURN_ERROR_ON(padding[i].second > input->dimension(i));
262  }
263  }
264  break;
265  }
266  default:
267  {
268  ARM_COMPUTE_ERROR("Invalid mode");
269  }
270  }
271  return Status{};
272 }
273 
275 {
276  if(_num_dimensions > 0)
277  {
278  switch(_mode)
279  {
281  {
282  CLScheduler::get().enqueue(_memset_kernel, false);
283  CLScheduler::get().enqueue(_copy_kernel, true);
284  break;
285  }
288  {
289  for(uint32_t i = 0; i < _num_dimensions; ++i)
290  {
291  if(_padding[i].first > 0 || _padding[i].second > 0)
292  {
293  if(_padding[i].first > 0 && _slice_results[2 * i].info()->total_size() > 0)
294  {
295  _slice_functions[2 * i].run();
296  }
297  if(_padding[i].second > 0 && _slice_results[2 * i + 1].info()->total_size() > 0)
298  {
299  _slice_functions[2 * i + 1].run();
300  }
302  _concat_functions[i].run();
304  }
305  }
306  break;
307  }
308  default:
309  ARM_COMPUTE_ERROR("Padding mode not supported.");
310  }
311  }
312  else
313  {
314  CLScheduler::get().enqueue(_copy_kernel, true);
315  }
316 }
317 } // namespace arm_compute
static Status validate(const ITensorInfo *tensor, const PixelValue &constant_value, Window *window=nullptr)
Static function to check if given info will lead to a valid configuration of CLMemsetKernel.
#define ARM_COMPUTE_ERROR(...)
Print the given message then throw an std::runtime_error.
Definition: Error.h:261
virtual size_t num_dimensions() const =0
The number of dimensions of the tensor (rank)
Class describing the value of a pixel for any image format.
Definition: PixelValue.h:34
Shape of a tensor.
Definition: TensorShape.h:39
virtual size_t dimension(size_t index) const =0
Return the size of the requested dimension.
std::vector< PaddingInfo > PaddingList
List of padding information.
Definition: Types.h:445
static CLScheduler & get()
Access the scheduler singleton.
Definition: CLScheduler.cpp:41
#define ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(...)
Definition: Validate.h:545
#define ARM_COMPUTE_RETURN_ON_ERROR(status)
Checks if a status contains an error and returns it.
Definition: Error.h:193
void configure(const ICLTensor *input, ICLTensor *output, const PaddingList &padding=PaddingList(), Window *output_window=nullptr)
Initialize the kernel's input, output.
Store the tensor's metadata.
Definition: ITensorInfo.h:40
#define ARM_COMPUTE_ERROR_THROW_ON(status)
Definition: Error.h:327
Describe one of the image's dimensions with a start, end and step.
Definition: Window.h:75
Status class.
Definition: Error.h:52
static Status validate(const ITensorInfo *input, const ITensorInfo *output, const PaddingList &padding, PixelValue constant_value=PixelValue(), PaddingMode mode=PaddingMode::CONSTANT)
Static function to check if given info will lead to a valid configuration of CLPadLayer.
Definition: CLPadLayer.cpp:211
#define ARM_COMPUTE_RETURN_ERROR_ON(cond)
If the condition is true, an error is returned.
Definition: Error.h:244
Copyright (c) 2017-2018 ARM Limited.
bool auto_init_if_empty(ITensorInfo &info, const TensorShape &shape, int num_channels, DataType data_type, QuantizationInfo quantization_info=QuantizationInfo())
Auto initialize the tensor info (shape, number of channels and data type) if the current assignment i...
Definition: Helpers.inl:201
virtual const TensorShape & tensor_shape() const =0
Size for each dimension of the tensor.
PaddingMode
Padding mode to use for PadLayer.
Definition: Types.h:155
void configure(ICLTensor *input, ICLTensor *output, const PaddingList &padding, PixelValue constant_value=PixelValue(), PaddingMode mode=PaddingMode::CONSTANT)
Initialize the function.
Definition: CLPadLayer.cpp:164
#define ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DIMENSIONS(...)
Definition: Validate.h:288
virtual std::unique_ptr< T > clone() const =0
Provide a clone of the current object of class T.
virtual ITensorInfo * info() const =0
Interface to be implemented by the child class to return the tensor's metadata.
static Status validate(const ITensorInfo *input, const ITensorInfo *output, const PaddingList &padding=PaddingList(), Window *output_window=nullptr)
Static function to check if given info will lead to a valid configuration of CLCopyKernel.
void set(size_t dimension, const Dimension &dim)
Set the values of a given dimension.
Definition: Window.inl:48
TensorShape compute_padded_shape(const TensorShape &input_shape, const PaddingList &padding)
Calculate the padded shape of a tensor.
CLPadLayer()
Default constructor.
Definition: CLPadLayer.cpp:33
void enqueue(ICLKernel &kernel, bool flush=true)
Schedule the execution of the passed kernel if possible.
Definition: CLScheduler.cpp:95
void sync()
Blocks until all commands in the associated command queue have finished.
Definition: CLScheduler.h:151
Interface for OpenCL tensor.
Definition: ICLTensor.h:42
virtual size_t total_size() const =0
Returns the total size of the tensor in bytes.
std::pair< uint32_t, uint32_t > PaddingInfo
Padding information as a pair of unsigned int start/end.
Definition: Types.h:442
void configure(ICLTensor *tensor, const PixelValue &constant_value, Window *window=nullptr)
Initialise the kernel's tensor and filling value.
void run() override
Run the kernels contained in the function.
Definition: CLPadLayer.cpp:274
static constexpr size_t num_max_dimensions
Number of dimensions the tensor has.
Definition: Dimensions.h:45
Describe a multidimensional execution window.
Definition: Window.h:39