Compute Library
 20.08
NEScale.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2016-2020 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/Error.h"
36 
38 
39 #include <cmath>
40 #include <cstddef>
41 #include <utility>
42 
43 namespace arm_compute
44 {
45 namespace
46 {
47 void precompute_dx_dy_offsets(ITensor *dx, ITensor *dy, ITensor *offsets, float wr, float hr, size_t input_element_size, SamplingPolicy sampling_policy, bool align_corners)
48 {
49  ARM_COMPUTE_ERROR_ON(nullptr == offsets);
50  ARM_COMPUTE_UNUSED(sampling_policy);
51  float sampling_offset = 0.0f;
52  if(sampling_policy == SamplingPolicy::CENTER)
53  {
54  sampling_offset = 0.5f;
55  }
56 
57  Window win;
58  win.set(Window::DimX, Window::Dimension(0, offsets->info()->dimension(0), 1));
59  win.set(Window::DimY, Window::Dimension(0, offsets->info()->dimension(1), 1));
60 
61  if(dx != nullptr && dy != nullptr)
62  {
63  // Pre-compute the offset and pixel's distance for BILINEAR interpolation
64  Iterator offsets_it(offsets, win);
65  Iterator dx_it(dx, win);
66  Iterator dy_it(dy, win);
67 
68  execute_window_loop(win, [&](const Coordinates & id)
69  {
70  const float in_x = (id.x() + sampling_offset) * wr - sampling_offset;
71  const float in_y = (id.y() + sampling_offset) * hr - sampling_offset;
72  const int in_xi = std::floor(in_x);
73  const int in_yi = std::floor(in_y);
74 
75  *reinterpret_cast<int32_t *>(offsets_it.ptr()) = in_xi * static_cast<int>(input_element_size);
76  *reinterpret_cast<float *>(dx_it.ptr()) = in_x - in_xi;
77  *reinterpret_cast<float *>(dy_it.ptr()) = in_y - in_yi;
78  },
79  offsets_it, dx_it, dy_it);
80  }
81  else
82  {
83  // Pre-compute the offset for NEAREST interpolation
84  Iterator offsets_it(offsets, win);
85 
86  execute_window_loop(win, [&](const Coordinates & id)
87  {
88  const float float_in_xi = (id.x() + sampling_offset) * wr;
89  const auto in_xi = static_cast<size_t>(align_corners ? arm_compute::utils::rounding::round_half_away_from_zero(float_in_xi) : std::floor(float_in_xi));
90 
91  *reinterpret_cast<int32_t *>(offsets_it.ptr()) = in_xi * input_element_size;
92  },
93  offsets_it);
94  }
95 }
96 } // namespace
97 
98 NEScale::NEScale() // NOLINT
99  : _offsets(),
100  _dx(),
101  _dy(),
102  _scale_kernel(),
103  _border_handler(),
104  _use_padding(true)
105 {
106 }
107 
109 {
112 
113  _use_padding = info.use_padding;
114  const bool is_align_corners_used = info.align_corners && arm_compute::scale_utils::is_align_corners_allowed_sampling_policy(info.sampling_policy);
115 
116  // Get data layout and width/height indices
117  const DataLayout data_layout = input->info()->data_layout();
120 
121  // Get the tensor shape
122  const TensorShape shape(output->info()->dimension(idx_width), output->info()->dimension(idx_height));
123 
124  // Compute the ratio between source width/height and destination width/height
125  const auto wr = arm_compute::scale_utils::calculate_resize_ratio(input->info()->dimension(idx_width), output->info()->dimension(idx_width), is_align_corners_used);
126  const auto hr = arm_compute::scale_utils::calculate_resize_ratio(input->info()->dimension(idx_height), output->info()->dimension(idx_height), is_align_corners_used);
127 
128  // Get the element size of the input image
129  const size_t input_element_size = input->info()->element_size();
130 
131  // Area interpolation behaves as Nearest Neighbour in case of up-sampling
132  const auto policy_to_use = (info.interpolation_policy == InterpolationPolicy::AREA && wr <= 1.f && hr <= 1.f) ? InterpolationPolicy::NEAREST_NEIGHBOR : info.interpolation_policy;
133 
134  switch(policy_to_use)
135  {
137  {
138  TensorInfo tensor_info_offsets(shape, Format::S32);
139  _offsets.allocator()->init(tensor_info_offsets);
140 
141  _scale_kernel.configure(input, nullptr, nullptr, &_offsets, output, info);
142 
143  // Allocate once the configure methods have been called
144  _offsets.allocator()->allocate();
145 
146  // Pre-compute offsets for nearest interpolation
147  precompute_dx_dy_offsets(nullptr, nullptr, &_offsets, wr, hr, input_element_size, info.sampling_policy, is_align_corners_used);
148  break;
149  }
151  {
152  TensorInfo tensor_info_offsets(shape, Format::S32);
153  TensorInfo tensor_info_dxdy(shape, Format::F32);
154 
155  _offsets.allocator()->init(tensor_info_offsets);
156  _dx.allocator()->init(tensor_info_dxdy);
157  _dy.allocator()->init(tensor_info_dxdy);
158 
159  _scale_kernel.configure(input, &_dx, &_dy, &_offsets, output, info);
160 
161  // Allocate once the configure methods have been called
162  _offsets.allocator()->allocate();
163  _dx.allocator()->allocate();
164  _dy.allocator()->allocate();
165 
166  // Pre-compute dx, dy and offsets for bilinear interpolation
167  precompute_dx_dy_offsets(&_dx, &_dy, &_offsets, wr, hr, input_element_size, info.sampling_policy, is_align_corners_used);
168  break;
169  }
171  {
172  _scale_kernel.configure(input, nullptr, nullptr, nullptr, output, info);
173  break;
174  }
175  default:
176  ARM_COMPUTE_ERROR("Unsupported interpolation mode");
177  }
178  if(info.use_padding)
179  {
180  _border_handler.configure(input, _scale_kernel.border_size(), info.border_mode, info.constant_border_value);
181  }
182 }
183 
184 void NEScale::configure(ITensor *input, ITensor *output, InterpolationPolicy policy, BorderMode border_mode, PixelValue constant_border_value, SamplingPolicy sampling_policy, bool use_padding,
185  bool align_corners)
186 {
187  configure(input, output, ScaleKernelInfo{ policy, border_mode, constant_border_value, sampling_policy, use_padding, align_corners });
188 }
189 
191 {
193  ARM_COMPUTE_RETURN_ERROR_ON(info.sampling_policy != SamplingPolicy::CENTER && info.sampling_policy != SamplingPolicy::TOP_LEFT);
194 
195  ITensorInfo *offsets = nullptr;
196  ITensorInfo *dx = nullptr;
197  ITensorInfo *dy = nullptr;
198 
199  // Get data layout and width/height indices
200  const DataLayout data_layout = input->data_layout();
203 
204  // Get the tensor shape of auxilary buffers
205  const TensorShape shape(output->dimension(idx_width), output->dimension(idx_height));
206 
207  TensorInfo tensor_info_offsets(shape, Format::S32);
208  TensorInfo tensor_info_dx(shape, Format::F32);
209  TensorInfo tensor_info_dy(shape, Format::F32);
210 
211  switch(info.interpolation_policy)
212  {
214  offsets = &tensor_info_offsets;
215  break;
217  offsets = &tensor_info_offsets;
218  dx = &tensor_info_dx;
219  dy = &tensor_info_dy;
220  break;
221  default:
222  break;
223  }
224 
225  ARM_COMPUTE_RETURN_ON_ERROR(NEScaleKernel::validate(input->clone().get(), dx, dy, offsets, output->clone().get(), info));
226  return Status{};
227 }
228 
230  BorderMode border_mode, PixelValue constant_border_value, SamplingPolicy sampling_policy, bool use_padding, bool align_corners)
231 {
232  ARM_COMPUTE_RETURN_ON_ERROR(NEScale::validate(input, output, ScaleKernelInfo{ policy, border_mode, constant_border_value, sampling_policy, use_padding, align_corners }));
233  return Status{};
234 }
235 
237 {
238  if(_use_padding)
239  {
240  NEScheduler::get().schedule(&_border_handler, Window::DimZ);
241  }
242  NEScheduler::get().schedule(&_scale_kernel, Window::DimY);
243 }
244 } // namespace arm_compute
BorderMode
Methods available to handle borders.
Definition: Types.h:264
Class describing the value of a pixel for any image format.
Definition: PixelValue.h:34
InterpolationPolicy
Interpolation method.
Definition: Types.h:369
Shape of a tensor.
Definition: TensorShape.h:39
const DataLayout data_layout
Definition: Im2Col.cpp:146
void init(const TensorAllocator &allocator, const Coordinates &coords, TensorInfo &sub_info)
Shares the same backing memory with another tensor allocator, while the tensor info might be differen...
virtual size_t dimension(size_t index) const =0
Return the size of the requested dimension.
NEScale()
Constructor.
Definition: NEScale.cpp:98
#define ARM_COMPUTE_ERROR(msg)
Print the given message then throw an std::runtime_error.
Definition: Error.h:352
#define ARM_COMPUTE_RETURN_ON_ERROR(status)
Checks if a status contains an error and returns it.
Definition: Error.h:204
1 channel, 1 F32 per channel
Output values are defined by bilinear interpolation between the pixels.
#define ARM_COMPUTE_ERROR_ON(cond)
If the condition is true then an error message is printed and an exception thrown.
Definition: Error.h:466
BorderSize border_size() const override
The size of the border for that kernel.
Store the tensor's metadata.
Definition: ITensorInfo.h:40
#define ARM_COMPUTE_ERROR_THROW_ON(status)
Definition: Error.h:455
static Status validate(const ITensorInfo *input, const ITensorInfo *output, InterpolationPolicy policy, BorderMode border_mode, PixelValue constant_border_value=PixelValue(), SamplingPolicy sampling_policy=SamplingPolicy::CENTER, bool use_padding=true, bool align_corners=false)
Static function to check if given info will lead to a valid configuration of NEScale.
Definition: NEScale.cpp:229
void configure(ITensor *tensor, BorderSize border_size, BorderMode border_mode, const PixelValue &constant_border_value=PixelValue())
Initialise the function.
T round_half_away_from_zero(T value)
Round floating-point value with half value rounding away from zero.
Definition: Rounding.h:106
Status class.
Definition: Error.h:52
Output values are defined to match the source pixel whose center is nearest to the sample position.
#define ARM_COMPUTE_RETURN_ERROR_ON(cond)
If the condition is true, an error is returned.
Definition: Error.h:296
Interface for NEON tensor.
Definition: ITensor.h:36
Copyright (c) 2017-2020 Arm Limited.
Samples are taken at pixel center.
bool is_align_corners_allowed_sampling_policy(SamplingPolicy sampling_policy)
Returns if aligned corners are allowed for the given sampling policy.
Definition: ScaleUtils.h:52
TensorAllocator * allocator()
Return a pointer to the tensor's allocator.
Definition: Tensor.cpp:48
1 channel, 1 S32 per channel
static Status validate(const ITensorInfo *input, const ITensorInfo *dx, const ITensorInfo *dy, const ITensorInfo *offsets, ITensorInfo *output, const ScaleKernelInfo &info)
Static function to check if given info will lead to a valid configuration of NEScaleKernel.
static constexpr size_t DimX
Alias for dimension 0 also known as X dimension.
Definition: Window.h:43
#define ARM_COMPUTE_UNUSED(...)
To avoid unused variables warnings.
Definition: Error.h:152
void allocate() override
Allocate size specified by TensorInfo of CPU memory.
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.
Samples are taken at pixel top left corner.
void configure(const ITensor *input, const ITensor *dx, const ITensor *dy, const ITensor *offsets, ITensor *output, const ScaleKernelInfo &info)
Initialise the kernel's inputs, output and interpolation policy.
Output values are determined by averaging the source pixels whose areas fall under the area of the de...
#define ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(...)
Definition: Validate.h:163
static constexpr size_t DimY
Alias for dimension 1 also known as Y dimension.
Definition: Window.h:45
#define ARM_COMPUTE_ERROR_ON_NULLPTR(...)
Definition: Validate.h:161
void run() override
Run the kernels contained in the function.
Definition: NEScale.cpp:236
virtual void schedule(ICPPKernel *kernel, const Hints &hints)=0
Runs the kernel in the same thread as the caller synchronously.
static constexpr size_t DimZ
Alias for dimension 2 also known as Z dimension.
Definition: Window.h:47
void configure(ITensor *input, ITensor *output, InterpolationPolicy policy, BorderMode border_mode, PixelValue constant_border_value=PixelValue(), SamplingPolicy sampling_policy=SamplingPolicy::CENTER, bool use_padding=true, bool align_corners=false)
Initialize the function's source, destination, interpolation type and border_mode.
Definition: NEScale.cpp:184
Store the tensor's metadata.
Definition: TensorInfo.h:45
void execute_window_loop(const Window &w, L &&lambda_function, Ts &&... iterators)
Iterate through the passed window, automatically adjusting the iterators and calling the lambda_funct...
Definition: Helpers.inl:128
float calculate_resize_ratio(size_t input_size, size_t output_size, bool align_corners=false)
Returns resize ratio between input and output with consideration of aligned corners.
Definition: ScaleUtils.cpp:27
size_t get_data_layout_dimension_index(const DataLayout data_layout, const DataLayoutDimension data_layout_dimension)
Get the index of the given dimension.
Definition: Helpers.inl:332
DataLayout
[DataLayout enum definition]
Definition: Types.h:120
SamplingPolicy
Available Sampling Policies.
Definition: Types.h:102
static IScheduler & get()
Access the scheduler singleton.
Definition: Scheduler.cpp:95