Ocean
FrameMinMax.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) Meta Platforms, Inc. and affiliates.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  */
7 
8 #ifndef META_OCEAN_CV_FRAME_MIN_MAX_H
9 #define META_OCEAN_CV_FRAME_MIN_MAX_H
10 
11 #include "ocean/cv/CV.h"
12 #include "ocean/cv/PixelPosition.h"
13 
14 #include "ocean/base/Frame.h"
15 #include "ocean/base/Worker.h"
16 
17 #include <limits>
18 
19 namespace Ocean
20 {
21 
22 namespace CV
23 {
24 
25 /**
26  * This class implements functions allowing to determine minimum and maximum values within frames.
27  * @ingroup cv
28  */
29 class OCEAN_CV_EXPORT FrameMinMax
30 {
31  protected:
32 
33  /**
34  * This class allows to determine the extremum (the global minimum or maximum) within a given frame.
35  * The application of this helper class allows to simplify the implementation while providing fast performance,<br>
36  * as it allows to specialize the implementation for the data type 'T' independently from 'tDetermineMinimum'.
37  * @tparam T The data type of each pixel e.g., 'unsigned char', 'int', 'float'
38  */
39  template <typename T>
41  {
42  public:
43 
44  /**
45  * Determines the extremum minimum value (either the global minimum or the global maximum) within a given frame with one channel.
46  * In case several locations with same value exist, one of them will be returned.<br>
47  * This function supports a padding at the end of each row, in case a padding is specified the actual memory must have size: (width + paddingElements) * sizeof(T) * height.
48  * @param frame The pointer to the first pixel of the frame, must be valid
49  * @param width The width of the given frame in pixel, with range [1, infinity)
50  * @param height The height of the given frame in pixel, with range [1, infinity)
51  * @param framePaddingElements The number of padding elements at the end of each frame row, in elements, with range [0, infinity)
52  * @param extremumValue Optional resulting extremum value (minimal or maximal) found within the frame, 'nullptr' otherwise
53  * @param extremumLocation Optional resulting position where the extremum value (minimal or maximal) is located, with range [0, width - 1]x[0, height - 1], 'nullptr' otherwise
54  * @tparam tDetermineMinimum True, to seek for the minimum value; False, to seek for the maximum value
55  */
56  template <bool tDetermineMinimum>
57  static void determineExtremumValue(const T* frame, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, T* extremumValue = nullptr, PixelPosition* extremumLocation = nullptr);
58  };
59 
60  public:
61 
62  /**
63  * Determines the minimum value (the global minimum) within a given frame with one channel.
64  * In case several locations with same value exist, one of them will be returned.<br>
65  * This function supports a padding at the end of each row, in case a padding is specified the actual memory must have size: (width + paddingElements) * sizeof(T) * height.<br>
66  * Information: This function is the equivalent to OpenCV's cv::minMaxLoc() - but significantly faster.
67  * @param frame The pointer to the first pixel of the frame, must be valid
68  * @param width The width of the given frame in pixel, with range [1, infinity)
69  * @param height The height of the given frame in pixel, with range [1, infinity)
70  * @param framePaddingElements The number of padding elements at the end of each frame row, in elements, with range [0, infinity)
71  * @param minValue Optional resulting minimal value found within the frame, 'nullptr' otherwise
72  * @param minLocation Optional resulting position where the minimal value is located, with range [0, width - 1]x[0, height - 1], 'nullptr' otherwise
73  * @tparam T The data type of each pixel e.g., 'unsigned char', 'int', 'float'
74  */
75  template <typename T>
76  static void determineMinValue(const T* frame, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, T* minValue = nullptr, PixelPosition* minLocation = nullptr);
77 
78  /**
79  * Determines the maximum value (the peak value) within a given frame with one channel.
80  * In case several locations with same peak value exist, one of them will be returned.<br>
81  * This function supports a padding at the end of each row, in case a padding is specified the actual memory must have size: (width + paddingElements) * sizeof(T) * height.<br>
82  * Information: This function is the equivalent to OpenCV's cv::minMaxLoc() - but significantly faster.
83  * @param frame The pointer to the first pixel of the frame, must be valid
84  * @param width The width of the given frame in pixel, with range [1, infinity)
85  * @param height The height of the given frame in pixel, with range [1, infinity)
86  * @param framePaddingElements The number of padding elements at the end of each frame row, in elements, with range [0, infinity)
87  * @param maxValue Optional resulting maximal value found within the frame, 'nullptr' otherwise
88  * @param maxLocation Optional resulting position where the maximal value is located, with range [0, width - 1]x[0, height - 1], 'nullptr' otherwise
89  * @tparam T The data type of each pixel e.g., 'unsigned char', 'int', 'float'
90  */
91  template <typename T>
92  static void determineMaxValue(const T* frame, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, T* maxValue = nullptr, PixelPosition* maxLocation = nullptr);
93 
94  /**
95  * Determines the minimal and maximal pixel values in a given frame.
96  * In case the frame has multiple channels, minimal and maximal values are determined for each channel individually.
97  * @param frame The frame for which the minimal and maximal pixel values are determined, must be valid
98  * @param width The width of the frame in pixel, with range [1, infinity)
99  * @param height The height of the frame in pixel, with range [1, infinity)
100  * @param framePaddingElements The number of padding elements at the end of each frame row, in elements, with range [0, infinity)
101  * @param minimalValues Resulting minimal values, one for each channel, must be valid
102  * @param maximalValues Resulting maximal values, one for each channel, must be valid
103  * @param worker Optional worker object to distribute the computation
104  * @tparam T Data type of each pixel color value (per channel)
105  * @tparam tChannels Number of channels of the frame, with range [1, infinity)
106  * @tparam tIgnoreInfinity True, to ignore +/- infinity and NaN float values; False, to consider +/- infinity float as minimum and maximum values as well, beahvior with NaN values is undefined
107  */
108  template <typename T, unsigned int tChannels, bool tIgnoreInfinity = false>
109  static inline void determineMinMaxValues(const T* frame, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, T* minimalValues, T* maximalValues, Worker* worker = nullptr);
110 
111  /**
112  * Counts frame elements in a 1-channel frame that are outside a specified range of values
113  * @param frame The pointer to the first pixel of the frame, must be valid
114  * @param width The width of the given frame in pixel, with range [1, infinity)
115  * @param height The height of the given frame in pixel, with range [1, infinity)
116  * @param framePaddingElements The number of padding elements at the end of each frame row, in elements, with range [0, infinity)
117  * @param rangeStart Start value of the range, range: [lowest<T>(), rangeEnd]
118  * @param rangeEnd Exclusive end value of the range, range: [rangeStart, max<T>()]
119  * @param elementsBelowRange Optional resulting number of elements with values `< rangeStart`
120  * @param elementsAboveRange Optional resulting number of elements with values `>= rangeEnd`
121  * @return True on success, otherwise false
122  */
123  template <typename T>
124  static bool countElementsOutsideRange(const T* frame, const uint32_t width, const uint32_t height, const uint32_t framePaddingElements, const T rangeStart, const T rangeEnd, uint32_t* elementsBelowRange = nullptr, uint32_t* elementsAboveRange = nullptr);
125 
126  protected:
127 
128  /**
129  * Determines the minimal and maximal pixel values in a subset of a given frame.
130  * @param frame The frame for which the minimal and maximal pixel values are determined, must be valid
131  * @param width The width of the frame in pixel, with range [1, infinity)
132  * @param height The height of the frame in pixel, with range [1, infinity)
133  * @param minimalValues Resulting minimal values
134  * @param maximalValues Resulting maximal values
135  * @param lock Optional lock if this function is executed distributed within several threads
136  * @param framePaddingElements The number of padding elements at the end of each frame row, in elements, with range [0, infinity)
137  * @param firstRow First row to be handled, with range [0, height - 1]
138  * @param numberRows Number of rows to be handled, with range [1, height - firstRow]
139  * @tparam T Data type of each pixel color value (per channel)
140  * @tparam tChannels Number of channels of the frame, with range [1, infinity)
141  * @tparam tIgnoreInfinity True, to ignore +/- inf values; False, to consider +/- inf as minimum and maximum values as well
142  */
143  template <typename T, unsigned int tChannels, bool tIgnoreInfinity = false>
144  static void determineMinMaxValuesSubset(const T* frame, const unsigned int width, const unsigned int height, T* minimalValues, T* maximalValues, Lock* lock, const unsigned int framePaddingElements, const unsigned int firstRow, const unsigned int numberRows);
145 };
146 
147 template <typename T>
148 template <bool tDetermineMinimum>
149 void FrameMinMax::ExtremumDeterminer<T>::determineExtremumValue(const T* frame, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, T* extremumValue, PixelPosition* extremumLocation)
150 {
151  ocean_assert(frame != nullptr);
152  ocean_assert(width != 0u && height != 0u);
153 
154  ocean_assert_accuracy(extremumValue != nullptr || extremumLocation != nullptr); // one of the two parameters should be defined
155 
156  const unsigned int frameStrideElements = width + framePaddingElements;
157 
158  T internalExtremumValue = frame[0];
159  PixelPosition internalExtremumLocation(0u, 0u);
160 
161  for (unsigned int y = 0u; y < height; ++y)
162  {
163  for (unsigned int x = 0u; x < width; ++x)
164  {
165  if (tDetermineMinimum ? (frame[x] < internalExtremumValue) : (frame[x] > internalExtremumValue))
166  {
167  internalExtremumValue = frame[x];
168  internalExtremumLocation = PixelPosition(x, y);
169  }
170  }
171 
172  frame += frameStrideElements;
173  }
174 
175  if (extremumValue)
176  {
177  *extremumValue = internalExtremumValue;
178  }
179 
180  if (extremumLocation)
181  {
182  *extremumLocation = internalExtremumLocation;
183  }
184 }
185 
186 #if defined(OCEAN_HARDWARE_NEON_VERSION) && OCEAN_HARDWARE_NEON_VERSION >= 10
187 
188 template <>
189 template <bool tDetermineMinimum>
190 void FrameMinMax::ExtremumDeterminer<unsigned char>::determineExtremumValue(const unsigned char* frame, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, unsigned char* extremumValue, PixelPosition* extremumLocation)
191 {
192  ocean_assert(frame != nullptr);
193  ocean_assert(width != 0u && height != 0u);
194 
195  ocean_assert_accuracy(extremumValue != nullptr || extremumLocation != nullptr); // one of the two parameters should be defined
196 
197  const unsigned int frameStrideElements = width + framePaddingElements;
198 
199  unsigned char internalExtremumValue = frame[0];
200  PixelPosition internalExtremumLocation(0u, 0u);
201 
202  if (width >= 16u && width < 65535u && height < 65535u)
203  {
204  /*
205  * We handle 16 values concurrently
206  * Strategy: we go through the provided memory and simply keep the smallest values in our NEON registers,
207  * values and coordinates are 'blended' using binary operations (here for finding the minimum value):
208  *
209  * mask[i] = candidates[i] < minValue[i] ? 0xFFFFFFFF : 0x00000000, for i = [0, 15]
210  *
211  * handling values:
212  * minValue[i] = mask[i] == 0xFFFFFFFF ? candidates[i] : minValue[i]
213  *
214  * handling coordinates:
215  * minCoordinateX[i] = mask[i] == 0xFFFFFFFF ? candidateCoordinateX[i] : minCoordinateX[i]
216  */
217 
218  // [0, 1, 2, 3, 4, 5, 6, 7]
219  const uint16x8_t constant_01234567_u_16x8 = {0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u};
220  // [8, 8, 8, 8, 8, 8, 8, 8]
221  const uint16x8_t constant_8_u_16x8 = vdupq_n_u16(8u);
222  // [16, 16, 16, 16, 16, 16, 16, 16]
223  const uint16x8_t constant_16_u_16x8 = vdupq_n_u16(16u);
224 
225  // the four locations of the extremum value:
226  uint16x8_t extremumLocationX_01234567_u_16x8 = constant_01234567_u_16x8;
227  uint16x8_t extremumLocationX_89ABCDEF_u_16x8 = vaddq_u16(constant_01234567_u_16x8, constant_8_u_16x8);
228 
229  uint16x8_t extremumLocationY_01234567_u_16x8 = vdupq_n_u16(0u);
230  uint16x8_t extremumLocationY_89ABCDEF_u_16x8 = vdupq_n_u16(0u);
231 
232  uint8x16_t extremumValue_u_8x16 = vld1q_u8(frame + 0);
233 
234  for (unsigned int y = 0u; y < height; ++y)
235  {
236  // [y, y, y, y, y, y, y, y]
237  const uint16x8_t candidateLocation_y_u_16x8 = vdupq_n_u16(uint16_t(y));
238 
239  // [0, 1, 2, 3, 4, 5, 6, 7]
240  uint16x8_t candidateLocation_01234567_x_u_16x8 = constant_01234567_u_16x8;
241  uint16x8_t candidateLocation_89ABCDEF_x_u_16x8 = vaddq_u16(constant_01234567_u_16x8, constant_8_u_16x8);
242 
243  for (unsigned int x = 0u; x < width; x += 16u)
244  {
245  if (x + 16u > width)
246  {
247  // the last iteration will not fit into the output frame,
248  // so we simply shift x left by some pixels (at most 15) and we will calculate some pixels again
249 
250  ocean_assert(x >= 16u && width > 16u);
251  const unsigned int newX = width - 16u;
252 
253  ocean_assert(x > newX);
254  const unsigned int offset = x - newX;
255  ocean_assert(offset < 16u);
256 
257  x = newX;
258 
259  candidateLocation_01234567_x_u_16x8 = vsubq_u16(candidateLocation_01234567_x_u_16x8, vdupq_n_u16(uint16_t(offset)));
260  candidateLocation_89ABCDEF_x_u_16x8 = vsubq_u16(candidateLocation_89ABCDEF_x_u_16x8, vdupq_n_u16(uint16_t(offset)));
261 
262  // the for loop will stop after this iteration
263  ocean_assert(!(x + 16u < width));
264  }
265 
266  const uint8x16_t candidates_u_8x16 = vld1q_u8(frame + x);
267 
268  // if (candidates[i] < minValue[i]) - for the example of finding the minimum
269  // mask[i] = 0xFFFFFFFF
270  // else
271  // mask[i] = 0x00000000
272 
273  const uint8x16_t mask_u_8x16 = tDetermineMinimum ? vcltq_u8(candidates_u_8x16, extremumValue_u_8x16) : vcgtq_u8(candidates_u_8x16, extremumValue_u_8x16);
274 
275  // minValue[i] = mask[i] == 0xFFFFFFFF ? candidates[i] : minValue[i]
276  extremumValue_u_8x16 = vbslq_u8(mask_u_8x16, candidates_u_8x16, extremumValue_u_8x16);
277 
278  // separating our 8x16 mask to two 16x8 masks
279  uint16x8_t mask_01234567_u_16x8 = vmovl_u8(vget_low_u8(mask_u_8x16));
280  uint16x8_t mask_89ABCDEF_u_16x8 = vmovl_u8(vget_high_u8(mask_u_8x16));
281  mask_01234567_u_16x8 = vorrq_u16(mask_01234567_u_16x8, vshlq_n_u16(mask_01234567_u_16x8, 8));
282  mask_89ABCDEF_u_16x8 = vorrq_u16(mask_89ABCDEF_u_16x8, vshlq_n_u16(mask_89ABCDEF_u_16x8, 8));
283 
284  // minLocationX[i] = mask[i] == 0xFFFFFFFF ? candidateLocationX[i] : minLocationX[i]
285  extremumLocationX_01234567_u_16x8 = vbslq_u16(mask_01234567_u_16x8, candidateLocation_01234567_x_u_16x8, extremumLocationX_01234567_u_16x8);
286  extremumLocationX_89ABCDEF_u_16x8 = vbslq_u16(mask_89ABCDEF_u_16x8, candidateLocation_89ABCDEF_x_u_16x8, extremumLocationX_89ABCDEF_u_16x8);
287 
288  // minLocationY[i] = mask[i] == 0xFFFFFFFF ? candidateLocationY[i] : minLocationY[i]
289  extremumLocationY_01234567_u_16x8 = vbslq_u16(mask_01234567_u_16x8, candidateLocation_y_u_16x8, extremumLocationY_01234567_u_16x8);
290  extremumLocationY_89ABCDEF_u_16x8 = vbslq_u16(mask_89ABCDEF_u_16x8, candidateLocation_y_u_16x8, extremumLocationY_89ABCDEF_u_16x8);
291 
292  // += [16, 16, 16, 16]
293  candidateLocation_01234567_x_u_16x8 = vaddq_u16(candidateLocation_01234567_x_u_16x8, constant_16_u_16x8);
294  candidateLocation_89ABCDEF_x_u_16x8 = vaddq_u16(candidateLocation_89ABCDEF_x_u_16x8, constant_16_u_16x8);
295  }
296 
297  frame += frameStrideElements;
298  }
299 
300  // we compute the best 8 results out of our best 16 results
301 
302  const uint8x8_t extremumValue_01234567_u_8x8 = vget_low_u8(extremumValue_u_8x16);
303  const uint8x8_t extremumValue_89ABCDEF_u_8x8 = vget_high_u8(extremumValue_u_8x16);
304 
305  const uint8x8_t mask_u_8x8 = tDetermineMinimum ? vclt_u8(extremumValue_01234567_u_8x8, extremumValue_89ABCDEF_u_8x8) : vcgt_u8(extremumValue_01234567_u_8x8, extremumValue_89ABCDEF_u_8x8);
306  const uint8x8_t extremumValue_u_8x8 = vbsl_u8(mask_u_8x8, extremumValue_01234567_u_8x8, extremumValue_89ABCDEF_u_8x8);
307 
308  uint16x8_t mask_u_16x8 = vmovl_u8(mask_u_8x8);
309  mask_u_16x8 = vorrq_u16(mask_u_16x8, vshlq_n_u16(mask_u_16x8, 8));
310 
311  const uint16x8_t extremumLocationX_u_16x8 = vbslq_u16(mask_u_16x8, extremumLocationX_01234567_u_16x8, extremumLocationX_89ABCDEF_u_16x8);
312  const uint16x8_t extremumLocationY_u_16x8 = vbslq_u16(mask_u_16x8, extremumLocationY_01234567_u_16x8, extremumLocationY_89ABCDEF_u_16x8);
313 
314  // we store our four best values and finally need to select the best of them
315 
316  uint16_t extremumLocationsX[8];
317  vst1q_u16(extremumLocationsX, extremumLocationX_u_16x8);
318 
319  uint16_t extremumLocationsY[8];
320  vst1q_u16(extremumLocationsY, extremumLocationY_u_16x8);
321 
322  unsigned char extremumValues[8];
323  vst1_u8(extremumValues, extremumValue_u_8x8);
324 
325  internalExtremumValue = extremumValues[0];
326  internalExtremumLocation = CV::PixelPosition((unsigned int)(extremumLocationsX[0]), (unsigned int)(extremumLocationsY[0]));
327 
328  for (unsigned int n = 1u; n < 8u; ++n)
329  {
330  if (tDetermineMinimum ? (extremumValues[n] < internalExtremumValue) : (extremumValues[n] > internalExtremumValue))
331  {
332  internalExtremumValue = extremumValues[n];
333  internalExtremumLocation = CV::PixelPosition((unsigned int)(extremumLocationsX[n]), (unsigned int)(extremumLocationsY[n]));
334  }
335  }
336  }
337  else
338  {
339  for (unsigned int y = 0u; y < height; ++y)
340  {
341  for (unsigned int x = 0u; x < width; ++x)
342  {
343  if (tDetermineMinimum ? (frame[x] < internalExtremumValue) : (frame[x] > internalExtremumValue))
344  {
345  internalExtremumValue = frame[x];
346  internalExtremumLocation = PixelPosition(x, y);
347  }
348  }
349 
350  frame += frameStrideElements;
351  }
352  }
353 
354  if (extremumValue)
355  {
356  *extremumValue = internalExtremumValue;
357  }
358 
359  if (extremumLocation)
360  {
361  *extremumLocation = internalExtremumLocation;
362  }
363 }
364 
365 template <>
366 template <bool tDetermineMinimum>
367 void FrameMinMax::ExtremumDeterminer<float>::determineExtremumValue(const float* frame, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, float* extremumValue, PixelPosition* extremumLocation)
368 {
369  ocean_assert(frame != nullptr);
370  ocean_assert(width != 0u && height != 0u);
371 
372  ocean_assert_accuracy(extremumValue != nullptr || extremumLocation != nullptr); // one of the two parameters should be defined
373 
374  const unsigned int frameStrideElements = width + framePaddingElements;
375 
376  float internalExtremumValue = frame[0];
377  PixelPosition internalExtremumLocation(0u, 0u);
378 
379  if (width >= 8u)
380  {
381  /*
382  * We handle 8 float values concurrently
383  * Strategy: we go through the provided memory and simply keep the smallest values in our NEON registers,
384  * values and coordinates are 'blended' using binary operations (here for finding the minimum value):
385  *
386  * mask[i] = candidates[i] < minValue[i] ? 0xFFFFFFFF : 0x00000000, for i = [0, 8]
387  *
388  * handling values:
389  * minValue[i] = mask[i] == 0xFFFFFFFF ? candidates[i] : minValue[i]
390  *
391  * handling coordinates:
392  * minCoordinateX[i] = mask[i] == 0xFFFFFFFF ? candidateCoordinateX[i] : minCoordinateX[i]
393  */
394 
395  // [0, 1, 2, 3]
396  const uint32x4_t constant_0123_u_32x4 = {0u, 1u, 2u, 3u};
397  // [4, 4, 4, 4]
398  const uint32x4_t constant_4_u_32x4 = vdupq_n_u32(4u);
399  // [8, 8, 8, 8]
400  const uint32x4_t constant_8_u_32x4 = vdupq_n_u32(8u);
401 
402  // the four locations of the extremum value:
403  uint32x4_t extremumLocationX_0123_u_32x4 = constant_0123_u_32x4;
404  uint32x4_t extremumLocationX_4567_u_32x4 = vaddq_u32(constant_0123_u_32x4, constant_4_u_32x4);
405 
406  uint32x4_t extremumLocationY_0123_u_32x4 = vdupq_n_u32(0u);
407  uint32x4_t extremumLocationY_4567_u_32x4 = vdupq_n_u32(0u);
408 
409  float32x4_t extremumValue_0123_f_32x4 = vld1q_f32(frame + 0);
410  float32x4_t extremumValue_4567_f_32x4 = vld1q_f32(frame + 4);
411 
412  for (unsigned int y = 0u; y < height; ++y)
413  {
414  // [y, y, y, y]
415  const uint32x4_t candidateLocation_y_u_32x4 = vdupq_n_u32(y);
416 
417  // [0, 1, 2, 3]
418  uint32x4_t candidateLocation_0123_x_u_32x4 = constant_0123_u_32x4;
419  uint32x4_t candidateLocation_4567_x_u_32x4 = vaddq_u32(constant_0123_u_32x4, constant_4_u_32x4);
420 
421  for (unsigned int x = 0u; x < width; x += 8u)
422  {
423  if (x + 8u > width)
424  {
425  // the last iteration will not fit into the output frame,
426  // so we simply shift x left by some pixels (at most 7) and we will calculate some pixels again
427 
428  ocean_assert(x >= 8u && width > 8u);
429  const unsigned int newX = width - 8u;
430 
431  ocean_assert(x > newX);
432  const unsigned int offset = x - newX;
433  ocean_assert(offset < 8u);
434 
435  x = newX;
436 
437  candidateLocation_0123_x_u_32x4 = vsubq_u32(candidateLocation_0123_x_u_32x4, vdupq_n_u32(offset));
438  candidateLocation_4567_x_u_32x4 = vsubq_u32(candidateLocation_4567_x_u_32x4, vdupq_n_u32(offset));
439 
440  // the for loop will stop after this iteration
441  ocean_assert(!(x + 8u < width));
442  }
443 
444  const float32x4_t candidates_0123_f_32x4 = vld1q_f32(frame + x + 0u);
445  const float32x4_t candidates_4567_f_32x4 = vld1q_f32(frame + x + 4u);
446 
447  // if (candidates[i] < minValue[i]) - for the example of finding the minimum
448  // mask[i] = 0xFFFFFFFF
449  // else
450  // mask[i] = 0x00000000
451 
452  const uint32x4_t mask_0123_u_32x4 = tDetermineMinimum ? vcltq_f32(candidates_0123_f_32x4, extremumValue_0123_f_32x4) : vcgtq_f32(candidates_0123_f_32x4, extremumValue_0123_f_32x4);
453  const uint32x4_t mask_4567_u_32x4 = tDetermineMinimum ? vcltq_f32(candidates_4567_f_32x4, extremumValue_4567_f_32x4) : vcgtq_f32(candidates_4567_f_32x4, extremumValue_4567_f_32x4);
454 
455  // minValue[i] = mask[i] == 0xFFFFFFFF ? candidates[i] : minValue[i]
456  extremumValue_0123_f_32x4 = vbslq_f32(mask_0123_u_32x4, candidates_0123_f_32x4, extremumValue_0123_f_32x4);
457  extremumValue_4567_f_32x4 = vbslq_f32(mask_4567_u_32x4, candidates_4567_f_32x4, extremumValue_4567_f_32x4);
458 
459  extremumLocationX_0123_u_32x4 = vbslq_u32(mask_0123_u_32x4, candidateLocation_0123_x_u_32x4, extremumLocationX_0123_u_32x4);
460  extremumLocationX_4567_u_32x4 = vbslq_u32(mask_4567_u_32x4, candidateLocation_4567_x_u_32x4, extremumLocationX_4567_u_32x4);
461 
462  extremumLocationY_0123_u_32x4 = vbslq_u32(mask_0123_u_32x4, candidateLocation_y_u_32x4, extremumLocationY_0123_u_32x4);
463  extremumLocationY_4567_u_32x4 = vbslq_u32(mask_4567_u_32x4, candidateLocation_y_u_32x4, extremumLocationY_4567_u_32x4);
464 
465  // += [8, 8, 8, 8]
466  candidateLocation_0123_x_u_32x4 = vaddq_u32(candidateLocation_0123_x_u_32x4, constant_8_u_32x4);
467  candidateLocation_4567_x_u_32x4 = vaddq_u32(candidateLocation_4567_x_u_32x4, constant_8_u_32x4);
468  }
469 
470  frame += frameStrideElements;
471  }
472 
473  const uint32x4_t mask_u_32x4 = tDetermineMinimum ? vcltq_f32(extremumValue_0123_f_32x4, extremumValue_4567_f_32x4) : vcgtq_f32(extremumValue_0123_f_32x4, extremumValue_4567_f_32x4);
474  extremumValue_0123_f_32x4 = vbslq_f32(mask_u_32x4, extremumValue_0123_f_32x4, extremumValue_4567_f_32x4);
475  extremumLocationX_0123_u_32x4 = vbslq_u32(mask_u_32x4, extremumLocationX_0123_u_32x4, extremumLocationX_4567_u_32x4);
476  extremumLocationY_0123_u_32x4 = vbslq_u32(mask_u_32x4, extremumLocationY_0123_u_32x4, extremumLocationY_4567_u_32x4);
477 
478  // we store our four best values and finally need to select the best of them
479 
480  unsigned int extremumLocationsX[4];
481  vst1q_u32(extremumLocationsX, extremumLocationX_0123_u_32x4);
482 
483  unsigned int extremumLocationsY[4];
484  vst1q_u32(extremumLocationsY, extremumLocationY_0123_u_32x4);
485 
486  float extremumValues[4];
487  vst1q_f32(extremumValues, extremumValue_0123_f_32x4);
488 
489  internalExtremumValue = extremumValues[0];
490  internalExtremumLocation = CV::PixelPosition(extremumLocationsX[0], extremumLocationsY[0]);
491 
492  for (unsigned int n = 1u; n < 4u; ++n)
493  {
494  if (tDetermineMinimum ? (extremumValues[n] < internalExtremumValue) : (extremumValues[n] > internalExtremumValue))
495  {
496  internalExtremumValue = extremumValues[n];
497  internalExtremumLocation = CV::PixelPosition(extremumLocationsX[n], extremumLocationsY[n]);
498  }
499  }
500  }
501  else
502  {
503  for (unsigned int y = 0u; y < height; ++y)
504  {
505  for (unsigned int x = 0u; x < width; ++x)
506  {
507  if (tDetermineMinimum ? (frame[x] < internalExtremumValue) : (frame[x] > internalExtremumValue))
508  {
509  internalExtremumValue = frame[x];
510  internalExtremumLocation = PixelPosition(x, y);
511  }
512  }
513 
514  frame += frameStrideElements;
515  }
516  }
517 
518  if (extremumValue)
519  {
520  *extremumValue = internalExtremumValue;
521  }
522 
523  if (extremumLocation)
524  {
525  *extremumLocation = internalExtremumLocation;
526  }
527 }
528 
529 #endif // OCEAN_HARDWARE_NEON_VERSION >= 10
530 
531 template <typename T>
532 void FrameMinMax::determineMinValue(const T* frame, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, T* minValue, PixelPosition* minLocation)
533 {
534  return ExtremumDeterminer<T>::template determineExtremumValue<true>(frame, width, height, framePaddingElements, minValue, minLocation);
535 }
536 
537 template <typename T>
538 void FrameMinMax::determineMaxValue(const T* frame, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, T* maxValue, PixelPosition* maxLocation)
539 {
540  return ExtremumDeterminer<T>::template determineExtremumValue<false>(frame, width, height, framePaddingElements, maxValue, maxLocation);
541 }
542 
543 template <typename T, unsigned int tChannels, bool tIgnoreInfinity>
544 inline void FrameMinMax::determineMinMaxValues(const T* frame, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, T* minimalValues, T* maximalValues, Worker* worker)
545 {
546  static_assert(tChannels >= 1u, "Invalid channel number!");
547 
548  ocean_assert(frame != nullptr && minimalValues != nullptr && maximalValues != nullptr);
549  ocean_assert(width >= 1u && height >= 1u);
550 
551  for (unsigned int n = 0u; n < tChannels; ++n)
552  {
553  minimalValues[n] = NumericT<T>::maxValue();
554  }
555 
556  for (unsigned int n = 0u; n < tChannels; ++n)
557  {
558  maximalValues[n] = NumericT<T>::minValue();
559  }
560 
561  if (worker)
562  {
563  Lock lock;
564  worker->executeFunction(Worker::Function::createStatic(determineMinMaxValuesSubset<T, tChannels, tIgnoreInfinity>, frame, width, height, minimalValues, maximalValues, &lock, framePaddingElements, 0u, 0u), 0u, height, 7u, 8u, 20u);
565  }
566  else
567  {
568  determineMinMaxValuesSubset<T, tChannels, tIgnoreInfinity>(frame, width, height, minimalValues, maximalValues, nullptr, framePaddingElements, 0u, height);
569  }
570 }
571 
572 #if defined(OCEAN_HARDWARE_NEON_VERSION) && OCEAN_HARDWARE_NEON_VERSION >= 10
573 
574 /// Forward declaration of template socialization
575 template <>
576 bool FrameMinMax::countElementsOutsideRange<uint8_t>(const uint8_t* frame, const uint32_t width, const uint32_t height, const uint32_t framePaddingElements, const uint8_t rangeStart, const uint8_t rangeEnd, uint32_t* elementsBelowRange, uint32_t* elementsAboveRange);
577 
578 #endif // OCEAN_HARDWARE_NEON_VERSION >= 10
579 
580 template <typename T>
581 bool FrameMinMax::countElementsOutsideRange(const T* frame, const uint32_t width, const uint32_t height, const uint32_t framePaddingElements, const T rangeStart, const T rangeEnd, uint32_t* elementsBelowRange, uint32_t* elementsAboveRange)
582 {
583  if (frame == nullptr || width == 0u || height == 0u || rangeStart > rangeEnd || (elementsBelowRange == nullptr && elementsAboveRange == nullptr))
584  {
585  ocean_assert(false && "Invalid input");
586  return false;
587  }
588 
589  const uint32_t frameStrideElements = width + framePaddingElements;
590  const T* const frameEnd = frame + height * frameStrideElements - framePaddingElements;
591 
592  uint32_t elementsBelowRangeLocal = 0u;
593  uint32_t elementsAboveRangeLocal = 0u;
594 
595  if (framePaddingElements == 0u)
596  {
597  while (frame < frameEnd)
598  {
599  if (*frame < rangeStart)
600  {
601  ++elementsBelowRangeLocal;
602  }
603  else if (*frame >= rangeEnd)
604  {
605  ++elementsAboveRangeLocal;
606  }
607 
608  ++frame;
609  }
610  }
611  else
612  {
613  for (uint32_t y = 0u; y < height; ++y)
614  {
615  const T* frameRowEnd = frame + width;
616 
617  while (frame < frameRowEnd)
618  {
619  if (*frame < rangeStart)
620  {
621  ++elementsBelowRangeLocal;
622  }
623  else if (*frame >= rangeEnd)
624  {
625  ++elementsAboveRangeLocal;
626  }
627 
628  ++frame;
629  }
630 
631  frame += framePaddingElements;
632  }
633  }
634 
635  if (elementsBelowRange)
636  {
637  *elementsBelowRange = elementsBelowRangeLocal;
638  }
639 
640  if (elementsAboveRange)
641  {
642  *elementsAboveRange = elementsAboveRangeLocal;
643  }
644 
645  return true;
646 }
647 
648 template <typename T, unsigned int tChannels, bool tIgnoreInfinity>
649 void FrameMinMax::determineMinMaxValuesSubset(const T* frame, const unsigned int width, const unsigned int height, T* minimalValues, T* maximalValues, Lock* lock, const unsigned int framePaddingElements, const unsigned int firstRow, const unsigned int numberRows)
650 {
651  static_assert(tChannels >= 1u, "Invalid channel number!");
652 
653  ocean_assert(frame != nullptr && minimalValues != nullptr && maximalValues != nullptr);
654  ocean_assert(width >= 1u && height >= 1u);
655  ocean_assert_and_suppress_unused(firstRow + numberRows <= height, height);
656 
657  T localMinimal[tChannels];
658  for (unsigned int n = 0u; n < tChannels; ++n)
659  {
660  localMinimal[n] = NumericT<T>::maxValue();
661  }
662 
663  T localMaximal[tChannels];
664  for (unsigned int n = 0u; n < tChannels; ++n)
665  {
666  localMaximal[n] = NumericT<T>::minValue();
667  }
668 
669  const unsigned int frameStrideElements = width * tChannels + framePaddingElements;
670 
671  frame += firstRow * frameStrideElements;
672 
673  const T* const frameEnd = frame + numberRows * frameStrideElements;
674  while (frame != frameEnd)
675  {
676  ocean_assert(frame < frameEnd);
677 
678  const T* const frameRowEnd = frame + width * tChannels;
679 
680  while (frame != frameRowEnd)
681  {
682  ocean_assert(frame < frameRowEnd);
683  ocean_assert(frame < frameEnd);
684 
685  for (unsigned int n = 0u; n < tChannels; ++n)
686  {
687  if constexpr (tIgnoreInfinity && std::is_floating_point<T>::value)
688  {
689  if (!NumericT<T>::isInf(frame[n]) && !NumericT<T>::isNan(frame[n]))
690  {
691  if (frame[n] < localMinimal[n])
692  {
693  localMinimal[n] = frame[n];
694  }
695 
696  if (frame[n] > localMaximal[n])
697  {
698  localMaximal[n] = frame[n];
699  }
700  }
701  }
702  else
703  {
704  if (frame[n] < localMinimal[n])
705  {
706  localMinimal[n] = frame[n];
707  }
708 
709  if (frame[n] > localMaximal[n])
710  {
711  localMaximal[n] = frame[n];
712  }
713  }
714  }
715 
716  frame += tChannels;
717  }
718 
719  frame += framePaddingElements;
720  }
721 
722  const OptionalScopedLock scopedLock(lock);
723 
724  for (unsigned int n = 0u; n < tChannels; ++n)
725  {
726  minimalValues[n] = min(minimalValues[n], localMinimal[n]);
727  maximalValues[n] = max(maximalValues[n], localMaximal[n]);
728  }
729 }
730 
731 }
732 
733 }
734 
735 #endif // META_OCEAN_CV_FRAME_MIN_MAX_H
This class allows to determine the extremum (the global minimum or maximum) within a given frame.
Definition: FrameMinMax.h:41
static void determineExtremumValue(const T *frame, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, T *extremumValue=nullptr, PixelPosition *extremumLocation=nullptr)
Determines the extremum minimum value (either the global minimum or the global maximum) within a give...
Definition: FrameMinMax.h:149
This class implements functions allowing to determine minimum and maximum values within frames.
Definition: FrameMinMax.h:30
static void determineMaxValue(const T *frame, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, T *maxValue=nullptr, PixelPosition *maxLocation=nullptr)
Determines the maximum value (the peak value) within a given frame with one channel.
Definition: FrameMinMax.h:538
static bool countElementsOutsideRange(const T *frame, const uint32_t width, const uint32_t height, const uint32_t framePaddingElements, const T rangeStart, const T rangeEnd, uint32_t *elementsBelowRange=nullptr, uint32_t *elementsAboveRange=nullptr)
Counts frame elements in a 1-channel frame that are outside a specified range of values.
Definition: FrameMinMax.h:581
static void determineMinValue(const T *frame, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, T *minValue=nullptr, PixelPosition *minLocation=nullptr)
Determines the minimum value (the global minimum) within a given frame with one channel.
Definition: FrameMinMax.h:532
static void determineMinMaxValuesSubset(const T *frame, const unsigned int width, const unsigned int height, T *minimalValues, T *maximalValues, Lock *lock, const unsigned int framePaddingElements, const unsigned int firstRow, const unsigned int numberRows)
Determines the minimal and maximal pixel values in a subset of a given frame.
Definition: FrameMinMax.h:649
static void determineMinMaxValues(const T *frame, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, T *minimalValues, T *maximalValues, Worker *worker=nullptr)
Determines the minimal and maximal pixel values in a given frame.
Definition: FrameMinMax.h:544
static Caller< void > createStatic(typename StaticFunctionPointerMaker< void, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass >::Type function)
Creates a new caller container for a static function with no function parameter.
Definition: Caller.h:2876
This class implements a recursive lock object.
Definition: Lock.h:31
This class provides basic numeric functionalities.
Definition: Numeric.h:57
static constexpr T minValue()
Returns the min scalar value.
Definition: Numeric.h:3250
static constexpr T maxValue()
Returns the max scalar value.
Definition: Numeric.h:3244
This class implements an optional recursive scoped lock object locking the lock object only if it's d...
Definition: Lock.h:325
This class implements a worker able to distribute function calls over different threads.
Definition: Worker.h:33
bool executeFunction(const Function &function, const unsigned int first, const unsigned int size, const unsigned int firstIndex=(unsigned int)(-1), const unsigned int sizeIndex=(unsigned int)(-1), const unsigned int minimalIterations=1u, const unsigned int threadIndex=(unsigned int)(-1))
Executes a callback function separable by two function parameters.
PixelPositionT< unsigned int > PixelPosition
Definition of the default PixelPosition object with a data type allowing only positive coordinate val...
Definition: PixelPosition.h:27
The namespace covering the entire Ocean framework.
Definition: Accessor.h:15