arm_compute v17.04
[platform/upstream/armcl.git] / arm_compute / core / Helpers.h
1 /*
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/Coordinates.h"
28 #include "arm_compute/core/IAccessWindow.h"
29 #include "arm_compute/core/Steps.h"
30 #include "arm_compute/core/Strides.h"
31 #include "arm_compute/core/TensorShape.h"
32 #include "arm_compute/core/Window.h"
33
34 #include <array>
35 #include <cstddef>
36 #include <cstdint>
37 #include <memory>
38 #include <tuple>
39 #include <type_traits>
40 #include <utility>
41
42 namespace arm_compute
43 {
44 class IKernel;
45 class ITensor;
46 class TensorInfo;
47
48 namespace cpp14
49 {
50 #ifndef DOXYGEN_SKIP_THIS /* Doxygen gets confused by the templates and can't match the implementation to the declaration */
51 template <class T>
52 struct _Unique_if
53 {
54     typedef std::unique_ptr<T> _Single_object;
55 };
56
57 template <class T>
58 struct _Unique_if<T[]>
59 {
60     typedef std::unique_ptr<T[]> _Unknown_bound;
61 };
62
63 template <class T, size_t N>
64 struct _Unique_if<T[N]>
65 {
66     typedef void _Known_bound;
67 };
68
69 template <class T, class... Args>
70 typename _Unique_if<T>::_Single_object
71 make_unique(Args &&... args)
72 {
73     return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
74 }
75
76 template <class T>
77 typename _Unique_if<T>::_Unknown_bound
78 make_unique(size_t n)
79 {
80     typedef typename std::remove_extent<T>::type U;
81     return std::unique_ptr<T>(new U[n]());
82 }
83
84 template <class T, class... Args>
85 typename _Unique_if<T>::_Known_bound
86 make_unique(Args &&...) = delete;
87 #endif /* DOXYGEN_SKIP_THIS */
88 }
89 }
90
91 namespace
92 {
93 /** Computes bilinear interpolation using the pointer to the top-left pixel and the pixel's distance between
94  * the real coordinates and the smallest following integer coordinates.
95  *
96  * @param[in] pixel_ptr Pointer to the top-left pixel value. Format: Single channel U8
97  * @param[in] stride    Stride to access the bottom-left and bottom-right pixel values
98  * @param[in] dx        Pixel's distance between the X real coordinate and the smallest X following integer
99  * @param[in] dy        Pixel's distance between the Y real coordinate and the smallest Y following integer
100  *
101  * @note dx and dy must be in the range [0, 1.0]
102  *
103  * @return The bilinear interpolated pixel value
104  */
105 inline uint8_t delta_bilinear_c1u8(const uint8_t *pixel_ptr, size_t stride, float dx, float dy);
106
107 /** Return the pixel at (x,y) using bilinear interpolation. The image must be single channel U8
108  *
109  * @warning Only works if the iterator was created with an IImage
110  *
111  * @param[in] first_pixel_ptr Pointer to the first pixel of a single channel U8 image.
112  * @param[in] stride          Stride in bytes of the image;
113  * @param[in] x               X position of the wanted pixel
114  * @param[in] y               Y position of the wanted pixel
115  *
116  * @return The pixel at (x, y) using bilinear interpolation.
117  */
118 inline uint8_t pixel_bilinear_c1u8(const uint8_t *first_pixel_ptr, size_t stride, float x, float y);
119
120 /** Return the pixel at (x,y) using bilinear interpolation by clamping when out of borders. The image must be single channel U8
121  *
122  * @warning Only works if the iterator was created with an IImage
123  *
124  * @param[in] first_pixel_ptr Pointer to the first pixel of a single channel U8 image.
125  * @param[in] stride          Stride in bytes of the image
126  * @param[in] width           Width of the image
127  * @param[in] height          Height of the image
128  * @param[in] x               X position of the wanted pixel
129  * @param[in] y               Y position of the wanted pixel
130  *
131  * @return The pixel at (x, y) using bilinear interpolation.
132  */
133 inline uint8_t pixel_bilinear_c1u8_clamp(const uint8_t *first_pixel_ptr, size_t stride, size_t width, size_t height, float x, float y);
134
135 /** Return the pixel at (x,y) using area interpolation by clamping when out of borders. The image must be single channel U8
136  *
137  * @note The interpolation area depends on the width and height ration of the input and output images
138  * @note Currently average of the contributing pixels is calculated
139  *
140  * @param[in] first_pixel_ptr Pointer to the first pixel of a single channel U8 image.
141  * @param[in] stride          Stride in bytes of the image
142  * @param[in] width           Width of the image
143  * @param[in] height          Height of the image
144  * @param[in] wr              Width ratio among the input image width and output image width.
145  * @param[in] hr              Height ratio among the input image height and output image height.
146  * @param[in] x               X position of the wanted pixel
147  * @param[in] y               Y position of the wanted pixel
148  *
149  * @return The pixel at (x, y) using area interpolation.
150  */
151 inline 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);
152
153 /** Performs clamping among a lower and upper value.
154  *
155  * @param[in] n     Value to clamp.
156  * @param[in] lower Lower threshold.
157  * @param[in] upper Upper threshold.
158  *
159  *  @return Clamped value.
160  */
161 template <typename T>
162 inline T clamp(const T &n, const T &lower, const T &upper)
163 {
164     return std::max(lower, std::min(n, upper));
165 }
166
167 /** Base case of for_each. Does nothing. */
168 template <typename F>
169 inline void for_each(F &&)
170 {
171 }
172
173 /** Call the function for each of the arguments
174  *
175  * @param[in] func Function to be called
176  * @param[in] arg  Argument passed to the function
177  * @param[in] args Remaining arguments
178  */
179 template <typename F, typename T, typename... Ts>
180 inline void for_each(F &&func, T &&arg, Ts &&... args)
181 {
182     func(arg);
183     for_each(func, args...);
184 }
185
186 /** Base case of foldl. Return value. */
187 template <typename F, typename T>
188 inline T foldl(F &&, T &&value)
189 {
190     return value;
191 }
192
193 /** Fold left.
194  *
195  * @param[in] func    Function to be called
196  * @param[in] initial Initial value
197  * @param[in] value   Argument passed to the function
198  * @param[in] values  Remaining arguments
199  */
200 template <typename F, typename I, typename T, typename... Ts>
201 inline I foldl(F &&func, I &&initial, T &&value, Ts &&... values)
202 {
203     return foldl(func, func(initial, value), values...);
204 }
205 }
206
207 namespace arm_compute
208 {
209 /** Iterator updated by @ref execute_window_loop for each window element */
210 class Iterator
211 {
212 public:
213     /** Default constructor to create an empty iterator */
214     constexpr Iterator();
215     /** Create a container iterator for the metadata and allocation contained in the ITensor
216      *
217      * @param[in] tensor The tensor to associate to the iterator.
218      * @param[in] window The window which will be used to iterate over the tensor.
219      */
220     Iterator(const ITensor *tensor, const Window &window);
221
222     /** Increment the iterator along the specified dimension of the step value associated to the dimension.
223      *
224      * @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.
225      *
226      * @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.
227      *
228      * @param[in] dimension Dimension to increment
229      */
230     void increment(size_t dimension);
231
232     /** Return the offset in bytes from the first element to the current position of the iterator
233      *
234      * @return The current position of the iterator in bytes relative to the first element.
235      */
236     constexpr int offset() const;
237
238     /** Return a pointer to the current pixel.
239      *
240      * @warning Only works if the iterator was created with an ITensor.
241      *
242      * @return equivalent to  buffer() + offset()
243      */
244     constexpr uint8_t *ptr() const;
245
246     /** Move the iterator back to the beginning of the specified dimension.
247      *
248      * @param[in] dimension Dimension to reset
249      */
250     void reset(size_t dimension);
251
252 private:
253     uint8_t *_ptr;
254
255     class Dimension
256     {
257     public:
258         constexpr Dimension()
259             : _dim_start(0), _stride(0)
260         {
261         }
262
263         int _dim_start;
264         int _stride;
265     };
266
267     std::array<Dimension, Coordinates::num_max_dimensions> _dims;
268 };
269
270 /** Iterate through the passed window, automatically adjusting the iterators and calling the lambda_functino for each element.
271  *  It passes the x and y positions to the lambda_function for each iteration
272  *
273  * @param[in]     w               Window to iterate through.
274  * @param[in]     lambda_function The function of type void(function)( const Coordinates & id ) to call at each iteration.
275  *                                Where id represents the absolute coordinates of the item to process.
276  * @param[in,out] iterators       Tensor iterators which will be updated by this function before calling lambda_function.
277  */
278 template <typename L, typename... Ts>
279 inline void execute_window_loop(const Window &w, L &&lambda_function, Ts &&... iterators);
280
281 /** Update window and padding size for each of the access patterns.
282  *
283  * First the window size is reduced based on all access patterns that are not
284  * allowed to modify the padding of the underlying tensor. Then the padding of
285  * the remaining tensors is increased to match the window.
286  *
287  * @param[in] win      Window that is used by the kernel.
288  * @param[in] patterns Access patterns used to calculate the final window and padding.
289  *
290  * @return True if the window has been changed. Changes to the padding do not
291  *         influence the returned value.
292  */
293 template <typename... Ts>
294 bool update_window_and_padding(Window &win, Ts &&... patterns)
295 {
296     bool window_changed = false;
297
298     for_each([&](const IAccessWindow & w)
299     {
300         window_changed |= w.update_window_if_needed(win);
301     },
302     patterns...);
303
304     bool padding_changed = false;
305
306     for_each([&](const IAccessWindow & w)
307     {
308         padding_changed |= w.update_padding_if_needed(win);
309     },
310     patterns...);
311
312     return window_changed;
313 }
314
315 /** Calculate the maximum window for a given tensor shape and border setting
316  *
317  * @param[in] info        Tensor info object defining the shape of the object for which the window is created.
318  * @param[in] steps       (Optional) Number of elements processed for each step.
319  * @param[in] skip_border (Optional) If true exclude the border region from the window.
320  * @param[in] border_size (Optional) Border size.
321  *
322  * @return The maximum window the kernel can be executed on.
323  */
324 Window calculate_max_window(const TensorInfo &info, const Steps &steps = Steps(), bool skip_border = false, BorderSize border_size = BorderSize());
325
326 /** Calculate the maximum window used by a horizontal kernel for a given tensor shape and border setting
327  *
328  * @param[in] info        Tensor info object defining the shape of the object for which the window is created.
329  * @param[in] steps       (Optional) Number of elements processed for each step.
330  * @param[in] skip_border (Optional) If true exclude the border region from the window.
331  * @param[in] border_size (Optional) Border size. The border region will be excluded from the window.
332  *
333  * @return The maximum window the kernel can be executed on.
334  */
335 Window calculate_max_window_horizontal(const TensorInfo &info, const Steps &steps = Steps(), bool skip_border = false, BorderSize border_size = BorderSize());
336
337 /** Intersect multiple valid regions.
338  *
339  * @param[in] regions Valid regions.
340  *
341  * @return Intersection of all regions.
342  */
343 template <typename... Ts>
344 ValidRegion intersect_valid_regions(Ts &&... regions)
345 {
346     auto intersect = [](const ValidRegion & r1, const ValidRegion & r2) -> ValidRegion
347     {
348         ValidRegion region;
349
350         for(size_t d = 0; d < std::min(r1.anchor.num_dimensions(), r2.anchor.num_dimensions()); ++d)
351         {
352             region.anchor.set(d, std::max(r1.anchor[d], r2.anchor[d]));
353         }
354
355         for(size_t d = 0; d < std::min(r1.shape.num_dimensions(), r2.shape.num_dimensions()); ++d)
356         {
357             region.shape.set(d, std::min(r1.shape[d], r2.shape[d]));
358         }
359
360         return region;
361     };
362
363     return foldl(intersect, std::forward<Ts>(regions)...);
364 }
365
366 /** Create a strides object based on the provided strides and the tensor dimensions.
367  *
368  * @param[in] info          Tensor info object providing the shape of the tensor for unspecified strides.
369  * @param[in] stride_x      Stride to be used in X dimension (in bytes).
370  * @param[in] fixed_strides Strides to be used in higher dimensions starting at Y (in bytes).
371  *
372  * @return Strides object based on the specified strides. Missing strides are
373  *         calculated based on the tensor shape and the strides of lower dimensions.
374  */
375 template <typename T, typename... Ts>
376 inline Strides compute_strides(const TensorInfo &info, T stride_x, Ts &&... fixed_strides)
377 {
378     const TensorShape &shape = info.tensor_shape();
379
380     // Create strides object
381     Strides strides(stride_x, fixed_strides...);
382
383     for(size_t i = 1 + sizeof...(Ts); i < info.num_dimensions(); ++i)
384     {
385         strides.set(i, shape[i - 1] * strides[i - 1]);
386     }
387
388     return strides;
389 }
390
391 /** Create a strides object based on the tensor dimensions.
392  *
393  * @param[in] info Tensor info object used to compute the strides.
394  *
395  * @return Strides object based on element size and tensor shape.
396  */
397 template <typename... Ts>
398 inline Strides compute_strides(const TensorInfo &info)
399 {
400     return compute_strides(info, info.element_size());
401 }
402 }
403
404 #include "arm_compute/core/Helpers.inl"
405 #endif /*__ARM_COMPUTE_HELPERS_H__ */