Compute Library
 22.05
CpuScale.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2021 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 
30 #include "src/common/utils/Log.h"
33 #include "support/Rounding.h"
34 
35 namespace arm_compute
36 {
37 namespace cpu
38 {
39 namespace
40 {
41 void precompute_dx_dy_offsets(ITensor *dx, ITensor *dy, ITensor *offsets, float wr, float hr, SamplingPolicy sampling_policy, bool align_corners)
42 {
43  ARM_COMPUTE_ERROR_ON(offsets == nullptr);
44  float sampling_offset = 0.0f;
45  if(sampling_policy == SamplingPolicy::CENTER)
46  {
47  sampling_offset = 0.5f;
48  }
49 
50  Window win;
51  win.set(Window::DimX, Window::Dimension(0, offsets->info()->dimension(0), 1));
52  win.set(Window::DimY, Window::Dimension(0, offsets->info()->dimension(1), 1));
53 
54  if(dx != nullptr && dy != nullptr)
55  {
56  // Pre-compute the offset and pixel's distance for BILINEAR interpolation
57  Iterator offsets_it(offsets, win);
58  Iterator dx_it(dx, win);
59  Iterator dy_it(dy, win);
60 
61  execute_window_loop(win, [&](const Coordinates & id)
62  {
63  const float in_x = (id.x() + sampling_offset) * wr - sampling_offset;
64  const float in_y = (id.y() + sampling_offset) * hr - sampling_offset;
65  const int in_xi = std::floor(in_x);
66  const int in_yi = std::floor(in_y);
67 
68  *reinterpret_cast<int32_t *>(offsets_it.ptr()) = in_xi;
69  *reinterpret_cast<float *>(dx_it.ptr()) = in_x - in_xi;
70  *reinterpret_cast<float *>(dy_it.ptr()) = in_y - in_yi;
71  },
72  offsets_it, dx_it, dy_it);
73  }
74  else
75  {
76  // Pre-compute the offset for NEAREST interpolation
77  Iterator offsets_it(offsets, win);
78 
79  execute_window_loop(win, [&](const Coordinates & id)
80  {
81  const float float_in_xi = (id.x() + sampling_offset) * wr;
82  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));
83  *reinterpret_cast<int32_t *>(offsets_it.ptr()) = in_xi;
84  },
85  offsets_it);
86  }
87 }
88 } // namespace
89 
91 {
94  ARM_COMPUTE_LOG_PARAMS(src, dst, info);
95 
96  _scale_info = info;
97  _is_prepared = false;
98 
99  // Get data layout and width/height indices
100  _data_layout = _scale_info.data_layout == DataLayout::UNKNOWN ? src->data_layout() : _scale_info.data_layout;
103 
104  // Compute the ratio between source width/height and destination width/height
105  const bool is_align_corners_used = _scale_info.align_corners && arm_compute::scale_utils::is_align_corners_allowed_sampling_policy(_scale_info.sampling_policy);
106  const auto wr = arm_compute::scale_utils::calculate_resize_ratio(src->dimension(idx_width), dst->dimension(idx_width), is_align_corners_used);
107  const auto hr = arm_compute::scale_utils::calculate_resize_ratio(src->dimension(idx_height), dst->dimension(idx_height), is_align_corners_used);
108 
109  // Area interpolation behaves as Nearest Neighbour in case of up-sampling
110  InterpolationPolicy policy_to_use = (_scale_info.interpolation_policy == InterpolationPolicy::AREA && wr <= 1.f
111  && hr <= 1.f) ?
113  _scale_info.interpolation_policy;
114 
115  // Get the tensor shape
116  TensorShape shape(dst->dimension(idx_width));
117  shape.set(1, dst->dimension(idx_height), false);
118 
119  TensorInfo tensor_info_offsets(shape, Format::S32);
120  TensorInfo tensor_info_dxdy(shape, Format::F32);
121 
122  auto dx = std::make_unique<TensorInfo>(tensor_info_dxdy);
123  auto dy = std::make_unique<TensorInfo>(tensor_info_dxdy);
124  auto offsets = std::make_unique<TensorInfo>(tensor_info_offsets);
125  auto scale_kernel = std::make_unique<kernels::CpuScaleKernel>();
126  switch(policy_to_use)
127  {
129  {
130  scale_kernel->configure(src, nullptr, nullptr, offsets.get(), dst, info);
131  break;
132  }
134  {
135  scale_kernel->configure(src, dx.get(), dy.get(), offsets.get(), dst, info);
136  break;
137  }
139  {
140  scale_kernel->configure(src, nullptr, nullptr, nullptr, dst, info);
141  break;
142  }
143  default:
144  ARM_COMPUTE_ERROR("Unsupported interpolation mode");
145  }
146  _kernel = std::move(scale_kernel);
147 }
148 
150 {
153 
154  ITensorInfo *offsets = nullptr;
155  ITensorInfo *dx = nullptr;
156  ITensorInfo *dy = nullptr;
157 
158  // Get data layout and width/height indices
162 
163  // Compute the ratio between source width/height and destination width/height
165  const auto wr = arm_compute::scale_utils::calculate_resize_ratio(src->dimension(idx_width), dst->dimension(idx_width), is_align_corners_used);
166  const auto hr = arm_compute::scale_utils::calculate_resize_ratio(src->dimension(idx_height), dst->dimension(idx_height), is_align_corners_used);
167 
168  // Area interpolation behaves as Nearest Neighbour in case of up-sampling
170 
171  // Get the tensor shape of auxilary buffers
172  const TensorShape shape(dst->dimension(idx_width), dst->dimension(idx_height));
173  TensorInfo tensor_info_offsets(shape, Format::S32);
174  TensorInfo tensor_info_dx(shape, Format::F32);
175  TensorInfo tensor_info_dy(shape, Format::F32);
176  switch(policy_to_use)
177  {
179  offsets = &tensor_info_offsets;
180  break;
182  offsets = &tensor_info_offsets;
183  dx = &tensor_info_dx;
184  dy = &tensor_info_dy;
185  break;
186  default:
187  break;
188  }
189 
190  ARM_COMPUTE_RETURN_ON_ERROR(kernels::CpuScaleKernel::validate(src->clone().get(), dx, dy, offsets, dst->clone().get(), info));
191  return Status{};
192 }
193 
195 {
196  if(!_is_prepared)
197  {
198  _is_prepared = true;
199  const auto src = tensors.get_const_tensor(TensorType::ACL_SRC);
200  auto dst = tensors.get_tensor(TensorType::ACL_DST);
201  auto dx = tensors.get_tensor(TensorType::ACL_INT_0);
202  auto dy = tensors.get_tensor(TensorType::ACL_INT_1);
203  auto offsets = tensors.get_tensor(TensorType::ACL_INT_2);
204 
205  // Get data layout and width/height indices
208 
209  // Compute the ratio between source width/height and destination width/height
210  const bool is_align_corners_used = _scale_info.align_corners && arm_compute::scale_utils::is_align_corners_allowed_sampling_policy(_scale_info.sampling_policy);
211  const auto wr = arm_compute::scale_utils::calculate_resize_ratio(src->info()->dimension(idx_width), dst->info()->dimension(idx_width), is_align_corners_used);
212  const auto hr = arm_compute::scale_utils::calculate_resize_ratio(src->info()->dimension(idx_height), dst->info()->dimension(idx_height), is_align_corners_used);
213 
214  // Area interpolation behaves as Nearest Neighbour in case of up-sampling
215  InterpolationPolicy policy_to_use = (_scale_info.interpolation_policy == InterpolationPolicy::AREA && wr <= 1.f
216  && hr <= 1.f) ?
218  _scale_info.interpolation_policy;
219  const SamplingPolicy sampling_policy = _scale_info.sampling_policy;
220 
221  switch(policy_to_use)
222  {
224  {
225  // Pre-compute offsets for nearest interpolation
226  precompute_dx_dy_offsets(nullptr, nullptr, offsets, wr, hr, sampling_policy, is_align_corners_used);
227  break;
228  }
230  {
231  // Pre-compute dx, dy and offsets for bilinear interpolation
232  precompute_dx_dy_offsets(dx, dy, offsets, wr, hr, sampling_policy, is_align_corners_used);
233  break;
234  }
236  {
237  break;
238  }
239  default:
240  ARM_COMPUTE_ERROR("Unsupported interpolation mode");
241  }
242  }
243 }
244 
246 {
247  ARM_COMPUTE_ERROR_ON_MSG(tensors.empty(), "No inputs provided");
248  prepare(tensors);
249  NEScheduler::get().schedule_op(_kernel.get(), Window::DimY, _kernel->window(), tensors);
250 }
251 } // namespace cpu
252 } // namespace arm_compute
InterpolationPolicy
Interpolation method.
Definition: Types.h:411
Shape of a tensor.
Definition: TensorShape.h:39
InterpolationPolicy interpolation_policy
Interpolation type to use.
virtual size_t dimension(size_t index) const =0
Return the size of the requested dimension.
bool empty() const
Checks if pack is empty.
Definition: ITensorPack.cpp:80
#define ARM_COMPUTE_ERROR(msg)
Print the given message then throw an std::runtime_error.
Definition: Error.h:352
void run(ITensorPack &tensors) override
Run the kernels contained in the function.
Definition: CpuScale.cpp:245
#define ARM_COMPUTE_RETURN_ON_ERROR(status)
Checks if a status contains an error and returns it.
Definition: Error.h:204
virtual void schedule_op(ICPPKernel *kernel, const Hints &hints, const Window &window, ITensorPack &tensors)=0
Runs the kernel in the same thread as the caller synchronously.
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
bool align_corners
Align corners of input and output.
Store the tensor&#39;s metadata.
Definition: ITensorInfo.h:40
#define ARM_COMPUTE_ERROR_THROW_ON(status)
Definition: Error.h:455
void prepare(ITensorPack &tensors) override
Prepare the function for executing.
Definition: CpuScale.cpp:194
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
SimpleTensor< float > src
Definition: DFT.cpp:155
Copyright (c) 2017-2022 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
#define ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(...)
Definition: Validate.h:159
1 channel, 1 S32 per channel
static Status validate(const ITensorInfo *src, const ITensorInfo *dst, const ScaleKernelInfo &info)
Static function to check if given info will lead to a valid configuration.
Definition: CpuScale.cpp:149
const ITensor * get_const_tensor(int id) const
Get constant tensor of a given id.
Definition: ITensorPack.cpp:54
static constexpr size_t DimX
Alias for dimension 0 also known as X dimension.
Definition: Window.h:43
SamplingPolicy sampling_policy
Sampling policy used by the interpolation.
#define ARM_COMPUTE_ERROR_ON_MSG(cond, msg)
Definition: Error.h:456
virtual std::unique_ptr< T > clone() const =0
Provide a clone of the current object of class T.
Samples are taken at pixel top left corner.
Output values are determined by averaging the source pixels whose areas fall under the area of the de...
static constexpr size_t DimY
Alias for dimension 1 also known as Y dimension.
Definition: Window.h:45
ScaleKernelInfo info(interpolation_policy, default_border_mode, PixelValue(), sampling_policy, false)
ITensor * get_tensor(int id)
Get tensor of a given id from the pac.
Definition: ITensorPack.cpp:64
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:193
static Status validate(const ITensorInfo *src, const ITensorInfo *dx, const ITensorInfo *dy, const ITensorInfo *offsets, ITensorInfo *dst, const ScaleKernelInfo &info)
Static function to check if given info will lead to a valid configuration.
Tensor packing service.
Definition: ITensorPack.h:39
void configure(ITensorInfo *src, ITensorInfo *dst, const ScaleKernelInfo &info)
Initialize the function&#39;s source, destination, interpolation type and border_mode.
Definition: CpuScale.cpp:90
#define ARM_COMPUTE_LOG_PARAMS(...)
#define ARM_COMPUTE_ERROR_ON_NULLPTR(...)
Definition: Validate.h:157
Store the tensor&#39;s metadata.
Definition: TensorInfo.h:43
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:77
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
DataLayout data_layout
Data layout to use.
DataLayout
[DataLayout enum definition]
Definition: Types.h:113
SamplingPolicy
Available Sampling Policies.
Definition: Types.h:104
virtual DataLayout data_layout() const =0
Get the data layout of the tensor.
static IScheduler & get()
Access the scheduler singleton.
Definition: Scheduler.cpp:94