blob: c7dc03c8c8ed4d184bbaf55a6b18371f1eb26707 [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#include "arm_compute/core/NEON/kernels/NEMinMaxLocationKernel.h"
25
26#include "arm_compute/core/Coordinates.h"
27#include "arm_compute/core/Error.h"
28#include "arm_compute/core/Helpers.h"
29#include "arm_compute/core/IAccessWindow.h"
30#include "arm_compute/core/ITensor.h"
31#include "arm_compute/core/TensorInfo.h"
32#include "arm_compute/core/Types.h"
33#include "arm_compute/core/Validate.h"
steniu014c2938e2017-06-19 15:44:45 +010034#include "arm_compute/core/Window.h"
Anthony Barbier6ff3b192017-09-04 18:44:23 +010035
steniu014c2938e2017-06-19 15:44:45 +010036#include <algorithm>
Anthony Barbier6ff3b192017-09-04 18:44:23 +010037#include <arm_neon.h>
38#include <climits>
39#include <cstddef>
40
41namespace arm_compute
42{
43NEMinMaxKernel::NEMinMaxKernel()
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +010044 : _func(), _input(nullptr), _min(), _max(), _mtx()
Anthony Barbier6ff3b192017-09-04 18:44:23 +010045{
46}
47
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +010048void NEMinMaxKernel::configure(const IImage *input, void *min, void *max)
Anthony Barbier6ff3b192017-09-04 18:44:23 +010049{
50 ARM_COMPUTE_ERROR_ON_TENSOR_NOT_2D(input);
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +010051 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::U8, DataType::S16, DataType::F32);
Anthony Barbier6ff3b192017-09-04 18:44:23 +010052 ARM_COMPUTE_ERROR_ON(nullptr == min);
53 ARM_COMPUTE_ERROR_ON(nullptr == max);
54
55 _input = input;
56 _min = min;
57 _max = max;
58
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +010059 switch(_input->info()->data_type())
Anthony Barbier6ff3b192017-09-04 18:44:23 +010060 {
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +010061 case DataType::U8:
62 _func = &NEMinMaxKernel::minmax_U8;
Anthony Barbier6ff3b192017-09-04 18:44:23 +010063 break;
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +010064 case DataType::S16:
65 _func = &NEMinMaxKernel::minmax_S16;
66 break;
67 case DataType::F32:
68 _func = &NEMinMaxKernel::minmax_F32;
Anthony Barbier6ff3b192017-09-04 18:44:23 +010069 break;
70 default:
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +010071 ARM_COMPUTE_ERROR("Unsupported data type");
Anthony Barbier6ff3b192017-09-04 18:44:23 +010072 break;
73 }
74
Anthony Barbier6ff3b192017-09-04 18:44:23 +010075 // Configure kernel window
steniu014c2938e2017-06-19 15:44:45 +010076 constexpr unsigned int num_elems_processed_per_iteration = 1;
Anthony Barbier6ff3b192017-09-04 18:44:23 +010077
steniu014c2938e2017-06-19 15:44:45 +010078 Window win = calculate_max_window(*input->info(), Steps(num_elems_processed_per_iteration));
Anthony Barbier6ff3b192017-09-04 18:44:23 +010079
80 INEKernel::configure(win);
81}
82
Moritz Pflanzerc186b572017-09-07 09:48:04 +010083void NEMinMaxKernel::run(const Window &window, const ThreadInfo &info)
Anthony Barbier6ff3b192017-09-04 18:44:23 +010084{
Moritz Pflanzerc186b572017-09-07 09:48:04 +010085 ARM_COMPUTE_UNUSED(info);
Anthony Barbier6ff3b192017-09-04 18:44:23 +010086 ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this);
87 ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(INEKernel::window(), window);
88 ARM_COMPUTE_ERROR_ON(_func == nullptr);
89
90 (this->*_func)(window);
91}
92
93void NEMinMaxKernel::reset()
94{
95 ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this);
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +010096 switch(_input->info()->data_type())
97 {
98 case DataType::U8:
99 *static_cast<int32_t *>(_min) = UCHAR_MAX;
100 *static_cast<int32_t *>(_max) = 0;
101 break;
102 case DataType::S16:
103 *static_cast<int32_t *>(_min) = SHRT_MAX;
104 *static_cast<int32_t *>(_max) = SHRT_MIN;
105 break;
106 case DataType::F32:
107 *static_cast<float *>(_min) = std::numeric_limits<float>::max();
108 *static_cast<float *>(_max) = std::numeric_limits<float>::lowest();
109 break;
110 default:
111 ARM_COMPUTE_ERROR("Unsupported data type");
112 break;
113 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100114}
115
116template <typename T>
117void NEMinMaxKernel::update_min_max(const T min, const T max)
118{
Michalis Spyrou07781ac2017-08-31 15:11:41 +0100119 std::lock_guard<arm_compute::Mutex> lock(_mtx);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100120
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100121 using type = typename std::conditional<std::is_same<T, float>::value, float, int32_t>::type;
122
123 auto min_ptr = static_cast<type *>(_min);
124 auto max_ptr = static_cast<type *>(_max);
125
126 if(min < *min_ptr)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100127 {
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100128 *min_ptr = min;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100129 }
130
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100131 if(max > *max_ptr)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100132 {
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100133 *max_ptr = max;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100134 }
135}
136
steniu014c2938e2017-06-19 15:44:45 +0100137void NEMinMaxKernel::minmax_U8(Window win)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100138{
139 uint8x8_t carry_min = vdup_n_u8(UCHAR_MAX);
140 uint8x8_t carry_max = vdup_n_u8(0);
141
steniu014c2938e2017-06-19 15:44:45 +0100142 uint8_t carry_max_scalar = 0;
143 uint8_t carry_min_scalar = UCHAR_MAX;
144
145 const int x_start = win.x().start();
146 const int x_end = win.x().end();
147
148 // Handle X dimension manually to split into two loops
149 // First one will use vector operations, second one processes the left over pixels
150 win.set(Window::DimX, Window::Dimension(0, 1, 1));
151
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100152 Iterator input(_input, win);
153
154 execute_window_loop(win, [&](const Coordinates & id)
155 {
steniu014c2938e2017-06-19 15:44:45 +0100156 int x = x_start;
157
158 // Vector loop
159 for(; x <= x_end - 16; x += 16)
160 {
161 const uint8x16_t pixels = vld1q_u8(input.ptr() + x);
162 const uint8x8_t tmp_min = vmin_u8(vget_high_u8(pixels), vget_low_u8(pixels));
163 const uint8x8_t tmp_max = vmax_u8(vget_high_u8(pixels), vget_low_u8(pixels));
164 carry_min = vmin_u8(tmp_min, carry_min);
165 carry_max = vmax_u8(tmp_max, carry_max);
166 }
167
168 // Process leftover pixels
169 for(; x < x_end; ++x)
170 {
171 const uint8_t pixel = input.ptr()[x];
172 carry_min_scalar = std::min(pixel, carry_min_scalar);
173 carry_max_scalar = std::max(pixel, carry_max_scalar);
174 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100175 },
176 input);
177
178 // Reduce result
179 carry_min = vpmin_u8(carry_min, carry_min);
180 carry_max = vpmax_u8(carry_max, carry_max);
181 carry_min = vpmin_u8(carry_min, carry_min);
182 carry_max = vpmax_u8(carry_max, carry_max);
183 carry_min = vpmin_u8(carry_min, carry_min);
184 carry_max = vpmax_u8(carry_max, carry_max);
185
186 // Extract max/min values
steniu014c2938e2017-06-19 15:44:45 +0100187 const uint8_t min_i = std::min(vget_lane_u8(carry_min, 0), carry_min_scalar);
188 const uint8_t max_i = std::max(vget_lane_u8(carry_max, 0), carry_max_scalar);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100189
190 // Perform reduction of local min/max values
191 update_min_max(min_i, max_i);
192}
193
steniu014c2938e2017-06-19 15:44:45 +0100194void NEMinMaxKernel::minmax_S16(Window win)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100195{
196 int16x4_t carry_min = vdup_n_s16(SHRT_MAX);
197 int16x4_t carry_max = vdup_n_s16(SHRT_MIN);
198
steniu014c2938e2017-06-19 15:44:45 +0100199 int16_t carry_max_scalar = SHRT_MIN;
200 int16_t carry_min_scalar = SHRT_MAX;
201
202 const int x_start = win.x().start();
203 const int x_end = win.x().end();
204
205 // Handle X dimension manually to split into two loops
206 // First one will use vector operations, second one processes the left over pixels
207 win.set(Window::DimX, Window::Dimension(0, 1, 1));
208
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100209 Iterator input(_input, win);
210
211 execute_window_loop(win, [&](const Coordinates & id)
212 {
steniu014c2938e2017-06-19 15:44:45 +0100213 int x = x_start;
214 const auto in_ptr = reinterpret_cast<const int16_t *const>(input.ptr());
215
216 // Vector loop
217 for(; x <= x_end - 16; x += 16)
218 {
219 const int16x8x2_t pixels = vld2q_s16(in_ptr + x);
220 const int16x8_t tmp_min1 = vminq_s16(pixels.val[0], pixels.val[1]);
221 const int16x8_t tmp_max1 = vmaxq_s16(pixels.val[0], pixels.val[1]);
222 const int16x4_t tmp_min2 = vmin_s16(vget_high_s16(tmp_min1), vget_low_s16(tmp_min1));
223 const int16x4_t tmp_max2 = vmax_s16(vget_high_s16(tmp_max1), vget_low_s16(tmp_max1));
224 carry_min = vmin_s16(tmp_min2, carry_min);
225 carry_max = vmax_s16(tmp_max2, carry_max);
226 }
227
228 // Process leftover pixels
229 for(; x < x_end; ++x)
230 {
231 const int16_t pixel = in_ptr[x];
232 carry_min_scalar = std::min(pixel, carry_min_scalar);
233 carry_max_scalar = std::max(pixel, carry_max_scalar);
234 }
235
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100236 },
237 input);
238
239 // Reduce result
240 carry_min = vpmin_s16(carry_min, carry_min);
241 carry_max = vpmax_s16(carry_max, carry_max);
242 carry_min = vpmin_s16(carry_min, carry_min);
243 carry_max = vpmax_s16(carry_max, carry_max);
244
245 // Extract max/min values
steniu014c2938e2017-06-19 15:44:45 +0100246 const int16_t min_i = std::min(vget_lane_s16(carry_min, 0), carry_min_scalar);
247 const int16_t max_i = std::max(vget_lane_s16(carry_max, 0), carry_max_scalar);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100248
249 // Perform reduction of local min/max values
250 update_min_max(min_i, max_i);
251}
252
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100253void NEMinMaxKernel::minmax_F32(Window win)
254{
255 float32x2_t carry_min = vdup_n_f32(std::numeric_limits<float>::max());
256 float32x2_t carry_max = vdup_n_f32(std::numeric_limits<float>::lowest());
257
258 float carry_min_scalar = std::numeric_limits<float>::max();
259 float carry_max_scalar = std::numeric_limits<float>::lowest();
260
261 const int x_start = win.x().start();
262 const int x_end = win.x().end();
263
264 // Handle X dimension manually to split into two loops
265 // First one will use vector operations, second one processes the left over pixels
266 win.set(Window::DimX, Window::Dimension(0, 1, 1));
267
268 Iterator input(_input, win);
269
270 execute_window_loop(win, [&](const Coordinates & id)
271 {
272 int x = x_start;
273 const auto in_ptr = reinterpret_cast<const float *const>(input.ptr());
274
275 // Vector loop
276 for(; x <= x_end - 8; x += 8)
277 {
278 const float32x4x2_t pixels = vld2q_f32(in_ptr + x);
279 const float32x4_t tmp_min1 = vminq_f32(pixels.val[0], pixels.val[1]);
280 const float32x4_t tmp_max1 = vmaxq_f32(pixels.val[0], pixels.val[1]);
281 const float32x2_t tmp_min2 = vmin_f32(vget_high_f32(tmp_min1), vget_low_f32(tmp_min1));
282 const float32x2_t tmp_max2 = vmax_f32(vget_high_f32(tmp_max1), vget_low_f32(tmp_max1));
283 carry_min = vmin_f32(tmp_min2, carry_min);
284 carry_max = vmax_f32(tmp_max2, carry_max);
285 }
286
287 // Process leftover pixels
288 for(; x < x_end; ++x)
289 {
290 const float pixel = in_ptr[x];
291 carry_min_scalar = std::min(pixel, carry_min_scalar);
292 carry_max_scalar = std::max(pixel, carry_max_scalar);
293 }
294
295 },
296 input);
297
298 // Reduce result
299 carry_min = vpmin_f32(carry_min, carry_min);
300 carry_max = vpmax_f32(carry_max, carry_max);
301 carry_min = vpmin_f32(carry_min, carry_min);
302 carry_max = vpmax_f32(carry_max, carry_max);
303
304 // Extract max/min values
305 const float min_i = std::min(vget_lane_f32(carry_min, 0), carry_min_scalar);
306 const float max_i = std::max(vget_lane_f32(carry_max, 0), carry_max_scalar);
307
308 // Perform reduction of local min/max values
309 update_min_max(min_i, max_i);
310}
311
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100312NEMinMaxLocationKernel::NEMinMaxLocationKernel()
steniu014c2938e2017-06-19 15:44:45 +0100313 : _func(nullptr), _input(nullptr), _min(nullptr), _max(nullptr), _min_count(nullptr), _max_count(nullptr), _min_loc(nullptr), _max_loc(nullptr)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100314{
315}
316
317bool NEMinMaxLocationKernel::is_parallelisable() const
318{
319 return false;
320}
321
322template <unsigned int...>
323struct index_seq
324{
325 index_seq() = default;
326 index_seq(const index_seq &) = default;
327 index_seq &operator=(const index_seq &) = default;
328 index_seq(index_seq &&) noexcept = default;
329 index_seq &operator=(index_seq &&) noexcept = default;
330 virtual ~index_seq() = default;
331};
332template <unsigned int N, unsigned int... S>
333struct gen_index_seq : gen_index_seq < N - 1, N - 1, S... >
334{
335};
336template <unsigned int... S>
337struct gen_index_seq<0u, S...> : index_seq<S...>
338{
339 using type = index_seq<S...>;
340};
341
342template <class T, unsigned int... N>
343struct NEMinMaxLocationKernel::create_func_table<T, index_seq<N...>>
344{
345 static const NEMinMaxLocationKernel::MinMaxLocFunction func_table[sizeof...(N)];
346};
347
348template <class T, unsigned int... N>
349const NEMinMaxLocationKernel::MinMaxLocFunction NEMinMaxLocationKernel::create_func_table<T, index_seq<N...>>::func_table[sizeof...(N)] =
350{
351 &NEMinMaxLocationKernel::minmax_loc<T, bool(N & 8), bool(N & 4), bool(N & 2), bool(N & 1)>...
352};
353
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100354void NEMinMaxLocationKernel::configure(const IImage *input, void *min, void *max,
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100355 ICoordinates2DArray *min_loc, ICoordinates2DArray *max_loc,
356 uint32_t *min_count, uint32_t *max_count)
357{
358 ARM_COMPUTE_ERROR_ON_TENSOR_NOT_2D(input);
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100359 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::U8, DataType::S16, DataType::F32);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100360 ARM_COMPUTE_ERROR_ON(nullptr == min);
361 ARM_COMPUTE_ERROR_ON(nullptr == max);
362
363 _input = input;
364 _min = min;
365 _max = max;
366 _min_count = min_count;
367 _max_count = max_count;
368 _min_loc = min_loc;
369 _max_loc = max_loc;
370
371 unsigned int count_min = (nullptr != min_count ? 1 : 0);
372 unsigned int count_max = (nullptr != max_count ? 1 : 0);
373 unsigned int loc_min = (nullptr != min_loc ? 1 : 0);
374 unsigned int loc_max = (nullptr != max_loc ? 1 : 0);
375
376 unsigned int table_idx = (count_min << 3) | (count_max << 2) | (loc_min << 1) | loc_max;
377
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100378 switch(input->info()->data_type())
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100379 {
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100380 case DataType::U8:
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100381 _func = create_func_table<uint8_t, gen_index_seq<16>::type>::func_table[table_idx];
382 break;
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100383 case DataType::S16:
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100384 _func = create_func_table<int16_t, gen_index_seq<16>::type>::func_table[table_idx];
385 break;
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100386 case DataType::F32:
387 _func = create_func_table<float, gen_index_seq<16>::type>::func_table[table_idx];
388 break;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100389 default:
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100390 ARM_COMPUTE_ERROR("Unsupported data type");
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100391 break;
392 }
393
steniu014c2938e2017-06-19 15:44:45 +0100394 constexpr unsigned int num_elems_processed_per_iteration = 1;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100395
396 // Configure kernel window
steniu014c2938e2017-06-19 15:44:45 +0100397 Window win = calculate_max_window(*input->info(), Steps(num_elems_processed_per_iteration));
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100398
steniu014c2938e2017-06-19 15:44:45 +0100399 update_window_and_padding(win, AccessWindowHorizontal(input->info(), 0, num_elems_processed_per_iteration));
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100400
401 INEKernel::configure(win);
402}
403
Moritz Pflanzerc186b572017-09-07 09:48:04 +0100404void NEMinMaxLocationKernel::run(const Window &window, const ThreadInfo &info)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100405{
Moritz Pflanzerc186b572017-09-07 09:48:04 +0100406 ARM_COMPUTE_UNUSED(info);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100407 ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this);
408 ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(INEKernel::window(), window);
409 ARM_COMPUTE_ERROR_ON(_func == nullptr);
410
411 (this->*_func)(window);
412}
413
414template <class T, bool count_min, bool count_max, bool loc_min, bool loc_max>
415void NEMinMaxLocationKernel::minmax_loc(const Window &win)
416{
417 if(count_min || count_max || loc_min || loc_max)
418 {
419 Iterator input(_input, win);
420
steniu014c2938e2017-06-19 15:44:45 +0100421 size_t min_count = 0;
422 size_t max_count = 0;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100423
424 // Clear min location array
425 if(loc_min)
426 {
427 _min_loc->clear();
428 }
429
430 // Clear max location array
431 if(loc_max)
432 {
433 _max_loc->clear();
434 }
435
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100436 using type = typename std::conditional<std::is_same<T, float>::value, float, int32_t>::type;
437
438 auto min_ptr = static_cast<type *>(_min);
439 auto max_ptr = static_cast<type *>(_max);
440
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100441 execute_window_loop(win, [&](const Coordinates & id)
442 {
443 auto in_ptr = reinterpret_cast<const T *>(input.ptr());
444 int32_t idx = id.x();
445 int32_t idy = id.y();
446
steniu014c2938e2017-06-19 15:44:45 +0100447 const T pixel = *in_ptr;
448 Coordinates2D p{ idx, idy };
449
450 if(count_min || loc_min)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100451 {
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100452 if(*min_ptr == pixel)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100453 {
steniu014c2938e2017-06-19 15:44:45 +0100454 if(count_min)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100455 {
steniu014c2938e2017-06-19 15:44:45 +0100456 ++min_count;
457 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100458
steniu014c2938e2017-06-19 15:44:45 +0100459 if(loc_min)
460 {
461 _min_loc->push_back(p);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100462 }
463 }
steniu014c2938e2017-06-19 15:44:45 +0100464 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100465
steniu014c2938e2017-06-19 15:44:45 +0100466 if(count_max || loc_max)
467 {
Michele Di Giorgioef4b4ae2017-07-04 17:19:43 +0100468 if(*max_ptr == pixel)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100469 {
steniu014c2938e2017-06-19 15:44:45 +0100470 if(count_max)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100471 {
steniu014c2938e2017-06-19 15:44:45 +0100472 ++max_count;
473 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100474
steniu014c2938e2017-06-19 15:44:45 +0100475 if(loc_max)
476 {
477 _max_loc->push_back(p);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100478 }
479 }
480 }
481 },
482 input);
483
484 if(count_min)
485 {
486 *_min_count = min_count;
487 }
488
489 if(count_max)
490 {
491 *_max_count = max_count;
492 }
493 }
494}
495} // namespace arm_compute