blob: a13a0f56a25dca9839582605bff86f301bdacdcd [file] [log] [blame]
Manuel Bottini10b38262021-02-19 18:16:44 +00001/*
Gunes Bayir0eed3052022-09-04 21:00:10 +01002 * Copyright (c) 2021-2022 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
Manuel Bottini10b38262021-02-19 18:16:44 +000026#include "arm_compute/runtime/NEON/NEScheduler.h"
ramelg013ae3d882021-09-12 23:07:47 +010027#include "src/common/utils/Log.h"
Manuel Bottini10b38262021-02-19 18:16:44 +000028#include "src/core/utils/ScaleUtils.h"
Georgios Pinitas7891a732021-08-20 21:39:25 +010029#include "src/cpu/kernels/CpuScaleKernel.h"
Manuel Bottini10b38262021-02-19 18:16:44 +000030#include "support/Rounding.h"
31
32namespace arm_compute
33{
34namespace cpu
35{
36namespace
37{
38void 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
Manuel Bottini10b38262021-02-19 18:16:44 +000087void CpuScale::configure(ITensorInfo *src, ITensorInfo *dst, const ScaleKernelInfo &info)
88{
89 ARM_COMPUTE_ERROR_ON_NULLPTR(src, dst);
90 ARM_COMPUTE_ERROR_THROW_ON(CpuScale::validate(src, dst, info));
ramelg013ae3d882021-09-12 23:07:47 +010091 ARM_COMPUTE_LOG_PARAMS(src, dst, info);
Manuel Bottini10b38262021-02-19 18:16:44 +000092
Georgios Pinitas2eb5d162021-07-02 09:01:49 +010093 _scale_info = info;
94 _is_prepared = false;
Manuel Bottini10b38262021-02-19 18:16:44 +000095
96 // Get data layout and width/height indices
Georgios Pinitas2eb5d162021-07-02 09:01:49 +010097 _data_layout = _scale_info.data_layout == DataLayout::UNKNOWN ? src->data_layout() : _scale_info.data_layout;
98 const int idx_width = get_data_layout_dimension_index(_data_layout, DataLayoutDimension::WIDTH);
99 const int idx_height = get_data_layout_dimension_index(_data_layout, DataLayoutDimension::HEIGHT);
Manuel Bottini10b38262021-02-19 18:16:44 +0000100
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) ?
109 InterpolationPolicy::NEAREST_NEIGHBOR :
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 {
125 case InterpolationPolicy::NEAREST_NEIGHBOR:
126 {
127 scale_kernel->configure(src, nullptr, nullptr, offsets.get(), dst, info);
128 break;
129 }
130 case InterpolationPolicy::BILINEAR:
131 {
132 scale_kernel->configure(src, dx.get(), dy.get(), offsets.get(), dst, info);
133 break;
134 }
135 case InterpolationPolicy::AREA:
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
146Status CpuScale::validate(const ITensorInfo *src, const ITensorInfo *dst, const ScaleKernelInfo &info)
147{
148 ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(src, dst);
149 ARM_COMPUTE_RETURN_ERROR_ON(info.sampling_policy != SamplingPolicy::CENTER && info.sampling_policy != SamplingPolicy::TOP_LEFT);
150
151 ITensorInfo *offsets = nullptr;
152 ITensorInfo *dx = nullptr;
153 ITensorInfo *dy = nullptr;
154
155 // Get data layout and width/height indices
156 const DataLayout data_layout = info.data_layout == DataLayout::UNKNOWN ? src->data_layout() : info.data_layout;
157 const int idx_width = get_data_layout_dimension_index(data_layout, DataLayoutDimension::WIDTH);
158 const int idx_height = get_data_layout_dimension_index(data_layout, DataLayoutDimension::HEIGHT);
159
160 // Compute the ratio between source width/height and destination width/height
161 const bool is_align_corners_used = info.align_corners && arm_compute::scale_utils::is_align_corners_allowed_sampling_policy(info.sampling_policy);
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
166 InterpolationPolicy policy_to_use = (info.interpolation_policy == InterpolationPolicy::AREA && wr <= 1.f && hr <= 1.f) ? InterpolationPolicy::NEAREST_NEIGHBOR : info.interpolation_policy;
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 {
175 case InterpolationPolicy::NEAREST_NEIGHBOR:
176 offsets = &tensor_info_offsets;
177 break;
178 case InterpolationPolicy::BILINEAR:
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
191void CpuScale::prepare(ITensorPack &tensors)
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
Georgios Pinitas2eb5d162021-07-02 09:01:49 +0100203 const int idx_width = get_data_layout_dimension_index(_data_layout, DataLayoutDimension::WIDTH);
204 const int idx_height = get_data_layout_dimension_index(_data_layout, DataLayoutDimension::HEIGHT);
Manuel Bottini10b38262021-02-19 18:16:44 +0000205
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) ?
214 InterpolationPolicy::NEAREST_NEIGHBOR :
215 _scale_info.interpolation_policy;
216 const SamplingPolicy sampling_policy = _scale_info.sampling_policy;
217
Gunes Bayirc4f27432022-09-11 15:59:19 +0100218 bool precompute_indices_weights = arm_compute::scale_utils::is_precomputation_required(_data_layout, src->info()->data_type(), policy_to_use, _scale_info.border_mode);
Gunes Bayir0eed3052022-09-04 21:00:10 +0100219
Gunes Bayirc4f27432022-09-11 15:59:19 +0100220 if(precompute_indices_weights)
Manuel Bottini10b38262021-02-19 18:16:44 +0000221 {
Gunes Bayir0eed3052022-09-04 21:00:10 +0100222 switch(policy_to_use)
Manuel Bottini10b38262021-02-19 18:16:44 +0000223 {
Gunes Bayir0eed3052022-09-04 21:00:10 +0100224 case InterpolationPolicy::NEAREST_NEIGHBOR:
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 }
230 case InterpolationPolicy::BILINEAR:
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 }
236 case InterpolationPolicy::AREA:
237 {
238 break;
239 }
240 default:
241 ARM_COMPUTE_ERROR("Unsupported interpolation mode");
Manuel Bottini10b38262021-02-19 18:16:44 +0000242 }
Gunes Bayir0eed3052022-09-04 21:00:10 +0100243 }
244 else
245 {
246 if(policy_to_use != InterpolationPolicy::NEAREST_NEIGHBOR && policy_to_use != InterpolationPolicy::BILINEAR && policy_to_use != InterpolationPolicy::AREA)
Manuel Bottini10b38262021-02-19 18:16:44 +0000247 {
Manuel Bottini10b38262021-02-19 18:16:44 +0000248 ARM_COMPUTE_ERROR("Unsupported interpolation mode");
Gunes Bayir0eed3052022-09-04 21:00:10 +0100249 }
Manuel Bottini10b38262021-02-19 18:16:44 +0000250 }
251 }
252}
253
254void CpuScale::run(ITensorPack &tensors)
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