blob: 96d5e2d3c5b944a75463380055899dc25258502d [file] [log] [blame]
Anthony Barbier6ff3b192017-09-04 18:44:23 +01001/*
2 * Copyright (c) 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_TEST_TENSOR_OPERATIONS_H__
25#define __ARM_COMPUTE_TEST_TENSOR_OPERATIONS_H__
26
Anthony Barbier6ff3b192017-09-04 18:44:23 +010027#include "arm_compute/core/FixedPoint.h"
28#include "arm_compute/core/Types.h"
Moritz Pflanzere49e2662017-07-21 15:55:28 +010029#include "support/ToolchainSupport.h"
30#include "tests/Types.h"
31#include "tests/Utils.h"
Anthony Barbier6ff3b192017-09-04 18:44:23 +010032#include "tests/validation/FixedPoint.h"
Moritz Pflanzere49e2662017-07-21 15:55:28 +010033#include "tests/validation/Tensor.h"
Giorgio Arena50f9fd72017-06-19 17:05:30 +010034#include "tests/validation/ValidationUserConfiguration.h"
Moritz Pflanzere49e2662017-07-21 15:55:28 +010035#include "tests/validation/half.h"
Anthony Barbier6ff3b192017-09-04 18:44:23 +010036
37#include <algorithm>
38#include <array>
39#include <cmath>
Giorgio Arena50f9fd72017-06-19 17:05:30 +010040#include <random>
Georgios Pinitasac4e8732017-07-05 17:02:25 +010041#include <string>
Georgios Pinitasd4f8c272017-06-30 16:16:19 +010042#include <vector>
Anthony Barbier6ff3b192017-09-04 18:44:23 +010043
44namespace arm_compute
45{
46namespace test
47{
48namespace validation
49{
50namespace tensor_operations
51{
52namespace
53{
Pablo Tello383deec2017-06-23 10:40:05 +010054template <class T>
55struct is_floating_point
56 : std::integral_constant < bool,
Moritz Pflanzere49e2662017-07-21 15:55:28 +010057 std::is_same<float, typename std::remove_cv<T>::type>::value || std::is_same<half_float::half, typename std::remove_cv<T>::type>::value
58 || std::is_same<double, typename std::remove_cv<T>::type>::value || std::is_same<long double, typename std::remove_cv<T>::type>::value >
Pablo Tello383deec2017-06-23 10:40:05 +010059{
60};
61
Gian Marco Iodice2bbd9642017-07-04 16:46:32 +010062template <typename T, typename std::enable_if<is_floating_point<T>::value, int>::type * = nullptr>
Anthony Barbier6ff3b192017-09-04 18:44:23 +010063void vector_matrix_multiply(const T *in, const T *weights, const T *bias, T *out, int cols_weights, int rows_weights, uint8_t fixed_point_position)
64{
65 for(int x = 0; x < cols_weights; ++x)
66 {
Moritz Pflanzere49e2662017-07-21 15:55:28 +010067 T acc(0);
Anthony Barbier6ff3b192017-09-04 18:44:23 +010068 for(int y = 0; y < rows_weights; ++y)
69 {
70 acc += in[y] * weights[x + y * cols_weights];
71 }
72 out[x] = acc + bias[x];
73 }
74}
75
Gian Marco Iodice2bbd9642017-07-04 16:46:32 +010076// Vector matrix multiply for fixed point type
77template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type * = nullptr>
78void vector_matrix_multiply(const T *in, const T *weights, const T *bias, T *out, int cols_weights, int rows_weights, uint8_t fixed_point_position)
Anthony Barbier6ff3b192017-09-04 18:44:23 +010079{
80 using namespace fixed_point_arithmetic;
Gian Marco Iodice2bbd9642017-07-04 16:46:32 +010081 using promoted_type = typename fixed_point_arithmetic::traits::promote<T>::type;
Anthony Barbier6ff3b192017-09-04 18:44:23 +010082
83 for(int x = 0; x < cols_weights; ++x)
84 {
85 // Reset accumulator
86 fixed_point<promoted_type> acc(0, fixed_point_position);
87
88 for(int y = 0; y < rows_weights; ++y)
89 {
90 const fixed_point<promoted_type> i_value(in[y], fixed_point_position, true);
91 const fixed_point<promoted_type> w_value(weights[x + y * cols_weights], fixed_point_position, true);
92 const fixed_point<promoted_type> iw = i_value * w_value;
93 acc = iw + acc;
94 }
95
96 // Get the bias
Gian Marco Iodice2bbd9642017-07-04 16:46:32 +010097 const fixed_point<T> b(bias[x], fixed_point_position, true);
Anthony Barbier6ff3b192017-09-04 18:44:23 +010098
99 // Convert back and accumulate the bias
Gian Marco Iodice2bbd9642017-07-04 16:46:32 +0100100 fixed_point<T> res(acc);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100101 res = res + b;
102
103 // Store the result
104 out[x] = res.raw();
105 }
106}
107
SiCong Libacaf9a2017-06-19 13:41:45 +0100108// Return a tensor element at a specified coordinate with different border modes
Giorgio Arena50f9fd72017-06-19 17:05:30 +0100109template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
110T tensor_elem_at(const Tensor<T> &in, Coordinates &coord, BorderMode border_mode, T constant_border_value)
111{
112 const int x = coord.x();
113 const int y = coord.y();
114 const int width = static_cast<int>(in.shape().x());
115 const int height = static_cast<int>(in.shape().y());
116
SiCong Libacaf9a2017-06-19 13:41:45 +0100117 // If coordinates beyond range of tensor's width or height
Giorgio Arena50f9fd72017-06-19 17:05:30 +0100118 if(x < 0 || y < 0 || x >= width || y >= height)
119 {
SiCong Libacaf9a2017-06-19 13:41:45 +0100120 if(border_mode == BorderMode::REPLICATE)
Giorgio Arena50f9fd72017-06-19 17:05:30 +0100121 {
122 coord.set(0, std::max(0, std::min(x, width - 1)));
123 coord.set(1, std::max(0, std::min(y, height - 1)));
124 return in[coord2index(in.shape(), coord)];
125 }
126 else
127 {
SiCong Libacaf9a2017-06-19 13:41:45 +0100128 return constant_border_value;
Giorgio Arena50f9fd72017-06-19 17:05:30 +0100129 }
130 }
131 else
132 {
133 return in[coord2index(in.shape(), coord)];
134 }
135}
136
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100137/** Apply 2D spatial filter on a single element of @p in at coordinates @p coord
138 *
139 * - filter sizes have to be odd number
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100140 * - Row major order of filter assumed
141 * - TO_ZERO rounding policy assumed
142 * - SATURATE convert policy assumed
143 *
144 */
145template <typename T1, typename T2, typename T3>
Giorgio Arena50f9fd72017-06-19 17:05:30 +0100146void apply_2d_spatial_filter(Coordinates coord, const Tensor<T1> &in, Tensor<T3> &out, const TensorShape &filter_shape, const T2 *filter_itr, float scale, BorderMode border_mode,
147 T1 constant_border_value = 0)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100148{
Giorgio Arena50f9fd72017-06-19 17:05:30 +0100149 double val = 0;
150 const int x = coord.x();
151 const int y = coord.y();
152 for(int j = y - static_cast<int>(filter_shape[1] / 2); j <= y + static_cast<int>(filter_shape[1] / 2); ++j)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100153 {
Giorgio Arena50f9fd72017-06-19 17:05:30 +0100154 for(int i = x - static_cast<int>(filter_shape[0] / 2); i <= x + static_cast<int>(filter_shape[0] / 2); ++i)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100155 {
156 coord.set(0, i);
157 coord.set(1, j);
SiCong Libacaf9a2017-06-19 13:41:45 +0100158 val += static_cast<double>(*filter_itr) * tensor_elem_at(in, coord, border_mode, constant_border_value);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100159 ++filter_itr;
160 }
161 }
162 coord.set(0, x);
163 coord.set(1, y);
Moritz Pflanzerd0ae8b82017-06-29 14:51:57 +0100164 const double rounded_val = support::cpp11::trunc(val * static_cast<double>(scale));
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100165 out[coord2index(in.shape(), coord)] = saturate_cast<T3>(rounded_val);
166}
167} // namespace
168
Giorgio Arena50f9fd72017-06-19 17:05:30 +0100169// Sobel 3x3
170template <typename T1, typename T2>
171void sobel_3x3(Tensor<T1> &in, Tensor<T2> &out_x, Tensor<T2> &out_y, BorderMode border_mode, uint8_t constant_border_value)
172{
173 const std::array<int8_t, 9> sobel_x{ { -1, 0, 1, -2, 0, 2, -1, 0, 1 } };
174 const std::array<int8_t, 9> sobel_y{ { -1, -2, -1, 0, 0, 0, 1, 2, 1 } };
175
176 for(int element_idx = 0; element_idx < in.num_elements(); ++element_idx)
177 {
178 const Coordinates id = index2coord(in.shape(), element_idx);
179
180 apply_2d_spatial_filter(id, in, out_x, TensorShape(3U, 3U), sobel_x.data(), 1.f, border_mode, constant_border_value);
181 apply_2d_spatial_filter(id, in, out_y, TensorShape(3U, 3U), sobel_y.data(), 1.f, border_mode, constant_border_value);
182 }
183}
184
185// Sobel 5x5
186template <typename T1, typename T2>
187void sobel_5x5(Tensor<T1> &in, Tensor<T2> &out_x, Tensor<T2> &out_y, BorderMode border_mode, uint8_t constant_border_value)
188{
189 const std::array<int8_t, 25> sobel_x{ {
190 -1, -2, 0, 2, 1,
191 -4, -8, 0, 8, 4,
192 -6, -12, 0, 12, 6,
193 -4, -8, 0, 8, 4,
194 -1, -2, 0, 2, 1
195 } };
196
197 const std::array<int8_t, 25> sobel_y{ {
198 -1, -4, -6, -4, -1,
199 -2, -8, -12, -8, -2,
200 0, 0, 0, 0, 0,
201 2, 8, 12, 8, 2,
202 1, 4, 6, 4, 1
203 } };
204
205 for(int element_idx = 0; element_idx < in.num_elements(); ++element_idx)
206 {
207 const Coordinates id = index2coord(in.shape(), element_idx);
208
209 apply_2d_spatial_filter(id, in, out_x, TensorShape(5U, 5U), sobel_x.data(), 1.f, border_mode, constant_border_value);
210 apply_2d_spatial_filter(id, in, out_y, TensorShape(5U, 5U), sobel_y.data(), 1.f, border_mode, constant_border_value);
211 }
212}
213
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100214template <typename T>
215void compute_min_max(const Tensor<T> &in, void *min, void *max)
Giorgio Arena2ca209e2017-06-13 15:49:37 +0100216{
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100217 using type = typename std::conditional<std::is_same<T, float>::value, float, int32_t>::type;
Giorgio Arena2ca209e2017-06-13 15:49:37 +0100218
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100219 // Set min and max to first pixel
220 type tmp_min = static_cast<type>(in[0]);
221 type tmp_max = static_cast<type>(in[0]);
Giorgio Arena2ca209e2017-06-13 15:49:37 +0100222
223 // Look for min and max values
224 for(int i = 1; i < in.num_elements(); ++i)
225 {
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100226 if(static_cast<type>(in[i]) < tmp_min)
Giorgio Arena2ca209e2017-06-13 15:49:37 +0100227 {
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100228 tmp_min = static_cast<type>(in[i]);
Giorgio Arena2ca209e2017-06-13 15:49:37 +0100229 }
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100230 if(static_cast<type>(in[i]) > tmp_max)
Giorgio Arena2ca209e2017-06-13 15:49:37 +0100231 {
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100232 tmp_max = static_cast<type>(in[i]);
Giorgio Arena2ca209e2017-06-13 15:49:37 +0100233 }
234 }
235
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100236 *static_cast<type *>(min) = tmp_min;
237 *static_cast<type *>(max) = tmp_max;
238}
239
240// Min max location
241template <typename T1>
242void min_max_location(const Tensor<T1> &in, void *min, void *max, IArray<Coordinates2D> &min_loc, IArray<Coordinates2D> &max_loc, uint32_t &min_count, uint32_t &max_count)
243{
244 const size_t width = in.shape().x();
245
246 compute_min_max(in, min, max);
247
248 using type = typename std::conditional<std::is_same<T1, float>::value, float, int32_t>::type;
249
250 type min_value = *static_cast<type *>(min);
251 type max_value = *static_cast<type *>(max);
252
253 min_count = 0;
254 max_count = 0;
Giorgio Arena2ca209e2017-06-13 15:49:37 +0100255 for(int i = 0; i < in.num_elements(); ++i)
256 {
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100257 if(static_cast<type>(in[i]) == min_value)
Giorgio Arena2ca209e2017-06-13 15:49:37 +0100258 {
259 Coordinates2D min_coord;
260 min_coord.x = static_cast<int32_t>(i % width);
261 min_coord.y = static_cast<int32_t>(i / width);
262
263 min_loc.push_back(min_coord);
264
265 min_count++;
266 }
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100267 if(static_cast<type>(in[i]) == max_value)
Giorgio Arena2ca209e2017-06-13 15:49:37 +0100268 {
269 Coordinates2D max_coord;
270 max_coord.x = static_cast<int32_t>(i % width);
271 max_coord.y = static_cast<int32_t>(i / width);
272
273 max_loc.push_back(max_coord);
274
275 max_count++;
276 }
277 }
278}
279
Giorgio Arenaf7959862017-06-13 15:19:51 +0100280// Mean Standard Deviation
281template <typename T1>
282void mean_and_standard_deviation(const Tensor<T1> &in, float &mean, float &std_dev)
283{
284 int num_elements = in.num_elements();
285
286 // Calculate mean
287 mean = 0.f;
288 for(int i = 0; i < num_elements; ++i)
289 {
290 mean += in[i];
291 }
292 mean /= num_elements;
293
294 // Calculate standard deviation
295 std_dev = 0.f;
296 for(int i = 0; i < num_elements; ++i)
297 {
298 std_dev += (mean - in[i]) * (mean - in[i]);
299 }
300 std_dev = sqrt(std_dev / num_elements);
301}
302
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100303// Integral Image
304void integral_image(const Tensor<uint8_t> &in, Tensor<uint32_t> &out)
305{
306 // Length of dimensions
307 const size_t width = in.shape().x();
308 const size_t height = in.shape().y();
309 const size_t depth = in.shape().z() * in.shape()[3] * in.shape()[4] * in.shape()[5];
310
311 const size_t image_size = width * height;
312
313 for(size_t z = 0; z < depth; ++z)
314 {
315 size_t current_image = z * image_size;
316
317 //First element of each image
318 out[current_image] = in[current_image];
319
320 // First row of each image (add only pixel on the left)
321 for(size_t x = 1; x < width; ++x)
322 {
323 out[current_image + x] = static_cast<uint32_t>(in[current_image + x]) + out[current_image + x - 1];
324 }
325
326 // Subsequent rows
327 for(size_t y = 1; y < height; ++y)
328 {
329 size_t current_row = current_image + (width * y);
330
331 // First element of each row (add only pixel up)
332 out[current_row] = static_cast<uint32_t>(in[current_row]) + out[current_row - width];
333
334 // Following row elements
335 for(size_t x = 1; x < width; ++x)
336 {
337 size_t current_pixel = current_row + x;
338
339 // out = in + up(out) + left(out) - up_left(out)
340 out[current_pixel] = static_cast<uint32_t>(in[current_pixel]) + out[current_pixel - 1]
341 + out[current_pixel - width] - out[current_pixel - width - 1];
342 }
343 }
344 }
345}
346
347// Absolute difference
348template <typename T1, typename T2, typename T3>
349void absolute_difference(const Tensor<T1> &in1, const Tensor<T2> &in2, Tensor<T3> &out)
350{
351 using intermediate_type = typename common_promoted_signed_type<T1, T2, T3>::intermediate_type;
352
353 for(int i = 0; i < in1.num_elements(); ++i)
354 {
Moritz Pflanzere49e2662017-07-21 15:55:28 +0100355 intermediate_type val(std::abs(static_cast<intermediate_type>(in1[i]) - static_cast<intermediate_type>(in2[i])));
356 out[i] = saturate_cast<T3>(val);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100357 }
358}
359
360// Accumulate
361template <typename T1, typename T2>
362void accumulate(const Tensor<T1> &in, Tensor<T2> &out)
363{
364 using intermediate_type = typename common_promoted_signed_type<T1, T2>::intermediate_type;
365
366 for(int i = 0; i < in.num_elements(); ++i)
367 {
368 intermediate_type val = static_cast<intermediate_type>(out[i]) + static_cast<intermediate_type>(in[i]);
369 out[i] = saturate_cast<T2>(val);
370 }
371}
372
373// Accumulate squared
374template <typename T1, typename T2>
375void accumulate_squared(const Tensor<T1> &in, Tensor<T2> &out, uint32_t shift)
376{
377 if(shift > 15)
378 {
379 ARM_COMPUTE_ERROR("Shift in accumulate_squared must be within the range [0, 15]");
380 }
381 using intermediate_type = typename common_promoted_signed_type<T1, T2>::intermediate_type;
382 intermediate_type denom = 1 << shift;
383
384 for(int i = 0; i < in.num_elements(); ++i)
385 {
386 intermediate_type val = static_cast<intermediate_type>(out[i]) + (static_cast<intermediate_type>(in[i]) * static_cast<intermediate_type>(in[i]) / denom);
387 out[i] = saturate_cast<T2>(val);
388 }
389}
390
391// Accumulate weighted
392template <typename T>
393void accumulate_weighted(const Tensor<T> &in, Tensor<T> &out, float alpha)
394{
395 if(alpha < 0.f || alpha > 1.f)
396 {
397 ARM_COMPUTE_ERROR("Weight (alpha) specified in accumulate_weighted must be within the range [0, 1]");
398 }
399 using intermediate_type = typename common_promoted_signed_type<T>::intermediate_type;
400
401 for(int i = 0; i < in.num_elements(); ++i)
402 {
403 double val = (1. - static_cast<double>(alpha)) * static_cast<intermediate_type>(out[i]) + static_cast<double>(alpha) * static_cast<intermediate_type>(in[i]);
404 out[i] = static_cast<T>(val);
405 }
406}
407
408// Arithmetic addition
409template <typename T1, typename T2, typename T3>
410void arithmetic_addition(const Tensor<T1> &in1, const Tensor<T2> &in2, Tensor<T3> &out, ConvertPolicy convert_policy)
411{
412 using intermediate_type = typename common_promoted_signed_type<T1, T2, T3>::intermediate_type;
413
414 for(int i = 0; i < in1.num_elements(); ++i)
415 {
416 intermediate_type val = static_cast<intermediate_type>(in1[i]) + static_cast<intermediate_type>(in2[i]);
417 out[i] = (convert_policy == ConvertPolicy::SATURATE) ? saturate_cast<T3>(val) : static_cast<T3>(val);
418 }
419}
420
421// Arithmetic Subtraction
422template <typename T1, typename T2, typename T3>
423void arithmetic_subtraction(const Tensor<T1> &in1, const Tensor<T2> &in2, Tensor<T3> &out, ConvertPolicy convert_policy)
424{
425 using intermediate_type = typename common_promoted_signed_type<T1, T2, T3>::intermediate_type;
426
427 for(int i = 0; i < in1.num_elements(); ++i)
428 {
429 intermediate_type val = static_cast<intermediate_type>(in1[i]) - static_cast<intermediate_type>(in2[i]);
430 out[i] = (convert_policy == ConvertPolicy::SATURATE) ? saturate_cast<T3>(val) : static_cast<T3>(val);
431 }
432}
433
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100434// Bitwise or
435template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
436void bitwise_or(const Tensor<T> &in1, const Tensor<T> &in2, Tensor<T> &out)
437{
438 for(int i = 0; i < in1.num_elements(); ++i)
439 {
440 out[i] = in1[i] | in2[i];
441 }
442}
443
444// Bitwise xor
445template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
446void bitwise_xor(const Tensor<T> &in1, const Tensor<T> &in2, Tensor<T> &out)
447{
448 for(int i = 0; i < in1.num_elements(); ++i)
449 {
450 out[i] = in1[i] ^ in2[i];
451 }
452}
453
454// Bitwise not
455template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
456void bitwise_not(const Tensor<T> &in, Tensor<T> &out)
457{
458 for(int i = 0; i < in.num_elements(); ++i)
459 {
460 out[i] = ~in[i];
461 }
462}
463
SiCong Libacaf9a2017-06-19 13:41:45 +0100464// Box3x3 filter
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100465template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
SiCong Libacaf9a2017-06-19 13:41:45 +0100466void box3x3(const Tensor<T> &in, Tensor<T> &out, BorderMode border_mode, T constant_border_value)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100467{
468 const std::array<T, 9> filter{ { 1, 1, 1, 1, 1, 1, 1, 1, 1 } };
SiCong Libacaf9a2017-06-19 13:41:45 +0100469 float scale = 1.f / static_cast<float>(filter.size());
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100470 for(int element_idx = 0; element_idx < in.num_elements(); ++element_idx)
471 {
472 const Coordinates id = index2coord(in.shape(), element_idx);
SiCong Libacaf9a2017-06-19 13:41:45 +0100473 apply_2d_spatial_filter(id, in, out, TensorShape(3U, 3U), filter.data(), scale, border_mode, constant_border_value);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100474 }
475}
476
477// Depth conversion
Pablo Tello91654c42017-07-05 11:32:17 +0100478template < typename T1, typename T2, typename std::enable_if < std::is_integral<T1>::value &&is_floating_point<T2>::value, int >::type = 0 >
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100479void depth_convert(const Tensor<T1> &in, Tensor<T2> &out, ConvertPolicy policy, uint32_t shift)
480{
Georgios Pinitas21efeb42017-07-04 12:47:17 +0100481 using namespace fixed_point_arithmetic;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100482
Georgios Pinitas21efeb42017-07-04 12:47:17 +0100483 const int fixed_point_position = in.fixed_point_position();
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100484 for(int i = 0; i < in.num_elements(); ++i)
485 {
Georgios Pinitas21efeb42017-07-04 12:47:17 +0100486 out[i] = static_cast<float>(fixed_point<T1>(in[i], fixed_point_position, true));
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100487 }
488}
489
Pablo Tello91654c42017-07-05 11:32:17 +0100490template < typename T1, typename T2, typename std::enable_if < is_floating_point<T1>::value &&std::is_integral<T2>::value, int >::type = 0 >
Georgios Pinitas21efeb42017-07-04 12:47:17 +0100491void depth_convert(const Tensor<T1> &in, Tensor<T2> &out, ConvertPolicy policy, uint32_t shift)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100492{
Georgios Pinitas21efeb42017-07-04 12:47:17 +0100493 using namespace fixed_point_arithmetic;
494
495 const int fixed_point_position = out.fixed_point_position();
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100496 for(int i = 0; i < in.num_elements(); ++i)
497 {
Georgios Pinitas21efeb42017-07-04 12:47:17 +0100498 out[i] = fixed_point<T2>(in[i], fixed_point_position).raw();
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100499 }
500}
501
Georgios Pinitase2229412017-07-12 12:30:40 +0100502template < typename T1, typename T2, typename std::enable_if < std::is_integral<T1>::value &&std::is_integral<T2>::value &&!std::is_same<T1, T2>::value, int >::type = 0 >
Georgios Pinitas21efeb42017-07-04 12:47:17 +0100503void depth_convert(const Tensor<T1> &in, Tensor<T2> &out, ConvertPolicy policy, uint32_t shift)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100504{
Georgios Pinitas21efeb42017-07-04 12:47:17 +0100505 // Up-casting
506 if(std::numeric_limits<T1>::digits <= std::numeric_limits<T2>::digits)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100507 {
Georgios Pinitas21efeb42017-07-04 12:47:17 +0100508 for(int i = 0; i < in.num_elements(); ++i)
509 {
510 out[i] = static_cast<T2>(in[i]) << shift;
511 }
512 }
513 // Down-casting
514 else
515 {
516 for(int i = 0; i < in.num_elements(); ++i)
517 {
518 T1 val = in[i] >> shift;
519 out[i] = ((policy == ConvertPolicy::SATURATE) ? saturate_cast<T2>(val) : static_cast<T2>(val));
520 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100521 }
522}
523
Georgios Pinitase2229412017-07-12 12:30:40 +0100524template < typename T1, typename T2, typename std::enable_if < std::is_integral<T1>::value &&std::is_integral<T2>::value &&std::is_same<T1, T2>::value, int >::type = 0 >
525void depth_convert(const Tensor<T1> &in, Tensor<T2> &out, ConvertPolicy policy, uint32_t shift)
526{
527 using namespace fixed_point_arithmetic;
528 bool is_in_place = (&in == &out);
529
530 const int fixed_point_position_in = in.fixed_point_position();
531 const int fixed_point_position_out = (is_in_place) ? static_cast<int>(shift) : out.fixed_point_position();
532
533 if(!is_in_place || (fixed_point_position_in != fixed_point_position_out))
534 {
535 for(int i = 0; i < in.num_elements(); ++i)
536 {
537 auto x = fixed_point<T2>(in[i], fixed_point_position_in, true);
538 x.rescale(fixed_point_position_out);
539 out[i] = x.raw();
540 }
541 }
542}
543
Pablo Tello331fc742017-07-06 11:47:06 +0100544template < typename T1, typename T2, typename std::enable_if < is_floating_point<T1>::value &&is_floating_point<T2>::value, int >::type = 0 >
Georgios Pinitas21efeb42017-07-04 12:47:17 +0100545void depth_convert(const Tensor<T1> &in, Tensor<T2> &out, ConvertPolicy policy, uint32_t shift)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100546{
547 for(int i = 0; i < in.num_elements(); ++i)
548 {
Georgios Pinitas21efeb42017-07-04 12:47:17 +0100549 out[i] = static_cast<T2>(in[i]);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100550 }
551}
552
SiCong Li5a536642017-06-19 14:47:05 +0100553// Gaussian3x3 filter
554template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
555void gaussian3x3(const Tensor<T> &in, Tensor<T> &out, BorderMode border_mode, T constant_border_value)
556{
557 const std::array<T, 9> filter{ { 1, 2, 1, 2, 4, 2, 1, 2, 1 } };
558 const float scale = 1.f / 16.f;
559 for(int element_idx = 0; element_idx < in.num_elements(); ++element_idx)
560 {
561 const Coordinates id = index2coord(in.shape(), element_idx);
562 apply_2d_spatial_filter(id, in, out, TensorShape(3U, 3U), filter.data(), scale, border_mode, constant_border_value);
563 }
564}
565
SiCong Li3eb263e2017-06-19 15:31:43 +0100566// Gaussian5x5 filter
567template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
568void gaussian5x5(const Tensor<T> &in, Tensor<T> &out, BorderMode border_mode, T constant_border_value)
569{
570 const std::array<T, 25> filter{ {
571 1, 4, 6, 4, 1,
572 4, 16, 24, 16, 4,
573 6, 24, 36, 24, 6,
574 4, 16, 24, 16, 4,
575 1, 4, 6, 4, 1
576 } };
577 const float scale = 1.f / 256.f;
578 for(int element_idx = 0; element_idx < in.num_elements(); ++element_idx)
579 {
580 const Coordinates id = index2coord(in.shape(), element_idx);
581 apply_2d_spatial_filter(id, in, out, TensorShape(5U, 5U), filter.data(), scale, border_mode, constant_border_value);
582 }
583}
584
Isabella Gottardi3b77e9d2017-06-22 11:05:41 +0100585// Non linear filter
586template <typename T>
587void non_linear_filter(const Tensor<T> &in, Tensor<T> &out, NonLinearFilterFunction function, unsigned int mask_size,
588 MatrixPattern pattern, const uint8_t *mask, BorderMode border_mode, uint8_t constant_border_value)
589{
SiCong Li7a035752017-06-28 15:27:02 +0100590 ARM_COMPUTE_ERROR_ON(pattern == MatrixPattern::OTHER && mask == nullptr);
Isabella Gottardi3b77e9d2017-06-22 11:05:41 +0100591
592 using intermediate_type = typename common_promoted_signed_type<T>::intermediate_type;
593
594 const int sq_mask_size = mask_size * mask_size;
595 const int half_mask_size = mask_size / 2;
596 std::vector<intermediate_type> vals(sq_mask_size);
597 intermediate_type current_value = 0;
598
SiCong Li7a035752017-06-28 15:27:02 +0100599 const ValidRegion valid_region = shape_to_valid_region(in.shape(), border_mode == BorderMode::UNDEFINED, BorderSize(half_mask_size));
Isabella Gottardi3b77e9d2017-06-22 11:05:41 +0100600
601 for(int element_idx = 0, count = 0, index = 0; element_idx < in.num_elements(); ++element_idx, count = 0, index = 0)
602 {
603 Coordinates id = index2coord(in.shape(), element_idx);
604 if(is_in_valid_region(valid_region, id))
605 {
606 int idx = id.x();
607 int idy = id.y();
608 for(int y = idy - half_mask_size; y <= idy + half_mask_size; ++y)
609 {
610 for(int x = idx - half_mask_size; x <= idx + half_mask_size; ++x, ++index)
611 {
612 id.set(0, x);
613 id.set(1, y);
614 current_value = tensor_elem_at(in, id, border_mode, constant_border_value);
615
616 if(mask[index] == 255)
617 {
618 vals[count] = static_cast<intermediate_type>(current_value);
619 ++count;
620 }
621 }
622 }
623 std::sort(vals.begin(), vals.begin() + count);
624 switch(function)
625 {
626 case NonLinearFilterFunction::MIN:
627 out[element_idx] = saturate_cast<T>(vals[0]);
628 break;
629 case NonLinearFilterFunction::MAX:
630 out[element_idx] = saturate_cast<T>(vals[count - 1]);
631 break;
632 case NonLinearFilterFunction::MEDIAN:
633 out[element_idx] = saturate_cast<T>(vals[count / 2]);
634 break;
635 default:
636 ARM_COMPUTE_ERROR("Unsupported NonLinearFilter function.");
637 }
638 }
639 }
640}
641
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100642// Pixel-wise multiplication
643template <typename T1, typename T2, typename T3>
644void pixel_wise_multiplication(const Tensor<T1> &in1, const Tensor<T2> &in2, Tensor<T3> &out, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy)
645{
646 if(scale < 0)
647 {
648 ARM_COMPUTE_ERROR("Scale of pixel-wise multiplication must be non-negative");
649 }
650 using intermediate_type = typename common_promoted_signed_type<T1, T2, T3>::intermediate_type;
651 for(int i = 0; i < in1.num_elements(); ++i)
652 {
653 double val = static_cast<intermediate_type>(in1[i]) * static_cast<intermediate_type>(in2[i]) * static_cast<double>(scale);
Pablo Tello383deec2017-06-23 10:40:05 +0100654 if(is_floating_point<T3>::value)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100655 {
656 out[i] = val;
657 }
658 else
659 {
660 double rounded_val = 0;
661 switch(rounding_policy)
662 {
663 case(RoundingPolicy::TO_ZERO):
Moritz Pflanzerd0ae8b82017-06-29 14:51:57 +0100664 rounded_val = support::cpp11::trunc(val);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100665 break;
666 case(RoundingPolicy::TO_NEAREST_UP):
Moritz Pflanzerd0ae8b82017-06-29 14:51:57 +0100667 rounded_val = round_half_up(val);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100668 break;
669 case(RoundingPolicy::TO_NEAREST_EVEN):
Moritz Pflanzerd0ae8b82017-06-29 14:51:57 +0100670 rounded_val = round_half_even(val);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100671 break;
672 default:
673 ARM_COMPUTE_ERROR("Unsupported rounding policy");
674 }
675 out[i] = (convert_policy == ConvertPolicy::SATURATE) ? saturate_cast<T3>(rounded_val) : static_cast<T3>(rounded_val);
676 }
677 }
678}
679
680// Fixed-point Pixel-wise Multiplication
681template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
Michele Di Giorgio1b80b6c2017-07-17 15:06:34 +0100682void fixed_point_pixel_wise_multiplication(const Tensor<T> &in1, const Tensor<T> &in2, Tensor<T> &out, float scale, ConvertPolicy convert_policy, RoundingPolicy rounding_policy)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100683{
684 using namespace fixed_point_arithmetic;
685
686 const int fixed_point_position = in1.fixed_point_position();
687
688 ARM_COMPUTE_ERROR_ON_MSG(in1.data_type() != in2.data_type() || in1.data_type() != out.data_type(),
689 "Tensors must all have the same DataType");
690 ARM_COMPUTE_ERROR_ON_MSG(fixed_point_position != in2.fixed_point_position() || fixed_point_position != out.fixed_point_position(),
691 "Fixed-point position must be the same for both inputs and outputs");
692
693 // Validate fixed_point_position
694 ARM_COMPUTE_ERROR_ON((in1.data_type() == DataType::QS8) && (fixed_point_position == 0 || fixed_point_position > 7));
695 ARM_COMPUTE_ERROR_ON((in1.data_type() == DataType::QS16) && (fixed_point_position == 0 || fixed_point_position > 15));
696
Michele Di Giorgio1b80b6c2017-07-17 15:06:34 +0100697 const fixed_point<T> fp_scale(scale, fixed_point_position);
698 const bool is_sat = convert_policy == ConvertPolicy::SATURATE;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100699
700 for(int i = 0; i < in1.num_elements(); ++i)
701 {
Michele Di Giorgio1b80b6c2017-07-17 15:06:34 +0100702 const fixed_point<T> val1(in1[i], fixed_point_position, true);
703 fixed_point<T> res(in2[i], fixed_point_position, true);
704 if(is_sat)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100705 {
Michele Di Giorgio1b80b6c2017-07-17 15:06:34 +0100706 res = mul(mul(res, val1), fp_scale);
707 }
708 else
709 {
710 res = mul<OverflowPolicy::WRAP>(mul<OverflowPolicy::WRAP>(res, val1), fp_scale);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100711 }
712 out[i] = res.raw();
713 }
714}
715
Isabella Gottardib797fa22017-06-23 15:02:11 +0100716//Table Lookup
717template <typename T, typename T1>
718void table_lookup(const Tensor<T> &in, Tensor<T> &out, std::map<T1, T1> &lut)
719{
720 for(int i = 0; i < in.num_elements(); ++i)
721 {
722 out[i] = static_cast<T>(lut[in[i]]);
723 }
724}
725
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100726// Threshold
727template <typename T>
728void threshold(const Tensor<T> &in, Tensor<T> &out, uint8_t threshold, uint8_t false_value, uint8_t true_value, ThresholdType type, uint8_t upper)
729{
730 switch(type)
731 {
732 case ThresholdType::BINARY:
733 for(int i = 0; i < in.num_elements(); ++i)
734 {
735 out[i] = ((in[i] > threshold) ? true_value : false_value);
736 }
737 break;
738 case ThresholdType::RANGE:
739 for(int i = 0; i < in.num_elements(); ++i)
740 {
741 if(in[i] > upper)
742 {
743 out[i] = false_value;
744 }
745 else if(in[i] < threshold)
746 {
747 out[i] = false_value;
748 }
749 else
750 {
751 out[i] = true_value;
752 }
753 }
754 break;
755 default:
756 ARM_COMPUTE_ERROR("Thresholding type not recognised");
757 break;
758 }
759}
760
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100761// Batch Normalization Layer for fixed point type
762template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type * = nullptr>
763void batch_normalization_layer(const Tensor<T> &in, Tensor<T> &out, const Tensor<T> &mean, const Tensor<T> &var, const Tensor<T> &beta, const Tensor<T> &gamma, float epsilon, int fixed_point_position)
764{
765 const int cols = static_cast<int>(in.shape()[0]);
766 const int rows = static_cast<int>(in.shape()[1]);
767 const int depth = static_cast<int>(in.shape()[2]);
768 int upper_dims = in.shape().total_size() / (cols * rows * depth);
769
770 for(int r = 0; r < upper_dims; ++r)
771 {
772 for(int i = 0; i < depth; ++i)
773 {
774 for(int k = 0; k < rows; ++k)
775 {
776 for(int l = 0; l < cols; ++l)
777 {
778 const int pos = l + k * cols + i * rows * cols + r * cols * rows * depth;
Michalis Spyrou172e5702017-06-26 14:18:47 +0100779 fixed_point_arithmetic::fixed_point<T> in_qs(in[pos], fixed_point_position, true);
780 fixed_point_arithmetic::fixed_point<T> var_qs(var[i], fixed_point_position, true);
781 fixed_point_arithmetic::fixed_point<T> mean_qs(mean[i], fixed_point_position, true);
782 fixed_point_arithmetic::fixed_point<T> beta_qs(beta[i], fixed_point_position, true);
783 fixed_point_arithmetic::fixed_point<T> gamma_qs(gamma[i], fixed_point_position, true);
784 fixed_point_arithmetic::fixed_point<T> epsilon_qs(epsilon, fixed_point_position);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100785
Michalis Spyrou172e5702017-06-26 14:18:47 +0100786 auto denominator = fixed_point_arithmetic::inv_sqrt(var_qs + epsilon_qs);
787 auto numerator = in_qs - mean_qs;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100788 auto x_bar = numerator * denominator;
Michalis Spyrou172e5702017-06-26 14:18:47 +0100789 x_bar = beta_qs + x_bar * gamma_qs;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100790 out[pos] = x_bar.raw();
791 }
792 }
793 }
794 }
795}
796
797// Batch Normalization Layer for floating point type
Pablo Tello383deec2017-06-23 10:40:05 +0100798template <typename T, typename std::enable_if<is_floating_point<T>::value, int>::type * = nullptr>
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100799void batch_normalization_layer(const Tensor<T> &in, Tensor<T> &out, const Tensor<T> &mean, const Tensor<T> &var, const Tensor<T> &beta, const Tensor<T> &gamma, float epsilon, int fixed_point_position)
800{
801 const int cols = static_cast<int>(in.shape()[0]);
802 const int rows = static_cast<int>(in.shape()[1]);
803 const int depth = static_cast<int>(in.shape()[2]);
804 int upper_dims = in.shape().total_size() / (cols * rows * depth);
805
806 for(int r = 0; r < upper_dims; ++r)
807 {
808 for(int i = 0; i < depth; ++i)
809 {
810 for(int k = 0; k < rows; ++k)
811 {
812 for(int l = 0; l < cols; ++l)
813 {
814 const int pos = l + k * cols + i * rows * cols + r * cols * rows * depth;
815 const float denominator = sqrt(var[i] + epsilon);
816 const float numerator = in[pos] - mean[i];
817 const float x_bar = numerator / denominator;
818 out[pos] = beta[i] + x_bar * gamma[i];
819 }
820 }
821 }
822 }
823}
824
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100825// Fully connected layer
826template <typename T>
827void fully_connected_layer(const Tensor<T> &in, const Tensor<T> &weights, const Tensor<T> &bias, Tensor<T> &out)
828{
829 ARM_COMPUTE_ERROR_ON(weights.shape().x() != out.shape().x());
830 ARM_COMPUTE_ERROR_ON(weights.shape().y() != in.shape().x() * in.shape().y() * in.shape().z());
831 const int cols_weights = weights.shape().x();
832 const int rows_weights = weights.shape().y();
833 const int num_batches = in.shape().total_size() / rows_weights;
834
835 for(int k = 0; k < num_batches; ++k)
836 {
837 vector_matrix_multiply<T>(in.data() + k * rows_weights,
838 weights.data(),
839 bias.data(),
840 out.data() + k * cols_weights,
841 cols_weights,
842 rows_weights,
843 in.fixed_point_position());
844 }
845}
846
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100847// Pooling layer
848template <typename T>
849void pooling_layer(const Tensor<T> &in, Tensor<T> &out, PoolingLayerInfo pool_info, int fixed_point_position)
850{
851 const int pool_size = pool_info.pool_size();
852 PoolingType type = pool_info.pool_type();
853 int pool_stride_x = 0;
854 int pool_stride_y = 0;
855 int pad_x = 0;
856 int pad_y = 0;
857 std::tie(pool_stride_x, pool_stride_y) = pool_info.pad_stride_info().stride();
858 std::tie(pad_x, pad_y) = pool_info.pad_stride_info().pad();
859
Georgios Pinitasce093142017-06-19 16:11:53 +0100860 const int w_in = static_cast<int>(in.shape()[0]);
861 const int h_in = static_cast<int>(in.shape()[1]);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100862
Georgios Pinitasce093142017-06-19 16:11:53 +0100863 const int w_out = static_cast<int>(out.shape()[0]);
864 const int h_out = static_cast<int>(out.shape()[1]);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100865
Georgios Pinitasce093142017-06-19 16:11:53 +0100866 int upper_dims = in.shape().total_size() / (w_in * h_in);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100867
Georgios Pinitasce093142017-06-19 16:11:53 +0100868 int pooled_w = 0;
869 int pooled_h = 0;
870 if(pool_info.pad_stride_info().round() == DimensionRoundingType::CEIL)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100871 {
Georgios Pinitasce093142017-06-19 16:11:53 +0100872 pooled_w = static_cast<int>(ceil(static_cast<float>(w_in + 2 * pad_x - pool_size) / pool_stride_x)) + 1;
873 pooled_h = static_cast<int>(ceil(static_cast<float>(h_in + 2 * pad_y - pool_size) / pool_stride_y)) + 1;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100874 }
Georgios Pinitasce093142017-06-19 16:11:53 +0100875 else
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100876 {
Georgios Pinitasce093142017-06-19 16:11:53 +0100877 pooled_w = static_cast<int>(floor(static_cast<float>(w_in + 2 * pad_x - pool_size) / pool_stride_x)) + 1;
878 pooled_h = static_cast<int>(floor(static_cast<float>(h_in + 2 * pad_y - pool_size) / pool_stride_y)) + 1;
879 }
880
881 if((pooled_w - 1) * pool_stride_x >= w_in + pad_x)
882 {
883 --pooled_w;
884 }
885 if((pooled_h - 1) * pool_stride_y >= h_in + pad_y)
886 {
887 --pooled_h;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100888 }
889
890 if(type == PoolingType::MAX)
891 {
892 for(int r = 0; r < upper_dims; ++r)
893 {
Georgios Pinitasce093142017-06-19 16:11:53 +0100894 for(int h = 0; h < pooled_h; ++h)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100895 {
Georgios Pinitasce093142017-06-19 16:11:53 +0100896 for(int w = 0; w < pooled_w; ++w)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100897 {
Georgios Pinitasce093142017-06-19 16:11:53 +0100898 int wstart = w * pool_stride_x - pad_x;
899 int hstart = h * pool_stride_y - pad_y;
900 int wend = std::min(wstart + pool_size, w_in);
901 int hend = std::min(hstart + pool_size, h_in);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100902 wstart = std::max(wstart, 0);
Georgios Pinitasce093142017-06-19 16:11:53 +0100903 hstart = std::max(hstart, 0);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100904
905 T max_val = std::numeric_limits<T>::lowest();
906 for(int y = hstart; y < hend; ++y)
907 {
908 for(int x = wstart; x < wend; ++x)
909 {
Pablo Tello0c34fe22017-06-26 17:17:42 +0100910 const T val = in[r * h_in * w_in + y * w_in + x];
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100911 if(val > max_val)
912 {
913 max_val = val;
914 }
915 }
916 }
917
Georgios Pinitasce093142017-06-19 16:11:53 +0100918 out[r * h_out * w_out + h * pooled_w + w] = max_val;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100919 }
920 }
921 }
922 }
923 else // Average pooling
924 {
925 for(int r = 0; r < upper_dims; ++r)
926 {
Georgios Pinitasce093142017-06-19 16:11:53 +0100927 for(int h = 0; h < pooled_h; ++h)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100928 {
Georgios Pinitasce093142017-06-19 16:11:53 +0100929 for(int w = 0; w < pooled_w; ++w)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100930 {
Moritz Pflanzere49e2662017-07-21 15:55:28 +0100931 T avg_val(0);
932 int wstart = w * pool_stride_x - pad_x;
933 int hstart = h * pool_stride_y - pad_y;
934 int wend = std::min(wstart + pool_size, w_in + pad_x);
935 int hend = std::min(hstart + pool_size, h_in + pad_y);
936 int pool = (hend - hstart) * (wend - wstart);
937 wstart = std::max(wstart, 0);
938 hstart = std::max(hstart, 0);
939 wend = std::min(wend, w_in);
940 hend = std::min(hend, h_in);
Pablo Tello383deec2017-06-23 10:40:05 +0100941 if(is_floating_point<T>::value)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100942 {
943 for(int y = hstart; y < hend; ++y)
944 {
945 for(int x = wstart; x < wend; ++x)
946 {
Georgios Pinitasce093142017-06-19 16:11:53 +0100947 avg_val += in[r * h_in * w_in + y * w_in + x];
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100948 }
949 }
Georgios Pinitasce093142017-06-19 16:11:53 +0100950 out[r * h_out * w_out + h * pooled_w + w] = avg_val / pool;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100951 }
952 else
953 {
954 static std::array<qint8_t, 10> scale_values_q8 =
955 { { 0x0, 0x0, 0x40, 0x2A, 0x20, 0x19, 0x15, 0x12, 0x10, 0xE } };
956
957 for(int y = hstart; y < hend; ++y)
958 {
959 for(int x = wstart; x < wend; ++x)
960 {
Georgios Pinitasce093142017-06-19 16:11:53 +0100961 avg_val = sqadd_qs8(avg_val, in[r * h_in * w_in + y * w_in + x]);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100962 }
963 }
Georgios Pinitasce093142017-06-19 16:11:53 +0100964 out[r * h_out * w_out + h * pooled_w + w] = sqmul_qs8(avg_val, (scale_values_q8[pool] >> (7 - fixed_point_position)), fixed_point_position);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100965 }
966 }
967 }
968 }
969 }
970}
971
Georgios Pinitas7b7858d2017-06-21 16:44:24 +0100972// Pooling layer
973template <typename T>
974void roi_pooling_layer(const Tensor<T> &in, Tensor<T> &out, const std::vector<ROI> &rois, const ROIPoolingLayerInfo &pool_info)
975{
976 const int num_rois = rois.size();
977 const int width_in = in.shape().x();
978 const int height_in = in.shape().y();
979 const int fms = in.shape().z();
980 const int volume_in = width_in * height_in * fms;
981 const int pool_w = pool_info.pooled_width();
982 const int pool_h = pool_info.pooled_height();
983 const int volume_out = pool_w * pool_h * fms;
984 const float roi_scale = pool_info.spatial_scale();
985
986 // Iterate through all rois
987 for(int roi_idx = 0; roi_idx < num_rois; ++roi_idx)
988 {
989 // Get dimensions of current ROI
990 const ROI &roi = rois[roi_idx];
991
992 int batch_id = roi.batch_idx;
993 int roi_start_x = support::cpp11::round(roi.rect.x * roi_scale);
994 int roi_start_y = support::cpp11::round(roi.rect.y * roi_scale);
995 int roi_width = std::max(support::cpp11::round(roi.rect.width * roi_scale), 1.f);
996 int roi_height = std::max(support::cpp11::round(roi.rect.height * roi_scale), 1.f);
997
998 // Determine pooling regions
999 float pool_region_size_x = static_cast<float>(roi_width) / pool_w;
1000 float pool_region_size_y = static_cast<float>(roi_height) / pool_h;
1001
1002 // Iterate through all channel
1003 for(int fm = 0; fm < fms; ++fm)
1004 {
1005 // Calculate each output pixel
1006 for(int py = 0; py < pool_h; ++py)
1007 {
1008 for(int px = 0; px < pool_w; ++px)
1009 {
1010 int region_start_x = static_cast<int>(std::floor(px * pool_region_size_x));
1011 int region_end_x = static_cast<int>(std::ceil((px + 1) * pool_region_size_x));
1012 int region_start_y = static_cast<int>(std::floor(py * pool_region_size_y));
1013 int region_end_y = static_cast<int>(std::ceil((py + 1) * pool_region_size_y));
1014
1015 region_start_x = std::min(std::max(region_start_x + roi_start_x, 0), width_in);
1016 region_end_x = std::min(std::max(region_end_x + roi_start_x, 0), width_in);
1017 region_start_y = std::min(std::max(region_start_y + roi_start_y, 0), height_in);
1018 region_end_y = std::min(std::max(region_end_y + roi_start_y, 0), height_in);
1019
1020 // Iterate through each pixel in the pooling region
1021 if((region_end_x <= region_start_x) || (region_end_y <= region_start_y))
1022 {
1023 out[roi_idx * volume_out + fm * pool_w * pool_h + py * pool_w + px] = 0;
1024 }
1025 else
1026 {
1027 T curr_max = std::numeric_limits<T>::lowest();
1028 for(int j = region_start_y; j < region_end_y; ++j)
1029 {
1030 for(int i = region_start_x; i < region_end_x; ++i)
1031 {
1032 const auto val = in[batch_id * volume_in + fm * width_in * height_in + j * width_in + i];
1033 curr_max = std::max(val, curr_max);
1034 }
1035 }
1036 out[roi_idx * volume_out + fm * pool_w * pool_h + py * pool_w + px] = curr_max;
1037 }
1038 }
1039 }
1040 }
1041 }
1042}
1043
Anthony Barbier6ff3b192017-09-04 18:44:23 +01001044// Fixed point operations
1045template <typename T>
1046void fixed_point_operation(const Tensor<T> &in, Tensor<T> &out, FixedPointOp op)
1047{
1048 int p = in.fixed_point_position();
1049 switch(op)
1050 {
1051 case FixedPointOp::EXP:
1052 for(int i = 0; i < in.num_elements(); ++i)
1053 {
1054 out[i] = fixed_point_arithmetic::exp(fixed_point_arithmetic::fixed_point<T>(in[i], p, true)).raw();
1055 }
1056 break;
1057 case FixedPointOp::LOG:
1058 for(int i = 0; i < in.num_elements(); ++i)
1059 {
1060 out[i] = fixed_point_arithmetic::log(fixed_point_arithmetic::fixed_point<T>(in[i], p, true)).raw();
1061 }
1062 break;
1063 case FixedPointOp::INV_SQRT:
1064 for(int i = 0; i < in.num_elements(); ++i)
1065 {
1066 out[i] = fixed_point_arithmetic::inv_sqrt(fixed_point_arithmetic::fixed_point<T>(in[i], p, true)).raw();
1067 }
1068 break;
1069 case FixedPointOp::RECIPROCAL:
1070 for(int i = 0; i < in.num_elements(); ++i)
1071 {
1072 out[i] = fixed_point_arithmetic::div(fixed_point_arithmetic::fixed_point<T>(1, p), fixed_point_arithmetic::fixed_point<T>(in[i], p, true)).raw();
1073 }
1074 break;
1075 default:
1076 ARM_COMPUTE_ERROR("Fixed point operation not supported");
1077 break;
1078 }
1079}
1080
1081// Tensor print
1082template <typename T>
1083void print(const Tensor<T> &in, std::ostream &out)
1084{
1085 out << "\n";
1086 for(int i = 0; i < in.num_elements(); ++i)
1087 {
1088 out << in[i] << " ";
1089 }
1090 out << "\n";
1091}
1092} // namespace tensor_operations
1093} // namespace validation
1094} // namespace test
1095} // namespace arm_compute
1096
1097#endif /* __ARM_COMPUTE_TEST_TENSOR_OPERATIONS_H__ */