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