blob: 7df92969312ef66f973438ffd567d5bbddbfc2cc [file] [log] [blame]
Manuel Bottini10b38262021-02-19 18:16:44 +00001/*
Matthew Bentham1d062042023-07-06 13:13:59 +00002 * Copyright (c) 2021-2023 Arm Limited.
Manuel Bottini10b38262021-02-19 18:16:44 +00003 *
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 */
Georgios Pinitas7891a732021-08-20 21:39:25 +010024#include "src/cpu/operators/CpuScale.h"
Manuel Bottini10b38262021-02-19 18:16:44 +000025
Matthew Bentham1d062042023-07-06 13:13:59 +000026#include "arm_compute/core/Helpers.h"
Matthew Bentham1d062042023-07-06 13:13:59 +000027#include "arm_compute/core/TensorInfo.h"
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010028#include "arm_compute/runtime/NEON/NEScheduler.h"
29
ramelg013ae3d882021-09-12 23:07:47 +010030#include "src/common/utils/Log.h"
Manuel Bottini10b38262021-02-19 18:16:44 +000031#include "src/core/utils/ScaleUtils.h"
Georgios Pinitas7891a732021-08-20 21:39:25 +010032#include "src/cpu/kernels/CpuScaleKernel.h"
Manuel Bottini10b38262021-02-19 18:16:44 +000033#include "support/Rounding.h"
34
35namespace arm_compute
36{
37namespace cpu
38{
39namespace
40{
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010041void precompute_dx_dy_offsets(
42 ITensor *dx, ITensor *dy, ITensor *offsets, float wr, float hr, SamplingPolicy sampling_policy, bool align_corners)
Manuel Bottini10b38262021-02-19 18:16:44 +000043{
44 ARM_COMPUTE_ERROR_ON(offsets == nullptr);
45 float sampling_offset = 0.0f;
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010046 if (sampling_policy == SamplingPolicy::CENTER)
Manuel Bottini10b38262021-02-19 18:16:44 +000047 {
48 sampling_offset = 0.5f;
49 }
50
51 Window win;
52 win.set(Window::DimX, Window::Dimension(0, offsets->info()->dimension(0), 1));
53 win.set(Window::DimY, Window::Dimension(0, offsets->info()->dimension(1), 1));
54
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010055 if (dx != nullptr && dy != nullptr)
Manuel Bottini10b38262021-02-19 18:16:44 +000056 {
57 // Pre-compute the offset and pixel's distance for BILINEAR interpolation
58 Iterator offsets_it(offsets, win);
59 Iterator dx_it(dx, win);
60 Iterator dy_it(dy, win);
61
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010062 execute_window_loop(
63 win,
64 [&](const Coordinates &id)
65 {
66 const float in_x = (id.x() + sampling_offset) * wr - sampling_offset;
67 const float in_y = (id.y() + sampling_offset) * hr - sampling_offset;
68 const int in_xi = std::floor(in_x);
69 const int in_yi = std::floor(in_y);
Manuel Bottini10b38262021-02-19 18:16:44 +000070
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010071 *reinterpret_cast<int32_t *>(offsets_it.ptr()) = in_xi;
72 *reinterpret_cast<float *>(dx_it.ptr()) = in_x - in_xi;
73 *reinterpret_cast<float *>(dy_it.ptr()) = in_y - in_yi;
74 },
75 offsets_it, dx_it, dy_it);
Manuel Bottini10b38262021-02-19 18:16:44 +000076 }
77 else
78 {
79 // Pre-compute the offset for NEAREST interpolation
80 Iterator offsets_it(offsets, win);
81
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010082 execute_window_loop(
83 win,
84 [&](const Coordinates &id)
85 {
86 const float float_in_xi = (id.x() + sampling_offset) * wr;
87 const auto in_xi = static_cast<size_t>(
88 align_corners ? arm_compute::utils::rounding::round_half_away_from_zero(float_in_xi)
89 : std::floor(float_in_xi));
90 *reinterpret_cast<int32_t *>(offsets_it.ptr()) = in_xi;
91 },
92 offsets_it);
Manuel Bottini10b38262021-02-19 18:16:44 +000093 }
94}
95} // namespace
96
Manuel Bottini10b38262021-02-19 18:16:44 +000097void CpuScale::configure(ITensorInfo *src, ITensorInfo *dst, const ScaleKernelInfo &info)
98{
99 ARM_COMPUTE_ERROR_ON_NULLPTR(src, dst);
100 ARM_COMPUTE_ERROR_THROW_ON(CpuScale::validate(src, dst, info));
ramelg013ae3d882021-09-12 23:07:47 +0100101 ARM_COMPUTE_LOG_PARAMS(src, dst, info);
Manuel Bottini10b38262021-02-19 18:16:44 +0000102
Georgios Pinitas2eb5d162021-07-02 09:01:49 +0100103 _scale_info = info;
104 _is_prepared = false;
Manuel Bottini10b38262021-02-19 18:16:44 +0000105
106 // Get data layout and width/height indices
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100107 _data_layout = _scale_info.data_layout == DataLayout::UNKNOWN ? src->data_layout() : _scale_info.data_layout;
108 const int idx_width = get_data_layout_dimension_index(_data_layout, DataLayoutDimension::WIDTH);
Georgios Pinitas2eb5d162021-07-02 09:01:49 +0100109 const int idx_height = get_data_layout_dimension_index(_data_layout, DataLayoutDimension::HEIGHT);
Manuel Bottini10b38262021-02-19 18:16:44 +0000110
111 // Compute the ratio between source width/height and destination width/height
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100112 const bool is_align_corners_used =
113 _scale_info.align_corners &&
114 arm_compute::scale_utils::is_align_corners_allowed_sampling_policy(_scale_info.sampling_policy);
115 const auto wr = arm_compute::scale_utils::calculate_resize_ratio(src->dimension(idx_width),
116 dst->dimension(idx_width), is_align_corners_used);
117 const auto hr = arm_compute::scale_utils::calculate_resize_ratio(src->dimension(idx_height),
118 dst->dimension(idx_height), is_align_corners_used);
Manuel Bottini10b38262021-02-19 18:16:44 +0000119
120 // Area interpolation behaves as Nearest Neighbour in case of up-sampling
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100121 InterpolationPolicy policy_to_use =
122 (_scale_info.interpolation_policy == InterpolationPolicy::AREA && wr <= 1.f && hr <= 1.f)
123 ? InterpolationPolicy::NEAREST_NEIGHBOR
124 : _scale_info.interpolation_policy;
Manuel Bottini10b38262021-02-19 18:16:44 +0000125
126 // Get the tensor shape
127 TensorShape shape(dst->dimension(idx_width));
128 shape.set(1, dst->dimension(idx_height), false);
129
130 TensorInfo tensor_info_offsets(shape, Format::S32);
131 TensorInfo tensor_info_dxdy(shape, Format::F32);
132
133 auto dx = std::make_unique<TensorInfo>(tensor_info_dxdy);
134 auto dy = std::make_unique<TensorInfo>(tensor_info_dxdy);
135 auto offsets = std::make_unique<TensorInfo>(tensor_info_offsets);
136 auto scale_kernel = std::make_unique<kernels::CpuScaleKernel>();
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100137 switch (policy_to_use)
Manuel Bottini10b38262021-02-19 18:16:44 +0000138 {
139 case InterpolationPolicy::NEAREST_NEIGHBOR:
140 {
141 scale_kernel->configure(src, nullptr, nullptr, offsets.get(), dst, info);
142 break;
143 }
144 case InterpolationPolicy::BILINEAR:
145 {
146 scale_kernel->configure(src, dx.get(), dy.get(), offsets.get(), dst, info);
147 break;
148 }
149 case InterpolationPolicy::AREA:
150 {
151 scale_kernel->configure(src, nullptr, nullptr, nullptr, dst, info);
152 break;
153 }
154 default:
155 ARM_COMPUTE_ERROR("Unsupported interpolation mode");
156 }
157 _kernel = std::move(scale_kernel);
158}
159
160Status CpuScale::validate(const ITensorInfo *src, const ITensorInfo *dst, const ScaleKernelInfo &info)
161{
162 ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(src, dst);
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100163 ARM_COMPUTE_RETURN_ERROR_ON(info.sampling_policy != SamplingPolicy::CENTER &&
164 info.sampling_policy != SamplingPolicy::TOP_LEFT);
Manuel Bottini10b38262021-02-19 18:16:44 +0000165
166 ITensorInfo *offsets = nullptr;
167 ITensorInfo *dx = nullptr;
168 ITensorInfo *dy = nullptr;
169
170 // Get data layout and width/height indices
171 const DataLayout data_layout = info.data_layout == DataLayout::UNKNOWN ? src->data_layout() : info.data_layout;
172 const int idx_width = get_data_layout_dimension_index(data_layout, DataLayoutDimension::WIDTH);
173 const int idx_height = get_data_layout_dimension_index(data_layout, DataLayoutDimension::HEIGHT);
174
175 // Compute the ratio between source width/height and destination width/height
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100176 const bool is_align_corners_used =
177 info.align_corners && arm_compute::scale_utils::is_align_corners_allowed_sampling_policy(info.sampling_policy);
178 const auto wr = arm_compute::scale_utils::calculate_resize_ratio(src->dimension(idx_width),
179 dst->dimension(idx_width), is_align_corners_used);
180 const auto hr = arm_compute::scale_utils::calculate_resize_ratio(src->dimension(idx_height),
181 dst->dimension(idx_height), is_align_corners_used);
Manuel Bottini10b38262021-02-19 18:16:44 +0000182
183 // Area interpolation behaves as Nearest Neighbour in case of up-sampling
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100184 InterpolationPolicy policy_to_use =
185 (info.interpolation_policy == InterpolationPolicy::AREA && wr <= 1.f && hr <= 1.f)
186 ? InterpolationPolicy::NEAREST_NEIGHBOR
187 : info.interpolation_policy;
Manuel Bottini10b38262021-02-19 18:16:44 +0000188
189 // Get the tensor shape of auxilary buffers
190 const TensorShape shape(dst->dimension(idx_width), dst->dimension(idx_height));
191 TensorInfo tensor_info_offsets(shape, Format::S32);
192 TensorInfo tensor_info_dx(shape, Format::F32);
193 TensorInfo tensor_info_dy(shape, Format::F32);
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100194 switch (policy_to_use)
Manuel Bottini10b38262021-02-19 18:16:44 +0000195 {
196 case InterpolationPolicy::NEAREST_NEIGHBOR:
197 offsets = &tensor_info_offsets;
198 break;
199 case InterpolationPolicy::BILINEAR:
200 offsets = &tensor_info_offsets;
201 dx = &tensor_info_dx;
202 dy = &tensor_info_dy;
203 break;
204 default:
205 break;
206 }
207
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100208 ARM_COMPUTE_RETURN_ON_ERROR(
209 kernels::CpuScaleKernel::validate(src->clone().get(), dx, dy, offsets, dst->clone().get(), info));
Manuel Bottini10b38262021-02-19 18:16:44 +0000210 return Status{};
211}
212
213void CpuScale::prepare(ITensorPack &tensors)
214{
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100215 if (!_is_prepared)
Manuel Bottini10b38262021-02-19 18:16:44 +0000216 {
217 _is_prepared = true;
218 const auto src = tensors.get_const_tensor(TensorType::ACL_SRC);
219 auto dst = tensors.get_tensor(TensorType::ACL_DST);
220 auto dx = tensors.get_tensor(TensorType::ACL_INT_0);
221 auto dy = tensors.get_tensor(TensorType::ACL_INT_1);
222 auto offsets = tensors.get_tensor(TensorType::ACL_INT_2);
223
224 // Get data layout and width/height indices
Georgios Pinitas2eb5d162021-07-02 09:01:49 +0100225 const int idx_width = get_data_layout_dimension_index(_data_layout, DataLayoutDimension::WIDTH);
226 const int idx_height = get_data_layout_dimension_index(_data_layout, DataLayoutDimension::HEIGHT);
Manuel Bottini10b38262021-02-19 18:16:44 +0000227
228 // Compute the ratio between source width/height and destination width/height
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100229 const bool is_align_corners_used =
230 _scale_info.align_corners &&
231 arm_compute::scale_utils::is_align_corners_allowed_sampling_policy(_scale_info.sampling_policy);
232 const auto wr = arm_compute::scale_utils::calculate_resize_ratio(
233 src->info()->dimension(idx_width), dst->info()->dimension(idx_width), is_align_corners_used);
234 const auto hr = arm_compute::scale_utils::calculate_resize_ratio(
235 src->info()->dimension(idx_height), dst->info()->dimension(idx_height), is_align_corners_used);
Manuel Bottini10b38262021-02-19 18:16:44 +0000236
237 // Area interpolation behaves as Nearest Neighbour in case of up-sampling
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100238 InterpolationPolicy policy_to_use =
239 (_scale_info.interpolation_policy == InterpolationPolicy::AREA && wr <= 1.f && hr <= 1.f)
240 ? InterpolationPolicy::NEAREST_NEIGHBOR
241 : _scale_info.interpolation_policy;
Manuel Bottini10b38262021-02-19 18:16:44 +0000242 const SamplingPolicy sampling_policy = _scale_info.sampling_policy;
243
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100244 bool precompute_indices_weights = arm_compute::scale_utils::is_precomputation_required(
245 _data_layout, src->info()->data_type(), policy_to_use, _scale_info.border_mode);
Gunes Bayir0eed3052022-09-04 21:00:10 +0100246
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100247 if (precompute_indices_weights)
Manuel Bottini10b38262021-02-19 18:16:44 +0000248 {
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100249 switch (policy_to_use)
Manuel Bottini10b38262021-02-19 18:16:44 +0000250 {
Gunes Bayir0eed3052022-09-04 21:00:10 +0100251 case InterpolationPolicy::NEAREST_NEIGHBOR:
252 {
253 // Pre-compute offsets for nearest interpolation
254 precompute_dx_dy_offsets(nullptr, nullptr, offsets, wr, hr, sampling_policy, is_align_corners_used);
255 break;
256 }
257 case InterpolationPolicy::BILINEAR:
258 {
259 // Pre-compute dx, dy and offsets for bilinear interpolation
260 precompute_dx_dy_offsets(dx, dy, offsets, wr, hr, sampling_policy, is_align_corners_used);
261 break;
262 }
263 case InterpolationPolicy::AREA:
264 {
265 break;
266 }
267 default:
268 ARM_COMPUTE_ERROR("Unsupported interpolation mode");
Manuel Bottini10b38262021-02-19 18:16:44 +0000269 }
Gunes Bayir0eed3052022-09-04 21:00:10 +0100270 }
271 else
272 {
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100273 if (policy_to_use != InterpolationPolicy::NEAREST_NEIGHBOR &&
274 policy_to_use != InterpolationPolicy::BILINEAR && policy_to_use != InterpolationPolicy::AREA)
Manuel Bottini10b38262021-02-19 18:16:44 +0000275 {
Manuel Bottini10b38262021-02-19 18:16:44 +0000276 ARM_COMPUTE_ERROR("Unsupported interpolation mode");
Gunes Bayir0eed3052022-09-04 21:00:10 +0100277 }
Manuel Bottini10b38262021-02-19 18:16:44 +0000278 }
279 }
280}
281
282void CpuScale::run(ITensorPack &tensors)
283{
284 ARM_COMPUTE_ERROR_ON_MSG(tensors.empty(), "No inputs provided");
285 prepare(tensors);
286 NEScheduler::get().schedule_op(_kernel.get(), Window::DimY, _kernel->window(), tensors);
287}
288} // namespace cpu
289} // namespace arm_compute