blob: 13d1f6c99feb1c9b2f1d3d4073af486eec926cad [file] [log] [blame]
Anthony Barbier6ff3b192017-09-04 18:44:23 +01001/*
2 * Copyright (c) 2016, 2017 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 */
24#ifndef __ARM_COMPUTE_HELPERS_H__
25#define __ARM_COMPUTE_HELPERS_H__
26
27#include "arm_compute/core/CL/CLTypes.h"
28#include "arm_compute/core/Coordinates.h"
Georgios Pinitas583137c2017-08-31 18:12:42 +010029#include "arm_compute/core/Error.h"
Anthony Barbier6ff3b192017-09-04 18:44:23 +010030#include "arm_compute/core/IAccessWindow.h"
31#include "arm_compute/core/Steps.h"
32#include "arm_compute/core/Strides.h"
33#include "arm_compute/core/TensorShape.h"
34#include "arm_compute/core/Types.h"
35#include "arm_compute/core/Window.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
Anthony Barbier6ff3b192017-09-04 18:44:23 +010051template <typename T>
52struct enable_bitwise_ops
53{
54 static constexpr bool value = false;
55};
56
57template <typename T>
58typename std::enable_if<enable_bitwise_ops<T>::value, T>::type operator&(T lhs, T rhs)
59{
60 using underlying_type = typename std::underlying_type<T>::type;
61 return static_cast<T>(static_cast<underlying_type>(lhs) & static_cast<underlying_type>(rhs));
62}
63
64namespace traits
65{
66/** Check if a type T is contained in a tuple Tuple of types */
67template <typename T, typename Tuple>
68struct is_contained;
69
70template <typename T>
71struct is_contained<T, std::tuple<>> : std::false_type
72{
73};
74
75template <typename T, typename... Ts>
76struct is_contained<T, std::tuple<T, Ts...>> : std::true_type
77{
78};
79
80template <typename T, typename U, typename... Ts>
81struct is_contained<T, std::tuple<U, Ts...>> : is_contained<T, std::tuple<Ts...>>
82{
83};
84}
85
86/** Computes bilinear interpolation using the pointer to the top-left pixel and the pixel's distance between
Georgios Pinitas583137c2017-08-31 18:12:42 +010087 * the real coordinates and the smallest following integer coordinates. Input must be in single channel format.
Anthony Barbier6ff3b192017-09-04 18:44:23 +010088 *
Georgios Pinitas583137c2017-08-31 18:12:42 +010089 * @param[in] pixel_ptr Pointer to the top-left pixel value of a single channel input.
Anthony Barbier6ff3b192017-09-04 18:44:23 +010090 * @param[in] stride Stride to access the bottom-left and bottom-right pixel values
91 * @param[in] dx Pixel's distance between the X real coordinate and the smallest X following integer
92 * @param[in] dy Pixel's distance between the Y real coordinate and the smallest Y following integer
93 *
94 * @note dx and dy must be in the range [0, 1.0]
95 *
96 * @return The bilinear interpolated pixel value
97 */
Georgios Pinitas583137c2017-08-31 18:12:42 +010098template <typename T>
99inline T delta_bilinear_c1(const T *pixel_ptr, size_t stride, float dx, float dy)
100{
101 ARM_COMPUTE_ERROR_ON(pixel_ptr == nullptr);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100102
Georgios Pinitas583137c2017-08-31 18:12:42 +0100103 const float dx1 = 1.0f - dx;
104 const float dy1 = 1.0f - dy;
105
106 const T a00 = *pixel_ptr;
107 const T a01 = *(pixel_ptr + 1);
108 const T a10 = *(pixel_ptr + stride);
109 const T a11 = *(pixel_ptr + stride + 1);
110
111 const float w1 = dx1 * dy1;
112 const float w2 = dx * dy1;
113 const float w3 = dx1 * dy;
114 const float w4 = dx * dy;
115
116 return static_cast<T>(a00 * w1 + a01 * w2 + a10 * w3 + a11 * w4);
117}
118
119/** Return the pixel at (x,y) using bilinear interpolation.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100120 *
121 * @warning Only works if the iterator was created with an IImage
122 *
Georgios Pinitas583137c2017-08-31 18:12:42 +0100123 * @param[in] first_pixel_ptr Pointer to the first pixel of a single channel input.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100124 * @param[in] stride Stride in bytes of the image;
125 * @param[in] x X position of the wanted pixel
126 * @param[in] y Y position of the wanted pixel
127 *
128 * @return The pixel at (x, y) using bilinear interpolation.
129 */
Georgios Pinitas583137c2017-08-31 18:12:42 +0100130template <typename T>
131inline T pixel_bilinear_c1(const T *first_pixel_ptr, size_t stride, float x, float y)
132{
133 ARM_COMPUTE_ERROR_ON(first_pixel_ptr == nullptr);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100134
Georgios Pinitas583137c2017-08-31 18:12:42 +0100135 const int32_t xi = std::floor(x);
136 const int32_t yi = std::floor(y);
137
138 const float dx = x - xi;
139 const float dy = y - yi;
140
141 return delta_bilinear_c1(first_pixel_ptr + xi + yi * stride, stride, dx, dy);
142}
143
144/** 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 +0100145 *
146 * @warning Only works if the iterator was created with an IImage
147 *
Georgios Pinitas583137c2017-08-31 18:12:42 +0100148 * @param[in] first_pixel_ptr Pointer to the first pixel of a single channel image.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100149 * @param[in] stride Stride in bytes of the image
150 * @param[in] width Width of the image
151 * @param[in] height Height of the image
152 * @param[in] x X position of the wanted pixel
153 * @param[in] y Y position of the wanted pixel
154 *
155 * @return The pixel at (x, y) using bilinear interpolation.
156 */
Georgios Pinitas583137c2017-08-31 18:12:42 +0100157template <typename T>
158inline uint8_t pixel_bilinear_c1_clamp(const T *first_pixel_ptr, size_t stride, size_t width, size_t height, float x, float y)
159{
160 ARM_COMPUTE_ERROR_ON(first_pixel_ptr == nullptr);
161
162 x = std::max(-1.f, std::min(x, static_cast<float>(width)));
163 y = std::max(-1.f, std::min(y, static_cast<float>(height)));
164
165 const float xi = std::floor(x);
166 const float yi = std::floor(y);
167
168 const float dx = x - xi;
169 const float dy = y - yi;
170
171 return delta_bilinear_c1(first_pixel_ptr + static_cast<int32_t>(xi) + static_cast<int32_t>(yi) * stride, stride, dx, dy);
172}
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100173
174/** Return the pixel at (x,y) using area interpolation by clamping when out of borders. The image must be single channel U8
175 *
176 * @note The interpolation area depends on the width and height ration of the input and output images
177 * @note Currently average of the contributing pixels is calculated
178 *
179 * @param[in] first_pixel_ptr Pointer to the first pixel of a single channel U8 image.
180 * @param[in] stride Stride in bytes of the image
181 * @param[in] width Width of the image
182 * @param[in] height Height of the image
183 * @param[in] wr Width ratio among the input image width and output image width.
184 * @param[in] hr Height ratio among the input image height and output image height.
185 * @param[in] x X position of the wanted pixel
186 * @param[in] y Y position of the wanted pixel
187 *
188 * @return The pixel at (x, y) using area interpolation.
189 */
190inline 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);
191
192/** Performs clamping among a lower and upper value.
193 *
194 * @param[in] n Value to clamp.
195 * @param[in] lower Lower threshold.
196 * @param[in] upper Upper threshold.
197 *
198 * @return Clamped value.
199 */
200template <typename T>
201inline T clamp(const T &n, const T &lower, const T &upper)
202{
203 return std::max(lower, std::min(n, upper));
204}
205
206/** Base case of for_each. Does nothing. */
207template <typename F>
208inline void for_each(F &&)
209{
210}
211
212/** Call the function for each of the arguments
213 *
214 * @param[in] func Function to be called
215 * @param[in] arg Argument passed to the function
216 * @param[in] args Remaining arguments
217 */
218template <typename F, typename T, typename... Ts>
219inline void for_each(F &&func, T &&arg, Ts &&... args)
220{
221 func(arg);
222 for_each(func, args...);
223}
224
225/** Base case of foldl.
226 *
227 * @return value.
228 */
229template <typename F, typename T>
230inline T foldl(F &&, const T &value)
231{
232 return value;
233}
234
235/** Base case of foldl.
236 *
237 * @return Function evaluation for value1 and value2
238 */
239template <typename F, typename T, typename U>
240inline auto foldl(F &&func, T &&value1, U &&value2) -> decltype(func(value1, value2))
241{
242 return func(value1, value2);
243}
244
245/** Fold left.
246 *
247 * @param[in] func Function to be called
248 * @param[in] initial Initial value
249 * @param[in] value Argument passed to the function
250 * @param[in] values Remaining arguments
251 */
252template <typename F, typename I, typename T, typename... Vs>
253inline I foldl(F &&func, I &&initial, T &&value, Vs &&... values)
254{
255 return foldl(std::forward<F>(func), func(std::forward<I>(initial), std::forward<T>(value)), std::forward<Vs>(values)...);
256}
257
258/** Iterator updated by @ref execute_window_loop for each window element */
259class Iterator
260{
261public:
262 /** Default constructor to create an empty iterator */
263 constexpr Iterator();
264 /** Create a container iterator for the metadata and allocation contained in the ITensor
265 *
266 * @param[in] tensor The tensor to associate to the iterator.
267 * @param[in] window The window which will be used to iterate over the tensor.
268 */
269 Iterator(const ITensor *tensor, const Window &window);
270
271 /** Increment the iterator along the specified dimension of the step value associated to the dimension.
272 *
273 * @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.
274 *
275 * @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.
276 *
277 * @param[in] dimension Dimension to increment
278 */
279 void increment(size_t dimension);
280
281 /** Return the offset in bytes from the first element to the current position of the iterator
282 *
283 * @return The current position of the iterator in bytes relative to the first element.
284 */
285 constexpr int offset() const;
286
287 /** Return a pointer to the current pixel.
288 *
289 * @warning Only works if the iterator was created with an ITensor.
290 *
291 * @return equivalent to buffer() + offset()
292 */
293 constexpr uint8_t *ptr() const;
294
295 /** Move the iterator back to the beginning of the specified dimension.
296 *
297 * @param[in] dimension Dimension to reset
298 */
299 void reset(size_t dimension);
300
301private:
302 uint8_t *_ptr;
303
304 class Dimension
305 {
306 public:
307 constexpr Dimension()
308 : _dim_start(0), _stride(0)
309 {
310 }
311
312 int _dim_start;
313 int _stride;
314 };
315
316 std::array<Dimension, Coordinates::num_max_dimensions> _dims;
317};
318
319/** Iterate through the passed window, automatically adjusting the iterators and calling the lambda_functino for each element.
320 * It passes the x and y positions to the lambda_function for each iteration
321 *
322 * @param[in] w Window to iterate through.
323 * @param[in] lambda_function The function of type void(function)( const Coordinates & id ) to call at each iteration.
324 * Where id represents the absolute coordinates of the item to process.
325 * @param[in,out] iterators Tensor iterators which will be updated by this function before calling lambda_function.
326 */
327template <typename L, typename... Ts>
328inline void execute_window_loop(const Window &w, L &&lambda_function, Ts &&... iterators);
329
330/** Update window and padding size for each of the access patterns.
331 *
332 * First the window size is reduced based on all access patterns that are not
333 * allowed to modify the padding of the underlying tensor. Then the padding of
334 * the remaining tensors is increased to match the window.
335 *
336 * @param[in] win Window that is used by the kernel.
337 * @param[in] patterns Access patterns used to calculate the final window and padding.
338 *
339 * @return True if the window has been changed. Changes to the padding do not
340 * influence the returned value.
341 */
342template <typename... Ts>
343bool update_window_and_padding(Window &win, Ts &&... patterns)
344{
345 bool window_changed = false;
346
347 for_each([&](const IAccessWindow & w)
348 {
349 window_changed |= w.update_window_if_needed(win);
350 },
351 patterns...);
352
353 bool padding_changed = false;
354
355 for_each([&](const IAccessWindow & w)
356 {
357 padding_changed |= w.update_padding_if_needed(win);
358 },
359 patterns...);
360
361 return window_changed;
362}
363
364/** Calculate the maximum window for a given tensor shape and border setting
365 *
366 * @param[in] info Tensor info object defining the shape of the object for which the window is created.
367 * @param[in] steps (Optional) Number of elements processed for each step.
368 * @param[in] skip_border (Optional) If true exclude the border region from the window.
369 * @param[in] border_size (Optional) Border size.
370 *
371 * @return The maximum window the kernel can be executed on.
372 */
373Window calculate_max_window(const ITensorInfo &info, const Steps &steps = Steps(), bool skip_border = false, BorderSize border_size = BorderSize());
374
375/** Calculate the maximum window used by a horizontal kernel for a given tensor shape and border setting
376 *
377 * @param[in] info Tensor info object defining the shape of the object for which the window is created.
378 * @param[in] steps (Optional) Number of elements processed for each step.
379 * @param[in] skip_border (Optional) If true exclude the border region from the window.
380 * @param[in] border_size (Optional) Border size. The border region will be excluded from the window.
381 *
382 * @return The maximum window the kernel can be executed on.
383 */
384Window calculate_max_window_horizontal(const ITensorInfo &info, const Steps &steps = Steps(), bool skip_border = false, BorderSize border_size = BorderSize());
385
386/** Calculate the maximum window for a given tensor shape and border setting. The window will also includes the border.
387 *
388 * @param[in] info Tensor info object defining the shape of the object for which the window is created.
389 * @param[in] steps (Optional) Number of elements processed for each step.
390 * @param[in] border_size (Optional) Border size. The border region will be included in the window.
391 *
392 * @return The maximum window the kernel can be executed on.
393 */
394Window calculate_max_enlarged_window(const ITensorInfo &info, const Steps &steps = Steps(), BorderSize border_size = BorderSize());
395
396/** Intersect multiple valid regions.
397 *
398 * @param[in] regions Valid regions.
399 *
400 * @return Intersection of all regions.
401 */
402template <typename... Ts>
403ValidRegion intersect_valid_regions(Ts &&... regions)
404{
405 auto intersect = [](const ValidRegion & r1, const ValidRegion & r2) -> ValidRegion
406 {
407 ValidRegion region;
408
409 for(size_t d = 0; d < std::min(r1.anchor.num_dimensions(), r2.anchor.num_dimensions()); ++d)
410 {
411 region.anchor.set(d, std::max(r1.anchor[d], r2.anchor[d]));
412 }
413
414 for(size_t d = 0; d < std::min(r1.shape.num_dimensions(), r2.shape.num_dimensions()); ++d)
415 {
416 region.shape.set(d, std::min(r1.shape[d], r2.shape[d]));
417 }
418
419 return region;
420 };
421
422 return foldl(intersect, std::forward<Ts>(regions)...);
423}
424
425/** Create a strides object based on the provided strides and the tensor dimensions.
426 *
427 * @param[in] info Tensor info object providing the shape of the tensor for unspecified strides.
428 * @param[in] stride_x Stride to be used in X dimension (in bytes).
429 * @param[in] fixed_strides Strides to be used in higher dimensions starting at Y (in bytes).
430 *
431 * @return Strides object based on the specified strides. Missing strides are
432 * calculated based on the tensor shape and the strides of lower dimensions.
433 */
434template <typename T, typename... Ts>
435inline Strides compute_strides(const ITensorInfo &info, T stride_x, Ts &&... fixed_strides)
436{
437 const TensorShape &shape = info.tensor_shape();
438
439 // Create strides object
440 Strides strides(stride_x, fixed_strides...);
441
442 for(size_t i = 1 + sizeof...(Ts); i < info.num_dimensions(); ++i)
443 {
444 strides.set(i, shape[i - 1] * strides[i - 1]);
445 }
446
447 return strides;
448}
449
450/** Create a strides object based on the tensor dimensions.
451 *
452 * @param[in] info Tensor info object used to compute the strides.
453 *
454 * @return Strides object based on element size and tensor shape.
455 */
456template <typename... Ts>
457inline Strides compute_strides(const ITensorInfo &info)
458{
459 return compute_strides(info, info.element_size());
460}
461
462/* Auto initialize the tensor info (shape, number of channels, data type and fixed point position) if the current assignment is empty.
463 *
464 * @param[in,out] info Tensor info used to check and assign.
465 * @param[in] shape New shape.
466 * @param[in] num_channels New number of channels.
467 * @param[in] data_type New data type
468 * @param[in] fixed_point_position New fixed point position
Georgios Pinitas05078ec2017-11-02 13:06:59 +0000469 * @param[in] quantization_info (Optional) New quantization info
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100470 *
471 * @return True if the tensor info has been initialized
472 */
Georgios Pinitas05078ec2017-11-02 13:06:59 +0000473bool auto_init_if_empty(ITensorInfo &info,
474 const TensorShape &shape,
475 int num_channels, DataType data_type,
476 int fixed_point_position,
477 QuantizationInfo quantization_info = QuantizationInfo());
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100478
Georgios Pinitas283c1792017-11-10 18:14:06 +0000479/** Auto initialize the tensor info using another tensor info.
480 *
481 * @param info_sink Tensor info used to check and assign
482 * @param info_source Tensor info used to assign
483 *
484 * @return True if the tensor info has been initialized
485 */
486bool auto_init_if_empty(ITensorInfo &info_sink, ITensorInfo &info_source);
487
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100488/* Set the shape to the specified value if the current assignment is empty.
489 *
490 * @param[in,out] info Tensor info used to check and assign.
491 * @param[in] shape New shape.
492 *
493 * @return True if the shape has been changed.
494 */
495bool set_shape_if_empty(ITensorInfo &info, const TensorShape &shape);
496
497/* Set the format, data type and number of channels to the specified value if
498 * the current data type is unknown.
499 *
500 * @param[in,out] info Tensor info used to check and assign.
501 * @param[in] format New format.
502 *
503 * @return True if the format has been changed.
504 */
505bool set_format_if_unknown(ITensorInfo &info, Format format);
506
507/* Set the data type and number of channels to the specified value if
508 * the current data type is unknown.
509 *
510 * @param[in,out] info Tensor info used to check and assign.
511 * @param[in] data_type New data type.
512 *
513 * @return True if the data type has been changed.
514 */
515bool set_data_type_if_unknown(ITensorInfo &info, DataType data_type);
516
517/* Set the fixed point position to the specified value if
518 * the current fixed point position is 0 and the data type is QS8 or QS16
519 *
520 * @param[in,out] info Tensor info used to check and assign.
521 * @param[in] fixed_point_position New fixed point position
522 *
523 * @return True if the fixed point position has been changed.
524 */
525bool set_fixed_point_position_if_zero(ITensorInfo &info, int fixed_point_position);
Georgios Pinitas05078ec2017-11-02 13:06:59 +0000526
527/* Set the quantization info to the specified value if
528 * the current quantization info is empty and the data type of asymmetric quantized type
529 *
530 * @param[in,out] info Tensor info used to check and assign.
531 * @param[in] quantization_info Quantization info
532 *
533 * @return True if the quantization info has been changed.
534 */
535bool set_quantization_info_if_empty(ITensorInfo &info, QuantizationInfo quantization_info);
536
Isabella Gottardi1fab09f2017-07-13 15:55:57 +0100537/** Helper function to calculate the Valid Region for Scale.
538 *
539 * @param[in] src_info Input tensor info used to check.
540 * @param[in] dst_shape Shape of the output.
541 * @param[in] policy Interpolation policy.
542 * @param[in] border_size Size of the border.
543 * @param[in] border_undefined True if the border is undefined.
544 *
545 * @return The corrispondent valid region
546 */
547ValidRegion calculate_valid_region_scale(const ITensorInfo &src_info, const TensorShape &dst_shape, InterpolationPolicy policy, BorderSize border_size, bool border_undefined);
Georgios Pinitas05078ec2017-11-02 13:06:59 +0000548
Georgios Pinitas5ee66ea2017-09-07 17:29:16 +0100549/** Convert a linear index into n-dimensional coordinates.
550 *
551 * @param[in] shape Shape of the n-dimensional tensor.
552 * @param[in] index Linear index specifying the i-th element.
553 *
554 * @return n-dimensional coordinates.
555 */
556inline Coordinates index2coords(const TensorShape &shape, int index);
Georgios Pinitas05078ec2017-11-02 13:06:59 +0000557
Georgios Pinitas5ee66ea2017-09-07 17:29:16 +0100558/** Convert n-dimensional coordinates into a linear index.
559 *
560 * @param[in] shape Shape of the n-dimensional tensor.
561 * @param[in] coord N-dimensional coordinates.
562 *
563 * @return linead index
564 */
565inline int coords2index(const TensorShape &shape, const Coordinates &coord);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100566} // namespace arm_compute
567
568#include "arm_compute/core/Helpers.inl"
569#endif /*__ARM_COMPUTE_HELPERS_H__ */