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"
13
14#include "ocean/base/Frame.h"
15#include "ocean/base/Worker.h"
16
17#include <limits>
18
19namespace Ocean
20{
21
22namespace CV
23{
24
25/**
26 * This class implements functions allowing to determine minimum and maximum values within frames.
27 * @ingroup cv
28 */
29class 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
147template <typename T>
148template <bool tDetermineMinimum>
149void 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
188template <>
189template <bool tDetermineMinimum>
190void 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
365template <>
366template <bool tDetermineMinimum>
367void 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
531template <typename T>
532void 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
537template <typename T>
538void 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
543template <typename T, unsigned int tChannels, bool tIgnoreInfinity>
544inline 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
575template <>
576bool 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
580template <typename T>
581bool 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
648template <typename T, unsigned int tChannels, bool tIgnoreInfinity>
649void 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 bool isNan(const T value)
Returns whether a given value is not a number.
Definition Numeric.h:3021
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:34
The namespace covering the entire Ocean framework.
Definition Accessor.h:15