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