blob: 48ac38b170ed1aa6bb41350a89e5ab48fd0ba9ec [file] [log] [blame]
Anthony Barbier6ff3b192017-09-04 18:44:23 +01001/*
Michele Di Giorgiod9eaf612020-07-08 11:12:57 +01002 * Copyright (c) 2016-2020 Arm Limited.
Anthony Barbier6ff3b192017-09-04 18:44:23 +01003 *
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 */
Michalis Spyrouf4643372019-11-29 16:17:13 +000024#ifndef ARM_COMPUTE_HELPERS_H
25#define ARM_COMPUTE_HELPERS_H
Anthony Barbier6ff3b192017-09-04 18:44:23 +010026
Anthony Barbier6ff3b192017-09-04 18:44:23 +010027#include "arm_compute/core/Coordinates.h"
Georgios Pinitas583137c2017-08-31 18:12:42 +010028#include "arm_compute/core/Error.h"
Anthony Barbier6ff3b192017-09-04 18:44:23 +010029#include "arm_compute/core/IAccessWindow.h"
30#include "arm_compute/core/Steps.h"
31#include "arm_compute/core/Strides.h"
32#include "arm_compute/core/TensorShape.h"
33#include "arm_compute/core/Types.h"
34#include "arm_compute/core/Window.h"
Matthew Bentham758b5ba2020-03-05 23:37:48 +000035#include "support/MemorySupport.h"
Georgios Pinitas583137c2017-08-31 18:12:42 +010036
Anthony Barbier6ff3b192017-09-04 18:44:23 +010037#include <array>
38#include <cstddef>
39#include <cstdint>
40#include <memory>
41#include <tuple>
42#include <type_traits>
43#include <utility>
44
45namespace arm_compute
46{
47class IKernel;
48class ITensor;
49class ITensorInfo;
50
Alex Gildayc357c472018-03-21 13:54:09 +000051/** Disable bitwise operations by default */
Anthony Barbier6ff3b192017-09-04 18:44:23 +010052template <typename T>
53struct enable_bitwise_ops
54{
Alex Gildayc357c472018-03-21 13:54:09 +000055 static constexpr bool value = false; /**< Disabled */
Anthony Barbier6ff3b192017-09-04 18:44:23 +010056};
57
Alex Gildayc357c472018-03-21 13:54:09 +000058#ifndef DOXYGEN_SKIP_THIS
Anthony Barbier6ff3b192017-09-04 18:44:23 +010059template <typename T>
60typename std::enable_if<enable_bitwise_ops<T>::value, T>::type operator&(T lhs, T rhs)
61{
62 using underlying_type = typename std::underlying_type<T>::type;
63 return static_cast<T>(static_cast<underlying_type>(lhs) & static_cast<underlying_type>(rhs));
64}
Alex Gildayc357c472018-03-21 13:54:09 +000065#endif /* DOXYGEN_SKIP_THIS */
Anthony Barbier6ff3b192017-09-04 18:44:23 +010066
Michele Di Giorgiob8fc60f2018-04-25 11:58:07 +010067/** Helper function to create and return a unique_ptr pointed to a CL/GLES kernel object
68 * It also calls the kernel's configuration.
69 *
70 * @param[in] args All the arguments that need pass to kernel's configuration.
71 *
72 * @return A unique pointer pointed to a CL/GLES kernel object
73 */
74template <typename Kernel, typename... T>
75std::unique_ptr<Kernel> create_configure_kernel(T &&... args)
76{
77 std::unique_ptr<Kernel> k = arm_compute::support::cpp14::make_unique<Kernel>();
78 k->configure(std::forward<T>(args)...);
79 return k;
80}
81
82/** Helper function to create and return a unique_ptr pointed to a CL/GLES kernel object
83 *
84 * @return A unique pointer pointed to a Kernel kernel object
85 */
86template <typename Kernel>
87std::unique_ptr<Kernel> create_kernel()
88{
89 std::unique_ptr<Kernel> k = arm_compute::support::cpp14::make_unique<Kernel>();
90 return k;
91}
92
Anthony Barbier6ff3b192017-09-04 18:44:23 +010093namespace traits
94{
95/** Check if a type T is contained in a tuple Tuple of types */
96template <typename T, typename Tuple>
97struct is_contained;
98
99template <typename T>
100struct is_contained<T, std::tuple<>> : std::false_type
101{
102};
103
104template <typename T, typename... Ts>
105struct is_contained<T, std::tuple<T, Ts...>> : std::true_type
106{
107};
108
109template <typename T, typename U, typename... Ts>
110struct is_contained<T, std::tuple<U, Ts...>> : is_contained<T, std::tuple<Ts...>>
111{
112};
113}
114
115/** Computes bilinear interpolation using the pointer to the top-left pixel and the pixel's distance between
Georgios Pinitas583137c2017-08-31 18:12:42 +0100116 * the real coordinates and the smallest following integer coordinates. Input must be in single channel format.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100117 *
Georgios Pinitas583137c2017-08-31 18:12:42 +0100118 * @param[in] pixel_ptr Pointer to the top-left pixel value of a single channel input.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100119 * @param[in] stride Stride to access the bottom-left and bottom-right pixel values
120 * @param[in] dx Pixel's distance between the X real coordinate and the smallest X following integer
121 * @param[in] dy Pixel's distance between the Y real coordinate and the smallest Y following integer
122 *
123 * @note dx and dy must be in the range [0, 1.0]
124 *
125 * @return The bilinear interpolated pixel value
126 */
Georgios Pinitas583137c2017-08-31 18:12:42 +0100127template <typename T>
128inline T delta_bilinear_c1(const T *pixel_ptr, size_t stride, float dx, float dy)
129{
130 ARM_COMPUTE_ERROR_ON(pixel_ptr == nullptr);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100131
Georgios Pinitas583137c2017-08-31 18:12:42 +0100132 const float dx1 = 1.0f - dx;
133 const float dy1 = 1.0f - dy;
134
135 const T a00 = *pixel_ptr;
136 const T a01 = *(pixel_ptr + 1);
137 const T a10 = *(pixel_ptr + stride);
138 const T a11 = *(pixel_ptr + stride + 1);
139
140 const float w1 = dx1 * dy1;
141 const float w2 = dx * dy1;
142 const float w3 = dx1 * dy;
143 const float w4 = dx * dy;
144
145 return static_cast<T>(a00 * w1 + a01 * w2 + a10 * w3 + a11 * w4);
146}
147
Vidhya Sudhan Loganathan3ac2f3a2019-01-17 15:16:19 +0000148/** Computes bilinear interpolation for quantized input and output, using the pointer to the top-left pixel and the pixel's distance between
Michele Di Giorgio6f7585b2020-05-04 16:44:28 +0100149 * the real coordinates and the smallest following integer coordinates. Input must be QASYMM8 and in single channel format.
Vidhya Sudhan Loganathan3ac2f3a2019-01-17 15:16:19 +0000150 *
151 * @param[in] pixel_ptr Pointer to the top-left pixel value of a single channel input.
152 * @param[in] stride Stride to access the bottom-left and bottom-right pixel values
153 * @param[in] dx Pixel's distance between the X real coordinate and the smallest X following integer
154 * @param[in] dy Pixel's distance between the Y real coordinate and the smallest Y following integer
155 * @param[in] iq_info Input QuantizationInfo
156 * @param[in] oq_info Output QuantizationInfo
157 *
158 * @note dx and dy must be in the range [0, 1.0]
159 *
160 * @return The bilinear interpolated pixel value
161 */
Georgios Pinitas4c5469b2019-05-21 13:32:43 +0100162inline uint8_t delta_bilinear_c1_quantized(const uint8_t *pixel_ptr, size_t stride, float dx, float dy, UniformQuantizationInfo iq_info, UniformQuantizationInfo oq_info)
Vidhya Sudhan Loganathan3ac2f3a2019-01-17 15:16:19 +0000163{
164 ARM_COMPUTE_ERROR_ON(pixel_ptr == nullptr);
165
166 const float dx1 = 1.0f - dx;
167 const float dy1 = 1.0f - dy;
168
Georgios Pinitas4c5469b2019-05-21 13:32:43 +0100169 const float a00 = dequantize_qasymm8(*pixel_ptr, iq_info);
170 const float a01 = dequantize_qasymm8(*(pixel_ptr + 1), iq_info);
171 const float a10 = dequantize_qasymm8(*(pixel_ptr + stride), iq_info);
172 const float a11 = dequantize_qasymm8(*(pixel_ptr + stride + 1), iq_info);
Vidhya Sudhan Loganathan3ac2f3a2019-01-17 15:16:19 +0000173
174 const float w1 = dx1 * dy1;
175 const float w2 = dx * dy1;
176 const float w3 = dx1 * dy;
177 const float w4 = dx * dy;
178 float res = a00 * w1 + a01 * w2 + a10 * w3 + a11 * w4;
Georgios Pinitas4c5469b2019-05-21 13:32:43 +0100179 return static_cast<uint8_t>(quantize_qasymm8(res, oq_info));
Vidhya Sudhan Loganathan3ac2f3a2019-01-17 15:16:19 +0000180}
181
Michele Di Giorgio6f7585b2020-05-04 16:44:28 +0100182/** Computes bilinear interpolation for quantized input and output, using the pointer to the top-left pixel and the pixel's distance between
183 * the real coordinates and the smallest following integer coordinates. Input must be QASYMM8_SIGNED and in single channel format.
184 *
185 * @param[in] pixel_ptr Pointer to the top-left pixel value of a single channel input.
186 * @param[in] stride Stride to access the bottom-left and bottom-right pixel values
187 * @param[in] dx Pixel's distance between the X real coordinate and the smallest X following integer
188 * @param[in] dy Pixel's distance between the Y real coordinate and the smallest Y following integer
189 * @param[in] iq_info Input QuantizationInfo
190 * @param[in] oq_info Output QuantizationInfo
191 *
192 * @note dx and dy must be in the range [0, 1.0]
193 *
194 * @return The bilinear interpolated pixel value
195 */
196inline int8_t delta_bilinear_c1_quantized(const int8_t *pixel_ptr, size_t stride, float dx, float dy, UniformQuantizationInfo iq_info, UniformQuantizationInfo oq_info)
197{
198 ARM_COMPUTE_ERROR_ON(pixel_ptr == nullptr);
199
200 const float dx1 = 1.0f - dx;
201 const float dy1 = 1.0f - dy;
202
203 const float a00 = dequantize_qasymm8_signed(*pixel_ptr, iq_info);
204 const float a01 = dequantize_qasymm8_signed(*(pixel_ptr + 1), iq_info);
205 const float a10 = dequantize_qasymm8_signed(*(pixel_ptr + stride), iq_info);
206 const float a11 = dequantize_qasymm8_signed(*(pixel_ptr + stride + 1), iq_info);
207
208 const float w1 = dx1 * dy1;
209 const float w2 = dx * dy1;
210 const float w3 = dx1 * dy;
211 const float w4 = dx * dy;
212 float res = a00 * w1 + a01 * w2 + a10 * w3 + a11 * w4;
213 return static_cast<int8_t>(quantize_qasymm8_signed(res, oq_info));
214}
215
Anthony Barbier9a33b542017-12-12 22:08:59 +0000216/** Computes linear interpolation using the pointer to the top pixel and the pixel's distance between
217 * the real coordinates and the smallest following integer coordinates. Input must be in single channel format.
218 *
219 * @param[in] pixel_ptr Pointer to the top pixel value of a single channel input.
220 * @param[in] stride Stride to access the bottom pixel value
221 * @param[in] dy Pixel's distance between the Y real coordinate and the smallest Y following integer
222 *
223 * @note dy must be in the range [0, 1.0]
224 *
225 * @return The linear interpolated pixel value
226 */
227template <typename T>
228inline T delta_linear_c1_y(const T *pixel_ptr, size_t stride, float dy)
229{
230 ARM_COMPUTE_ERROR_ON(pixel_ptr == nullptr);
231
232 const float dy1 = 1.0f - dy;
233
234 const T a00 = *pixel_ptr;
235 const T a10 = *(pixel_ptr + stride);
236
237 const float w1 = dy1;
238 const float w3 = dy;
239
240 return static_cast<T>(a00 * w1 + a10 * w3);
241}
242/** Computes linear interpolation using the pointer to the left pixel and the pixel's distance between
243 * the real coordinates and the smallest following integer coordinates. Input must be in single channel format.
244 *
245 * @param[in] pixel_ptr Pointer to the left pixel value of a single channel input.
246 * @param[in] dx Pixel's distance between the X real coordinate and the smallest X following integer
247 *
248 * @note dx must be in the range [0, 1.0]
249 *
250 * @return The linear interpolated pixel value
251 */
252template <typename T>
253inline T delta_linear_c1_x(const T *pixel_ptr, float dx)
254{
255 ARM_COMPUTE_ERROR_ON(pixel_ptr == nullptr);
256
257 const T a00 = *pixel_ptr;
258 const T a01 = *(pixel_ptr + 1);
259
260 const float dx1 = 1.0f - dx;
261
262 const float w1 = dx1;
263 const float w2 = dx;
264
265 return static_cast<T>(a00 * w1 + a01 * w2);
266}
Georgios Pinitas583137c2017-08-31 18:12:42 +0100267/** Return the pixel at (x,y) using bilinear interpolation.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100268 *
269 * @warning Only works if the iterator was created with an IImage
270 *
Georgios Pinitas583137c2017-08-31 18:12:42 +0100271 * @param[in] first_pixel_ptr Pointer to the first pixel of a single channel input.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100272 * @param[in] stride Stride in bytes of the image;
273 * @param[in] x X position of the wanted pixel
274 * @param[in] y Y position of the wanted pixel
275 *
276 * @return The pixel at (x, y) using bilinear interpolation.
277 */
Georgios Pinitas583137c2017-08-31 18:12:42 +0100278template <typename T>
279inline T pixel_bilinear_c1(const T *first_pixel_ptr, size_t stride, float x, float y)
280{
281 ARM_COMPUTE_ERROR_ON(first_pixel_ptr == nullptr);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100282
Georgios Pinitas583137c2017-08-31 18:12:42 +0100283 const int32_t xi = std::floor(x);
284 const int32_t yi = std::floor(y);
285
286 const float dx = x - xi;
287 const float dy = y - yi;
288
289 return delta_bilinear_c1(first_pixel_ptr + xi + yi * stride, stride, dx, dy);
290}
291
292/** Return the pixel at (x,y) using bilinear interpolation by clamping when out of borders. The image must be single channel input
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100293 *
294 * @warning Only works if the iterator was created with an IImage
295 *
Georgios Pinitas583137c2017-08-31 18:12:42 +0100296 * @param[in] first_pixel_ptr Pointer to the first pixel of a single channel image.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100297 * @param[in] stride Stride in bytes of the image
298 * @param[in] width Width of the image
299 * @param[in] height Height of the image
300 * @param[in] x X position of the wanted pixel
301 * @param[in] y Y position of the wanted pixel
302 *
303 * @return The pixel at (x, y) using bilinear interpolation.
304 */
Georgios Pinitas583137c2017-08-31 18:12:42 +0100305template <typename T>
306inline uint8_t pixel_bilinear_c1_clamp(const T *first_pixel_ptr, size_t stride, size_t width, size_t height, float x, float y)
307{
308 ARM_COMPUTE_ERROR_ON(first_pixel_ptr == nullptr);
309
310 x = std::max(-1.f, std::min(x, static_cast<float>(width)));
311 y = std::max(-1.f, std::min(y, static_cast<float>(height)));
312
313 const float xi = std::floor(x);
314 const float yi = std::floor(y);
315
316 const float dx = x - xi;
317 const float dy = y - yi;
318
Anthony Barbier9a33b542017-12-12 22:08:59 +0000319 if(dx == 0.0f)
320 {
321 if(dy == 0.0f)
322 {
323 return static_cast<T>(first_pixel_ptr[static_cast<int32_t>(xi) + static_cast<int32_t>(yi) * stride]);
324 }
325 return delta_linear_c1_y(first_pixel_ptr + static_cast<int32_t>(xi) + static_cast<int32_t>(yi) * stride, stride, dy);
326 }
327 if(dy == 0.0f)
328 {
329 return delta_linear_c1_x(first_pixel_ptr + static_cast<int32_t>(xi) + static_cast<int32_t>(yi) * stride, dx);
330 }
Georgios Pinitas583137c2017-08-31 18:12:42 +0100331 return delta_bilinear_c1(first_pixel_ptr + static_cast<int32_t>(xi) + static_cast<int32_t>(yi) * stride, stride, dx, dy);
332}
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100333
334/** Return the pixel at (x,y) using area interpolation by clamping when out of borders. The image must be single channel U8
335 *
336 * @note The interpolation area depends on the width and height ration of the input and output images
337 * @note Currently average of the contributing pixels is calculated
338 *
339 * @param[in] first_pixel_ptr Pointer to the first pixel of a single channel U8 image.
340 * @param[in] stride Stride in bytes of the image
341 * @param[in] width Width of the image
342 * @param[in] height Height of the image
343 * @param[in] wr Width ratio among the input image width and output image width.
344 * @param[in] hr Height ratio among the input image height and output image height.
345 * @param[in] x X position of the wanted pixel
346 * @param[in] y Y position of the wanted pixel
347 *
348 * @return The pixel at (x, y) using area interpolation.
349 */
350inline uint8_t pixel_area_c1u8_clamp(const uint8_t *first_pixel_ptr, size_t stride, size_t width, size_t height, float wr, float hr, int x, int y);
351
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100352/** Iterator updated by @ref execute_window_loop for each window element */
353class Iterator
354{
355public:
356 /** Default constructor to create an empty iterator */
357 constexpr Iterator();
358 /** Create a container iterator for the metadata and allocation contained in the ITensor
359 *
360 * @param[in] tensor The tensor to associate to the iterator.
361 * @param[in] window The window which will be used to iterate over the tensor.
362 */
363 Iterator(const ITensor *tensor, const Window &window);
364
365 /** Increment the iterator along the specified dimension of the step value associated to the dimension.
366 *
367 * @warning It is the caller's responsibility to call increment(dimension+1) when reaching the end of a dimension, the iterator will not check for overflow.
368 *
369 * @note When incrementing a dimension 'n' the coordinates of all the dimensions in the range (0,n-1) are reset. For example if you iterate over a 2D image, everytime you change row (dimension 1), the iterator for the width (dimension 0) is reset to its start.
370 *
371 * @param[in] dimension Dimension to increment
372 */
373 void increment(size_t dimension);
374
375 /** Return the offset in bytes from the first element to the current position of the iterator
376 *
377 * @return The current position of the iterator in bytes relative to the first element.
378 */
Sheri Zhanga3e6b6d2020-08-18 10:07:35 +0100379 constexpr size_t offset() const;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100380
381 /** Return a pointer to the current pixel.
382 *
383 * @warning Only works if the iterator was created with an ITensor.
384 *
385 * @return equivalent to buffer() + offset()
386 */
387 constexpr uint8_t *ptr() const;
388
389 /** Move the iterator back to the beginning of the specified dimension.
390 *
391 * @param[in] dimension Dimension to reset
392 */
393 void reset(size_t dimension);
394
395private:
396 uint8_t *_ptr;
397
398 class Dimension
399 {
400 public:
401 constexpr Dimension()
402 : _dim_start(0), _stride(0)
403 {
404 }
405
Sheri Zhanga3e6b6d2020-08-18 10:07:35 +0100406 size_t _dim_start;
407 size_t _stride;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100408 };
409
410 std::array<Dimension, Coordinates::num_max_dimensions> _dims;
411};
412
413/** Iterate through the passed window, automatically adjusting the iterators and calling the lambda_functino for each element.
414 * It passes the x and y positions to the lambda_function for each iteration
415 *
416 * @param[in] w Window to iterate through.
417 * @param[in] lambda_function The function of type void(function)( const Coordinates & id ) to call at each iteration.
418 * Where id represents the absolute coordinates of the item to process.
419 * @param[in,out] iterators Tensor iterators which will be updated by this function before calling lambda_function.
420 */
421template <typename L, typename... Ts>
422inline void execute_window_loop(const Window &w, L &&lambda_function, Ts &&... iterators);
423
424/** Update window and padding size for each of the access patterns.
425 *
426 * First the window size is reduced based on all access patterns that are not
427 * allowed to modify the padding of the underlying tensor. Then the padding of
428 * the remaining tensors is increased to match the window.
429 *
430 * @param[in] win Window that is used by the kernel.
431 * @param[in] patterns Access patterns used to calculate the final window and padding.
432 *
433 * @return True if the window has been changed. Changes to the padding do not
434 * influence the returned value.
435 */
436template <typename... Ts>
437bool update_window_and_padding(Window &win, Ts &&... patterns)
438{
439 bool window_changed = false;
440
Diego Lopez Recas490b3d82017-12-19 15:42:25 +0000441 utility::for_each([&](const IAccessWindow & w)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100442 {
443 window_changed |= w.update_window_if_needed(win);
444 },
445 patterns...);
446
447 bool padding_changed = false;
448
Diego Lopez Recas35ceeb22017-12-04 18:56:10 +0000449 utility::for_each([&](IAccessWindow & w)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100450 {
451 padding_changed |= w.update_padding_if_needed(win);
452 },
453 patterns...);
454
455 return window_changed;
456}
457
458/** Calculate the maximum window for a given tensor shape and border setting
459 *
Diego Lopez Recasbcbc9702017-12-18 11:28:27 +0000460 * @param[in] valid_region Valid region object defining the shape of the tensor space for which the window is created.
461 * @param[in] steps (Optional) Number of elements processed for each step.
462 * @param[in] skip_border (Optional) If true exclude the border region from the window.
463 * @param[in] border_size (Optional) Border size.
464 *
465 * @return The maximum window the kernel can be executed on.
466 */
467Window calculate_max_window(const ValidRegion &valid_region, const Steps &steps = Steps(), bool skip_border = false, BorderSize border_size = BorderSize());
468
469/** Calculate the maximum window for a given tensor shape and border setting
470 *
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100471 * @param[in] info Tensor info object defining the shape of the object for which the window is created.
472 * @param[in] steps (Optional) Number of elements processed for each step.
473 * @param[in] skip_border (Optional) If true exclude the border region from the window.
474 * @param[in] border_size (Optional) Border size.
475 *
476 * @return The maximum window the kernel can be executed on.
477 */
Diego Lopez Recasbcbc9702017-12-18 11:28:27 +0000478inline Window calculate_max_window(const ITensorInfo &info, const Steps &steps = Steps(), bool skip_border = false, BorderSize border_size = BorderSize())
479{
480 return calculate_max_window(info.valid_region(), steps, skip_border, border_size);
481}
482
483/** Calculate the maximum window used by a horizontal kernel for a given tensor shape and border setting
484 *
485 * @param[in] valid_region Valid region object defining the shape of the tensor space for which the window is created.
486 * @param[in] steps (Optional) Number of elements processed for each step.
487 * @param[in] skip_border (Optional) If true exclude the border region from the window.
488 * @param[in] border_size (Optional) Border size. The border region will be excluded from the window.
489 *
490 * @return The maximum window the kernel can be executed on.
491 */
492Window calculate_max_window_horizontal(const ValidRegion &valid_region, const Steps &steps = Steps(), bool skip_border = false, BorderSize border_size = BorderSize());
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100493
494/** Calculate the maximum window used by a horizontal kernel for a given tensor shape and border setting
495 *
496 * @param[in] info Tensor info object defining the shape of the object for which the window is created.
497 * @param[in] steps (Optional) Number of elements processed for each step.
498 * @param[in] skip_border (Optional) If true exclude the border region from the window.
Diego Lopez Recasbcbc9702017-12-18 11:28:27 +0000499 * @param[in] border_size (Optional) Border size.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100500 *
501 * @return The maximum window the kernel can be executed on.
502 */
Diego Lopez Recasbcbc9702017-12-18 11:28:27 +0000503inline Window calculate_max_window_horizontal(const ITensorInfo &info, const Steps &steps = Steps(), bool skip_border = false, BorderSize border_size = BorderSize())
504{
505 return calculate_max_window_horizontal(info.valid_region(), steps, skip_border, border_size);
506}
507
508/** Calculate the maximum window for a given tensor shape and border setting. The window will also includes the border.
509 *
510 * @param[in] valid_region Valid region object defining the shape of the tensor space for which the window is created.
511 * @param[in] steps (Optional) Number of elements processed for each step.
512 * @param[in] border_size (Optional) Border size. The border region will be included in the window.
513 *
514 * @return The maximum window the kernel can be executed on.
515 */
516Window calculate_max_enlarged_window(const ValidRegion &valid_region, const Steps &steps = Steps(), BorderSize border_size = BorderSize());
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100517
518/** Calculate the maximum window for a given tensor shape and border setting. The window will also includes the border.
519 *
520 * @param[in] info Tensor info object defining the shape of the object for which the window is created.
521 * @param[in] steps (Optional) Number of elements processed for each step.
522 * @param[in] border_size (Optional) Border size. The border region will be included in the window.
523 *
524 * @return The maximum window the kernel can be executed on.
525 */
Diego Lopez Recasbcbc9702017-12-18 11:28:27 +0000526inline Window calculate_max_enlarged_window(const ITensorInfo &info, const Steps &steps = Steps(), BorderSize border_size = BorderSize())
527{
528 return calculate_max_enlarged_window(info.valid_region(), steps, border_size);
529}
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100530
531/** Intersect multiple valid regions.
532 *
533 * @param[in] regions Valid regions.
534 *
535 * @return Intersection of all regions.
536 */
537template <typename... Ts>
Diego Lopez Recas490b3d82017-12-19 15:42:25 +0000538ValidRegion intersect_valid_regions(const Ts &... regions)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100539{
540 auto intersect = [](const ValidRegion & r1, const ValidRegion & r2) -> ValidRegion
541 {
542 ValidRegion region;
543
544 for(size_t d = 0; d < std::min(r1.anchor.num_dimensions(), r2.anchor.num_dimensions()); ++d)
545 {
546 region.anchor.set(d, std::max(r1.anchor[d], r2.anchor[d]));
547 }
548
549 for(size_t d = 0; d < std::min(r1.shape.num_dimensions(), r2.shape.num_dimensions()); ++d)
550 {
551 region.shape.set(d, std::min(r1.shape[d], r2.shape[d]));
552 }
553
554 return region;
555 };
556
Diego Lopez Recas490b3d82017-12-19 15:42:25 +0000557 return utility::foldl(intersect, regions...);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100558}
559
560/** Create a strides object based on the provided strides and the tensor dimensions.
561 *
562 * @param[in] info Tensor info object providing the shape of the tensor for unspecified strides.
563 * @param[in] stride_x Stride to be used in X dimension (in bytes).
564 * @param[in] fixed_strides Strides to be used in higher dimensions starting at Y (in bytes).
565 *
566 * @return Strides object based on the specified strides. Missing strides are
567 * calculated based on the tensor shape and the strides of lower dimensions.
568 */
569template <typename T, typename... Ts>
570inline Strides compute_strides(const ITensorInfo &info, T stride_x, Ts &&... fixed_strides)
571{
572 const TensorShape &shape = info.tensor_shape();
573
574 // Create strides object
575 Strides strides(stride_x, fixed_strides...);
576
577 for(size_t i = 1 + sizeof...(Ts); i < info.num_dimensions(); ++i)
578 {
579 strides.set(i, shape[i - 1] * strides[i - 1]);
580 }
581
582 return strides;
583}
584
585/** Create a strides object based on the tensor dimensions.
586 *
587 * @param[in] info Tensor info object used to compute the strides.
588 *
589 * @return Strides object based on element size and tensor shape.
590 */
591template <typename... Ts>
592inline Strides compute_strides(const ITensorInfo &info)
593{
594 return compute_strides(info, info.element_size());
595}
596
Georgios Pinitas8795ffb2017-12-01 16:13:40 +0000597/** Permutes given Dimensions according to a permutation vector
598 *
599 * @warning Validity of permutation is not checked
600 *
601 * @param[in, out] dimensions Dimensions to permute
602 * @param[in] perm Permutation vector
603 */
604template <typename T>
605inline void permute(Dimensions<T> &dimensions, const PermutationVector &perm)
606{
Georgios Pinitas69af6cf2018-02-14 19:23:44 +0000607 auto dimensions_copy = utility::make_array<Dimensions<T>::num_max_dimensions>(dimensions.begin(), dimensions.end());
Georgios Pinitas8795ffb2017-12-01 16:13:40 +0000608 for(unsigned int i = 0; i < perm.num_dimensions(); ++i)
609 {
Georgios Pinitas69af6cf2018-02-14 19:23:44 +0000610 T dimension_val = (perm[i] < dimensions.num_dimensions()) ? dimensions_copy[perm[i]] : 0;
611 dimensions.set(i, dimension_val);
612 }
613}
614
615/** Permutes given TensorShape according to a permutation vector
616 *
617 * @warning Validity of permutation is not checked
618 *
619 * @param[in, out] shape Shape to permute
620 * @param[in] perm Permutation vector
621 */
622inline void permute(TensorShape &shape, const PermutationVector &perm)
623{
Giorgio Arena563494c2018-04-30 17:29:41 +0100624 TensorShape shape_copy = shape;
Georgios Pinitas69af6cf2018-02-14 19:23:44 +0000625 for(unsigned int i = 0; i < perm.num_dimensions(); ++i)
626 {
627 size_t dimension_val = (perm[i] < shape.num_dimensions()) ? shape_copy[perm[i]] : 1;
Giorgio Arena563494c2018-04-30 17:29:41 +0100628 shape.set(i, dimension_val, false); // Avoid changes in _num_dimension
Georgios Pinitas8795ffb2017-12-01 16:13:40 +0000629 }
630}
631
Vidhya Sudhan Loganathan7485d5a2018-07-04 09:34:00 +0100632/** Auto initialize the tensor info (shape, number of channels and data type) if the current assignment is empty.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100633 *
Vidhya Sudhan Loganathan7485d5a2018-07-04 09:34:00 +0100634 * @param[in,out] info Tensor info used to check and assign.
635 * @param[in] shape New shape.
636 * @param[in] num_channels New number of channels.
637 * @param[in] data_type New data type
638 * @param[in] quantization_info (Optional) New quantization info
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100639 *
640 * @return True if the tensor info has been initialized
641 */
Georgios Pinitas05078ec2017-11-02 13:06:59 +0000642bool auto_init_if_empty(ITensorInfo &info,
643 const TensorShape &shape,
644 int num_channels, DataType data_type,
Georgios Pinitas05078ec2017-11-02 13:06:59 +0000645 QuantizationInfo quantization_info = QuantizationInfo());
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100646
Georgios Pinitas283c1792017-11-10 18:14:06 +0000647/** Auto initialize the tensor info using another tensor info.
648 *
649 * @param info_sink Tensor info used to check and assign
650 * @param info_source Tensor info used to assign
651 *
652 * @return True if the tensor info has been initialized
653 */
Pablo Palmiera2b89ca2017-10-05 15:01:34 +0100654bool auto_init_if_empty(ITensorInfo &info_sink, const ITensorInfo &info_source);
Georgios Pinitas283c1792017-11-10 18:14:06 +0000655
Alex Gildayc357c472018-03-21 13:54:09 +0000656/** Set the shape to the specified value if the current assignment is empty.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100657 *
658 * @param[in,out] info Tensor info used to check and assign.
659 * @param[in] shape New shape.
660 *
661 * @return True if the shape has been changed.
662 */
663bool set_shape_if_empty(ITensorInfo &info, const TensorShape &shape);
664
Alex Gildayc357c472018-03-21 13:54:09 +0000665/** Set the format, data type and number of channels to the specified value if
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100666 * the current data type is unknown.
667 *
668 * @param[in,out] info Tensor info used to check and assign.
669 * @param[in] format New format.
670 *
671 * @return True if the format has been changed.
672 */
673bool set_format_if_unknown(ITensorInfo &info, Format format);
674
Alex Gildayc357c472018-03-21 13:54:09 +0000675/** Set the data type and number of channels to the specified value if
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100676 * the current data type is unknown.
677 *
678 * @param[in,out] info Tensor info used to check and assign.
679 * @param[in] data_type New data type.
680 *
681 * @return True if the data type has been changed.
682 */
683bool set_data_type_if_unknown(ITensorInfo &info, DataType data_type);
684
Alex Gildayc357c472018-03-21 13:54:09 +0000685/** Set the data layout to the specified value if
Isabella Gottardid17a6772018-02-27 17:41:55 +0000686 * the current data layout is unknown.
687 *
688 * @param[in,out] info Tensor info used to check and assign.
689 * @param[in] data_layout New data layout.
690 *
691 * @return True if the data type has been changed.
692 */
693bool set_data_layout_if_unknown(ITensorInfo &info, DataLayout data_layout);
694
Alex Gildayc357c472018-03-21 13:54:09 +0000695/** Set the quantization info to the specified value if
Georgios Pinitas05078ec2017-11-02 13:06:59 +0000696 * the current quantization info is empty and the data type of asymmetric quantized type
697 *
698 * @param[in,out] info Tensor info used to check and assign.
699 * @param[in] quantization_info Quantization info
700 *
701 * @return True if the quantization info has been changed.
702 */
703bool set_quantization_info_if_empty(ITensorInfo &info, QuantizationInfo quantization_info);
704
Isabella Gottardi1fab09f2017-07-13 15:55:57 +0100705/** Helper function to calculate the Valid Region for Scale.
706 *
Diego Lopez Recas00854292018-02-22 13:08:01 +0000707 * @param[in] src_info Input tensor info used to check.
708 * @param[in] dst_shape Shape of the output.
709 * @param[in] interpolate_policy Interpolation policy.
710 * @param[in] sampling_policy Sampling policy.
711 * @param[in] border_undefined True if the border is undefined.
Isabella Gottardi1fab09f2017-07-13 15:55:57 +0100712 *
Diego Lopez Recas00854292018-02-22 13:08:01 +0000713 * @return The corresponding valid region
Isabella Gottardi1fab09f2017-07-13 15:55:57 +0100714 */
Diego Lopez Recas00854292018-02-22 13:08:01 +0000715ValidRegion calculate_valid_region_scale(const ITensorInfo &src_info, const TensorShape &dst_shape,
716 InterpolationPolicy interpolate_policy, SamplingPolicy sampling_policy, bool border_undefined);
Georgios Pinitas05078ec2017-11-02 13:06:59 +0000717
Georgios Pinitas5ee66ea2017-09-07 17:29:16 +0100718/** Convert a linear index into n-dimensional coordinates.
719 *
720 * @param[in] shape Shape of the n-dimensional tensor.
721 * @param[in] index Linear index specifying the i-th element.
722 *
723 * @return n-dimensional coordinates.
724 */
725inline Coordinates index2coords(const TensorShape &shape, int index);
Georgios Pinitas05078ec2017-11-02 13:06:59 +0000726
Georgios Pinitas5ee66ea2017-09-07 17:29:16 +0100727/** Convert n-dimensional coordinates into a linear index.
728 *
729 * @param[in] shape Shape of the n-dimensional tensor.
730 * @param[in] coord N-dimensional coordinates.
731 *
732 * @return linead index
733 */
734inline int coords2index(const TensorShape &shape, const Coordinates &coord);
Isabella Gottardid17a6772018-02-27 17:41:55 +0000735
Alex Gildayc357c472018-03-21 13:54:09 +0000736/** Get the index of the given dimension.
Isabella Gottardid17a6772018-02-27 17:41:55 +0000737 *
Alex Gildayc357c472018-03-21 13:54:09 +0000738 * @param[in] data_layout The data layout.
739 * @param[in] data_layout_dimension The dimension which this index is requested for.
Isabella Gottardid17a6772018-02-27 17:41:55 +0000740 *
741 * @return The int conversion of the requested data layout index.
742 */
Isabella Gottardid56e7702018-02-28 14:29:36 +0000743inline size_t get_data_layout_dimension_index(const DataLayout data_layout, const DataLayoutDimension data_layout_dimension);
Georgios Pinitase2220552018-07-20 13:23:44 +0100744
Usama Arif8cf8c112019-03-14 15:36:54 +0000745/** Get the DataLayoutDimension of a given index and layout.
746 *
747 * @param[in] data_layout The data layout.
748 * @param[in] index The data layout index.
749 *
750 * @return The dimension which this index is requested for.
751 */
752inline DataLayoutDimension get_index_data_layout_dimension(const DataLayout data_layout, const size_t index);
753
Georgios Pinitase2220552018-07-20 13:23:44 +0100754/** Calculate the normalization dimension index for a given normalization type
755 *
756 * @param[in] layout Data layout of the input and output tensor
757 * @param[in] info Normalization info
758 *
759 * @return Normalization dimension index
760 */
761inline unsigned int get_normalization_dimension_index(DataLayout layout, const NormalizationLayerInfo &info)
762{
763 const unsigned int width_idx = get_data_layout_dimension_index(layout, DataLayoutDimension::WIDTH);
764 const unsigned int channel_idx = get_data_layout_dimension_index(layout, DataLayoutDimension::CHANNEL);
765
766 return info.is_in_map() ? width_idx : channel_idx;
767}
768
769/** Calculate the number of output tiles required by Winograd Convolution layer. This utility function can be used by the Winograd input transform
770 * to know the number of tiles on the x and y direction
771 *
772 * @param[in] in_dims Spatial dimensions of the input tensor of convolution layer
773 * @param[in] kernel_size Kernel size
774 * @param[in] output_tile_size Size of a single output tile
775 * @param[in] conv_info Convolution info (i.e. pad, stride,...)
776 *
777 * @return the number of output tiles along the x and y directions of size "output_tile_size"
778 */
779inline Size2D compute_winograd_convolution_tiles(const Size2D &in_dims, const Size2D &kernel_size, const Size2D &output_tile_size, const PadStrideInfo &conv_info)
780{
781 int num_tiles_x = std::ceil((in_dims.width - (kernel_size.width - 1) + conv_info.pad_left() + conv_info.pad_right()) / static_cast<float>(output_tile_size.width));
782 int num_tiles_y = std::ceil((in_dims.height - (kernel_size.height - 1) + conv_info.pad_top() + conv_info.pad_bottom()) / static_cast<float>(output_tile_size.height));
783
784 // Clamp in case we provide paddings but we have 1D convolution
785 num_tiles_x = std::min(num_tiles_x, static_cast<int>(in_dims.width));
786 num_tiles_y = std::min(num_tiles_y, static_cast<int>(in_dims.height));
787
788 return Size2D(num_tiles_x, num_tiles_y);
789}
790
Gian Marco Iodice8aa985e2018-11-27 15:58:08 +0000791/** Wrap-around a number within the range 0 <= x < m
792 *
793 * @param[in] x Input value
794 * @param[in] m Range
795 *
796 * @return the wrapped-around number
797 */
798template <typename T>
799inline T wrap_around(T x, T m)
800{
801 return x >= 0 ? x % m : (x % m + m) % m;
802}
Gian Marco Iodiceb0c50372019-03-15 10:13:05 +0000803
SiCong Lid004a7a2020-05-28 15:26:41 +0100804/** Convert a dimension axis to the number of dimensions in the range [0, @p dim_axis]
805 * Handle negative axis, negative axis is used to specify axis from the end (e.g. -1 for the last axis).
806 *
807 * @param[in] dim_axis The last axis (inclusive) in the range [0, @p dim_axis]
808 * @param[in] num_dims The total number of dimensions
809 *
810 * @return The number of dimensions in the range [0, @p dim_axis]
811 */
SiCong Lie1536a82020-06-11 10:47:53 +0100812inline size_t dim_index_2_num_dims(int32_t dim_axis, int32_t num_dims);
SiCong Lid004a7a2020-05-28 15:26:41 +0100813
Pablo Tello93975152019-11-08 13:47:53 +0000814/** Convert negative coordinates to positive in the range [0, num_dims_input]
815 *
816 * @param[out] coords Array of coordinates to be converted.
817 * @param[in] max_value Maximum value to be used when wrapping the negative values in coords
818 */
819inline Coordinates &convert_negative_axis(Coordinates &coords, int max_value)
820{
821 for(unsigned int i = 0; i < coords.num_dimensions(); ++i)
822 {
823 coords[i] = wrap_around(coords[i], max_value);
824 }
825 return coords;
826}
827
Gian Marco Iodiceb0c50372019-03-15 10:13:05 +0000828/** Given an integer value, this function returns the next power of two
829 *
830 * @param[in] x Input value
831 *
832 * @return the next power of two
833 */
834inline unsigned int get_next_power_two(unsigned int x)
835{
836 // Decrement by 1
837 x--;
838
839 // Shift right by 1
840 x |= x >> 1u;
841 // Shift right by 2
842 x |= x >> 2u;
843 // Shift right by 4
844 x |= x >> 4u;
845 // Shift right by 8
846 x |= x >> 8u;
847 // Shift right by 16
848 x |= x >> 16u;
849
850 // Increment by 1
851 x++;
852
853 return x;
854}
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100855} // namespace arm_compute
856
857#include "arm_compute/core/Helpers.inl"
Michalis Spyrouf4643372019-11-29 16:17:13 +0000858#endif /*ARM_COMPUTE_HELPERS_H */