Compute Library
 22.11
CpuScale.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2021-2022 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 "src/common/utils/Log.h"
30 #include "support/Rounding.h"
31 
32 namespace arm_compute
33 {
34 namespace cpu
35 {
36 namespace
37 {
38 void precompute_dx_dy_offsets(ITensor *dx, ITensor *dy, ITensor *offsets, float wr, float hr, SamplingPolicy sampling_policy, bool align_corners)
39 {
40  ARM_COMPUTE_ERROR_ON(offsets == nullptr);
41  float sampling_offset = 0.0f;
42  if(sampling_policy == SamplingPolicy::CENTER)
43  {
44  sampling_offset = 0.5f;
45  }
46 
47  Window win;
48  win.set(Window::DimX, Window::Dimension(0, offsets->info()->dimension(0), 1));
49  win.set(Window::DimY, Window::Dimension(0, offsets->info()->dimension(1), 1));
50 
51  if(dx != nullptr && dy != nullptr)
52  {
53  // Pre-compute the offset and pixel's distance for BILINEAR interpolation
54  Iterator offsets_it(offsets, win);
55  Iterator dx_it(dx, win);
56  Iterator dy_it(dy, win);
57 
58  execute_window_loop(win, [&](const Coordinates & id)
59  {
60  const float in_x = (id.x() + sampling_offset) * wr - sampling_offset;
61  const float in_y = (id.y() + sampling_offset) * hr - sampling_offset;
62  const int in_xi = std::floor(in_x);
63  const int in_yi = std::floor(in_y);
64 
65  *reinterpret_cast<int32_t *>(offsets_it.ptr()) = in_xi;
66  *reinterpret_cast<float *>(dx_it.ptr()) = in_x - in_xi;
67  *reinterpret_cast<float *>(dy_it.ptr()) = in_y - in_yi;
68  },
69  offsets_it, dx_it, dy_it);
70  }
71  else
72  {
73  // Pre-compute the offset for NEAREST interpolation
74  Iterator offsets_it(offsets, win);
75 
76  execute_window_loop(win, [&](const Coordinates & id)
77  {
78  const float float_in_xi = (id.x() + sampling_offset) * wr;
79  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));
80  *reinterpret_cast<int32_t *>(offsets_it.ptr()) = in_xi;
81  },
82  offsets_it);
83  }
84 }
85 } // namespace
86 
88 {
91  ARM_COMPUTE_LOG_PARAMS(src, dst, info);
92 
93  _scale_info = info;
94  _is_prepared = false;
95 
96  // Get data layout and width/height indices
97  _data_layout = _scale_info.data_layout == DataLayout::UNKNOWN ? src->data_layout() : _scale_info.data_layout;
100 
101  // Compute the ratio between source width/height and destination width/height
102  const bool is_align_corners_used = _scale_info.align_corners && arm_compute::scale_utils::is_align_corners_allowed_sampling_policy(_scale_info.sampling_policy);
103  const auto wr = arm_compute::scale_utils::calculate_resize_ratio(src->dimension(idx_width), dst->dimension(idx_width), is_align_corners_used);
104  const auto hr = arm_compute::scale_utils::calculate_resize_ratio(src->dimension(idx_height), dst->dimension(idx_height), is_align_corners_used);
105 
106  // Area interpolation behaves as Nearest Neighbour in case of up-sampling
107  InterpolationPolicy policy_to_use = (_scale_info.interpolation_policy == InterpolationPolicy::AREA && wr <= 1.f
108  && hr <= 1.f) ?
110  _scale_info.interpolation_policy;
111 
112  // Get the tensor shape
113  TensorShape shape(dst->dimension(idx_width));
114  shape.set(1, dst->dimension(idx_height), false);
115 
116  TensorInfo tensor_info_offsets(shape, Format::S32);
117  TensorInfo tensor_info_dxdy(shape, Format::F32);
118 
119  auto dx = std::make_unique<TensorInfo>(tensor_info_dxdy);
120  auto dy = std::make_unique<TensorInfo>(tensor_info_dxdy);
121  auto offsets = std::make_unique<TensorInfo>(tensor_info_offsets);
122  auto scale_kernel = std::make_unique<kernels::CpuScaleKernel>();
123  switch(policy_to_use)
124  {
126  {
127  scale_kernel->configure(src, nullptr, nullptr, offsets.get(), dst, info);
128  break;
129  }
131  {
132  scale_kernel->configure(src, dx.get(), dy.get(), offsets.get(), dst, info);
133  break;
134  }
136  {
137  scale_kernel->configure(src, nullptr, nullptr, nullptr, dst, info);
138  break;
139  }
140  default:
141  ARM_COMPUTE_ERROR("Unsupported interpolation mode");
142  }
143  _kernel = std::move(scale_kernel);
144 }
145 
147 {
150 
151  ITensorInfo *offsets = nullptr;
152  ITensorInfo *dx = nullptr;
153  ITensorInfo *dy = nullptr;
154 
155  // Get data layout and width/height indices
159 
160  // Compute the ratio between source width/height and destination width/height
162  const auto wr = arm_compute::scale_utils::calculate_resize_ratio(src->dimension(idx_width), dst->dimension(idx_width), is_align_corners_used);
163  const auto hr = arm_compute::scale_utils::calculate_resize_ratio(src->dimension(idx_height), dst->dimension(idx_height), is_align_corners_used);
164 
165  // Area interpolation behaves as Nearest Neighbour in case of up-sampling
167 
168  // Get the tensor shape of auxilary buffers
169  const TensorShape shape(dst->dimension(idx_width), dst->dimension(idx_height));
170  TensorInfo tensor_info_offsets(shape, Format::S32);
171  TensorInfo tensor_info_dx(shape, Format::F32);
172  TensorInfo tensor_info_dy(shape, Format::F32);
173  switch(policy_to_use)
174  {
176  offsets = &tensor_info_offsets;
177  break;
179  offsets = &tensor_info_offsets;
180  dx = &tensor_info_dx;
181  dy = &tensor_info_dy;
182  break;
183  default:
184  break;
185  }
186 
187  ARM_COMPUTE_RETURN_ON_ERROR(kernels::CpuScaleKernel::validate(src->clone().get(), dx, dy, offsets, dst->clone().get(), info));
188  return Status{};
189 }
190 
192 {
193  if(!_is_prepared)
194  {
195  _is_prepared = true;
196  const auto src = tensors.get_const_tensor(TensorType::ACL_SRC);
197  auto dst = tensors.get_tensor(TensorType::ACL_DST);
198  auto dx = tensors.get_tensor(TensorType::ACL_INT_0);
199  auto dy = tensors.get_tensor(TensorType::ACL_INT_1);
200  auto offsets = tensors.get_tensor(TensorType::ACL_INT_2);
201 
202  // Get data layout and width/height indices
205 
206  // Compute the ratio between source width/height and destination width/height
207  const bool is_align_corners_used = _scale_info.align_corners && arm_compute::scale_utils::is_align_corners_allowed_sampling_policy(_scale_info.sampling_policy);
208  const auto wr = arm_compute::scale_utils::calculate_resize_ratio(src->info()->dimension(idx_width), dst->info()->dimension(idx_width), is_align_corners_used);
209  const auto hr = arm_compute::scale_utils::calculate_resize_ratio(src->info()->dimension(idx_height), dst->info()->dimension(idx_height), is_align_corners_used);
210 
211  // Area interpolation behaves as Nearest Neighbour in case of up-sampling
212  InterpolationPolicy policy_to_use = (_scale_info.interpolation_policy == InterpolationPolicy::AREA && wr <= 1.f
213  && hr <= 1.f) ?
215  _scale_info.interpolation_policy;
216  const SamplingPolicy sampling_policy = _scale_info.sampling_policy;
217 
218  bool precompute_indices_weights = arm_compute::scale_utils::is_precomputation_required(_data_layout, src->info()->data_type(), policy_to_use, _scale_info.border_mode);
219 
220  if(precompute_indices_weights)
221  {
222  switch(policy_to_use)
223  {
225  {
226  // Pre-compute offsets for nearest interpolation
227  precompute_dx_dy_offsets(nullptr, nullptr, offsets, wr, hr, sampling_policy, is_align_corners_used);
228  break;
229  }
231  {
232  // Pre-compute dx, dy and offsets for bilinear interpolation
233  precompute_dx_dy_offsets(dx, dy, offsets, wr, hr, sampling_policy, is_align_corners_used);
234  break;
235  }
237  {
238  break;
239  }
240  default:
241  ARM_COMPUTE_ERROR("Unsupported interpolation mode");
242  }
243  }
244  else
245  {
246  if(policy_to_use != InterpolationPolicy::NEAREST_NEIGHBOR && policy_to_use != InterpolationPolicy::BILINEAR && policy_to_use != InterpolationPolicy::AREA)
247  {
248  ARM_COMPUTE_ERROR("Unsupported interpolation mode");
249  }
250  }
251  }
252 }
253 
255 {
256  ARM_COMPUTE_ERROR_ON_MSG(tensors.empty(), "No inputs provided");
257  prepare(tensors);
258  NEScheduler::get().schedule_op(_kernel.get(), Window::DimY, _kernel->window(), tensors);
259 }
260 } // namespace cpu
261 } // namespace arm_compute
BorderMode border_mode
Border mode policy.
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:254
#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:191
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:49
#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:146
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:87
#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:31
bool is_precomputation_required(DataLayout data_layout, DataType data_type, InterpolationPolicy policy, BorderMode border_mode)
Returns if precomputation of indices and/or weights is required or/not.
Definition: ScaleUtils.cpp:43
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