Ocean
Loading...
Searching...
No Matches
NonMaximumSuppression.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_NON_MAXIMUM_SUPPRESSION_H
9#define META_OCEAN_CV_NON_MAXIMUM_SUPPRESSION_H
10
11#include "ocean/cv/CV.h"
12
13#include "ocean/base/Callback.h"
15#include "ocean/base/Worker.h"
16
17#include "ocean/math/Vector2.h"
18
19namespace Ocean
20{
21
22namespace CV
23{
24
25/**
26 * This class provides base functionality and type definitions for non-maximum-suppression operations.
27 * It defines common types like StrengthPosition and StrengthPositions used by the template class NonMaximumSuppressionT.
28 * @see NonMaximumSuppressionT
29 * @ingroup cv
30 */
32{
33 public:
34
35 /**
36 * This class extends a 2D position by a third parameter storing a strength value.
37 * @tparam TCoordinate The data type of a scalar coordinate
38 * @tparam TStrength The data type of the strength parameter
39 */
40 template <typename TCoordinate, typename TStrength>
41 class StrengthPosition : public VectorT2<TCoordinate>
42 {
43 public:
44
45 /**
46 * Creates a new object with default strength parameter.
47 */
48 StrengthPosition() = default;
49
50 /**
51 * Creates a new object with explicit position and strength parameter.
52 * @param x Horizontal position
53 * @param y Vertical position
54 * @param strength The strength parameter
55 */
56 inline StrengthPosition(const TCoordinate x, const TCoordinate y, const TStrength& strength);
57
58 /**
59 * Returns the strength parameter of this object.
60 * @return Strength parameter
61 */
62 inline const TStrength& strength() const;
63
64 /**
65 * Compares the strength value of two objects.
66 * @param left The left object to compare
67 * @param right The right object to compare
68 * @return True, if so
69 * @tparam tLeftLargerThanRight True, to return whether left's strength is larger than right's strength; False, to return whether right is larger than left
70 */
71 template <bool tLeftLargerThanRight = true>
73
74 private:
75
76 /// Strength parameter of this object.
77 TStrength strength_ = TStrength();
78 };
79
80 /**
81 * Definition of a vector holding strength pixel positions.
82 */
83 template <typename TCoordinate, typename TStrength>
84 using StrengthPositions = std::vector<StrengthPosition<TCoordinate, TStrength>>;
85};
86
87/**
88 * This class implements the possibility to find local maximum in a 2D array by applying a non-maximum-suppression search.
89 * The search is done within a 3x3 neighborhood (centered around the point of interest).<br>
90 * Use this class to determine e.g. reliable feature points.<br>
91 * The class supports bin accuracy (pixel accuracy) and sub-bin accuracy (sub-pixel accuracy).
92 *
93 * The non-maximum-suppression search is implemented by a vertical list holding maps of horizontal array elements.<br>
94 * The performance depends on the number of elements inserted into the individual maps.<br>
95 * Thus, do not add data elements with negligible value.
96 *
97 * It should be mentioned that the application of this class should be restricted to situations in which the entire filter response values do not exist already.<br>
98 * The performance boost comes with a simultaneous determination of filter responses and the insertion of possible candidates for maximum locations.
99 * @tparam T The data type of the individual elements that are applied for the non-maximum-suppression search.
100 * @ingroup cv
101 */
102template <typename T>
104{
105 public:
106
107 /**
108 * Definition of a callback function used to determine the precise sub-pixel position of a specific point.
109 * The first parameter provides the horizontal position.<br>
110 * The second parameter provides the vertical position.<br>
111 * The third parameter provides the strength value.<br>
112 * The fourth parameter receives the precise horizontal position.<br>
113 * The fifth parameter receives the precise vertical position.<br>
114 * The sixth parameter receives the precise strength value.<br>
115 * The return parameter should be True if the precise position could be determined
116 */
117 template <typename TCoordinate, typename TStrength>
119
120 private:
121
122 /**
123 * This class holds the horizontal position and strength parameter of an interest pixel.
124 */
126 {
127 public:
128
129 /**
130 * Creates a new candidate object.
131 */
132 inline StrengthCandidate();
133
134 /**
135 * Creates a new candidate object with horizontal position and strength parameter.
136 * @param x Horizontal position in pixel
137 * @param strength The strength parameter
138 */
139 inline StrengthCandidate(const unsigned int x, const T& strength);
140
141 /**
142 * Returns the horizontal position of this candidate object.
143 * @return Horizontal position in pixel
144 */
145 inline unsigned int x() const;
146
147 /**
148 * Returns the strength parameter of this object.
149 * @return Strength parameter
150 */
151 inline const T& strength() const;
152
153 private:
154
155 /// Horizontal position of this object.
156 unsigned int positionX_ = (unsigned int)(-1);
157
158 /// Strength parameter of this object.
159 T strength_ = T();
160 };
161
162 /**
163 * Definition of a vector holding strength candidate objects.
164 */
165 using StrengthCandidateRow = std::vector<StrengthCandidate>;
166
167 /**
168 * Definition of a vector holding a vector of strength candidates.
169 */
171
172 public:
173
174 /**
175 * Move constructor.
176 * @param nonMaximumSuppression The object to be moved
177 */
178 NonMaximumSuppressionT(NonMaximumSuppressionT<T>&& nonMaximumSuppression) noexcept;
179
180 /**
181 * Copy constructor.
182 * @param nonMaximumSuppression The object to be moved
183 */
184 NonMaximumSuppressionT(const NonMaximumSuppressionT<T>& nonMaximumSuppression) noexcept;
185
186 /**
187 * Creates a new maximum suppression object with a predefined size.
188 * @param width The width of this object in pixel, with range [3, infinity)
189 * @param height The height of this object in pixel, with range [3, infinity)
190 * @param yOffset Optional offset in the vertical direction moving the suppression region by the specified number of rows, with range [0, infinity)
191 */
192 NonMaximumSuppressionT(const unsigned int width, const unsigned int height, const unsigned int yOffset = 0u) noexcept;
193
194 /**
195 * Returns the width of this object.
196 * @return Width in pixel
197 */
198 inline unsigned int width() const;
199
200 /**
201 * Returns the height of this object.
202 * @return Height in pixel
203 */
204 inline unsigned int height() const;
205
206 /**
207 * Returns the optional offset in the vertical direction.
208 * @return Optional vertical direction offset, 0 by default
209 */
210 inline unsigned int yOffset() const;
211
212 /**
213 * Adds a new candidate to this object.
214 * Beware: Due to performance issues do no add candidates with negligible strength parameter.
215 * @param x Horizontal position in pixel, with range [0, width() - 1]
216 * @param y Vertical position in pixel, with range [yOffset(), yOffset() + height() - 1]
217 * @param strength The strength parameter
218 * @see addCandidates(), removeCandidatesFromRight().
219 */
220 inline void addCandidate(const unsigned int x, const unsigned int y, const T& strength);
221
222 /**
223 * Adds new candidates to this object from a given buffer providing one value for each bin/pixel of this object.
224 * Beware: Due to performance reasons, you should use the addCandidate() function to add one single new candidate in the moment the filter response is larger than a specific threshold.<br>
225 * @param values The from which candidates will be added, must be width() * height() elements
226 * @param valuesPaddingElements The number of padding elements at the end of each values row, in elements, with range [0, infinity)
227 * @param firstColumn First column to be handled, with range [0, width() - 1]
228 * @param numberColumns Number of columns to be handled, with range [1, width() - firstColumn]
229 * @param firstRow First row to be handled, with range [yOffset(), height() - 1]
230 * @param numberRows Number of rows to be handled, with range [1, height() - firstRow]
231 * @param minimalThreshold The minimal threshold so that a value counts as candidate
232 * @param worker Optional worker object to distribute the computation
233 * @see addCandidate(), removeCandidatesFromRight().
234 */
235 void addCandidates(const T* values, const unsigned int valuesPaddingElements, const unsigned int firstColumn, const unsigned int numberColumns, const unsigned int firstRow, const unsigned int numberRows, const T& minimalThreshold, Worker* worker);
236
237 /**
238 * Removes all candidates from a specified row having a horizontal location equal or larger than a specified coordinate.
239 * @param x The horizontal coordinate specifying which candidates will be removed, all candidates with horizontal location >= x will be removed, with range [0, infinity)
240 * @param y The index of the row in which the candidates will be removed, with range [yOffset(), yOffset() + height() - 1]
241 */
242 inline void removeCandidatesRightFrom(const unsigned int x, const unsigned int y);
243
244 /**
245 * Applies a non-maximum-suppression search on a given 2D frame in a 3x3 neighborhood (eight neighbors).
246 * This function allows to determine the precise position of the individual maximum value positions by application of a callback function determining the individual positions.<br>
247 * @param firstColumn First column to be handled, with range [1, width() - 1)
248 * @param numberColumns Number of columns to be handled
249 * @param firstRow First row to be handled, with range [yOffset() + 1, height() - 1)
250 * @param numberRows Number of rows to be handled
251 * @param strengthPositions The resulting non maximum suppressed positions including the strength parameters, will be appended
252 * @param worker Optional worker object to distribute the computation
253 * @param positionCallback Optional callback function allowing to determine the precise position of the individual maximum value positions
254 * @return True, if succeeded
255 * @tparam TCoordinate The data type of a scalar coordinate
256 * @tparam TStrength The data type of the strength parameter
257 * @tparam tStrictMaximum True, to search for a strict maximum (larger than all eight neighbors); False, to allow equal values in the upper left neighborhood
258 */
259 template <typename TCoordinate, typename TStrength, bool tStrictMaximum = true>
260 bool suppressNonMaximum(const unsigned int firstColumn, const unsigned int numberColumns, const unsigned int firstRow, const unsigned int numberRows, StrengthPositions<TCoordinate, TStrength>& strengthPositions, Worker* worker = nullptr, const PositionCallback<TCoordinate, TStrength>* positionCallback = nullptr) const;
261
262 /**
263 * Returns all gathered candidates of this object.
264 * The resulting candidates are raw candidates without any suppression.
265 * @param firstColumn The first column from which candidates will be returned, with range [0, width() - 1]
266 * @param numberColumns The number of columns for which candidates will be returned, with range [1, width() - firstColumn]
267 * @param firstRow The first row from which candidates will be returned, with range [yOffset(), height() - 1]
268 * @param numberRows The number of rows for which candidates will be returned, with range [1, height() - firstRow]
269 * @param strengthPositions The resulting strength positions
270 */
271 void candidates(const unsigned int firstColumn, const unsigned int numberColumns, const unsigned int firstRow, const unsigned int numberRows, StrengthPositions<unsigned int, T>& strengthPositions);
272
273 /**
274 * Removes the gathered non-maximum suppression information so that this object can be reused again (for the same task with same resolution etc.).
275 * The allocated memory will remain so that reusing this object may improve performance.
276 */
277 void reset();
278
279 /**
280 * Move operator.
281 * @param nonMaximumSuppression Object to be moved
282 * @return Reference to this object
283 */
285
286 /**
287 * Copy operator.
288 * @param nonMaximumSuppression The object to be copied
289 * @return Reference to this object
290 */
292
293 /**
294 * Applies a non-maximum-suppression based on already existing strength positions (just with a custom suppression radius) e.g., as a post-processing step.
295 * @param width The width of the image/domain in which the strength positions are located, e.g., in pixel, with range [1, infinity)
296 * @param height The height of the image/domain in which the strength positions are located, e.g., in pixel, with range [1, infinity)
297 * @param strengthPositions The strength positions for which a custom suppression-radius will be applied
298 * @param radius The suppression radius to be applied, with range [1, infinity)
299 * @param validIndices Optional resulting indices of all strength positions which remain after suppression
300 * @return The resulting strength positions
301 * @tparam TCoordinate The data type of a scalar coordinate
302 * @tparam TStrength The data type of the strength parameter
303 * @tparam tStrictMaximum True, to search for a strict maximum (larger than all eight neighbors); False, to allow equal values in the upper left neighborhood
304 */
305 template <typename TCoordinate, typename TStrength, bool tStrictMaximum>
306 static StrengthPositions<TCoordinate, TStrength> suppressNonMaximum(const unsigned int width, const unsigned int height, const StrengthPositions<TCoordinate, TStrength>& strengthPositions, const TCoordinate radius, Indices32* validIndices = nullptr);
307
308 /**
309 * Determines the precise peak location in 1D space for three discrete neighboring measurements at location x == 0.
310 * The precise peak is determined based on the first and second derivatives of the measurement values.
311 * @param leftValue The left discrete value, e.g., at location x - 1, with range (-infinity, middleValue]
312 * @param middleValue The middle discrete value, e.g., at location x, with range (-infinity, infinity)
313 * @param rightValue The discrete value, e.g., at location x + 1, with range (-infinity, middleValue]
314 * @param location The location of the precise peak of all values, with range (-1, 1)
315 * @return True, if succeeded
316 * @tparam TFloat The floating point data type to be used for calculation, either 'float' or 'double'
317 */
318 template <typename TFloat>
319 static bool determinePrecisePeakLocation1(const T& leftValue, const T& middleValue, const T& rightValue, TFloat& location);
320
321 /**
322 * Determines the precise peak location in 2D space for nine discrete neighboring measurements at location x == 0, y == 0.
323 * The precise peak is determined based on the first and second derivatives of the measurement values.
324 * @param topValues The three discrete values in the top row, must be valid
325 * @param centerValues The three discrete values in the center row, must be valid
326 * @param bottomValues The three discrete values in the bottom row, must be valid
327 * @param location The location of the precise peak of all values, with range (-1, 1)
328 * @return True, if succeeded
329 * @tparam TFloat The floating point data type to be used for calculation, either 'float' or 'double'
330 */
331 template <typename TFloat>
332 static bool determinePrecisePeakLocation2(const T* const topValues, const T* const centerValues, const T* const bottomValues, VectorT2<TFloat>& location);
333
334 private:
335
336 /**
337 * Adds new candidates to this object from a subset of a given buffer providing one value for each bin/pixel of this object.
338 * @param values The from which candidates will be added, must be width() * height() elements
339 * @param valuesStrideElements The number of elements between two values rows, in elements, with range [0, infinity)
340 * @param minimalThreshold The minimal threshold so that a value counts as candidate
341 * @param firstColumn First column to be handled, with range [0, width() - 1]
342 * @param numberColumns Number of columns to be handled, with range [1, width() - firstColumn]
343 * @param firstRow The first row in the buffer from which the candidates will be added, with range [yOffset(), height())
344 * @param numberRows The number of rows to be handled, with range [1, height() - firstRow]
345 * @see addCandidates().
346 */
347 void addCandidatesSubset(const T* values, const unsigned int valuesStrideElements, const unsigned int firstColumn, const unsigned int numberColumns, const T* minimalThreshold, const unsigned int firstRow, const unsigned int numberRows);
348
349 /**
350 * Applies a non-maximum-suppression search on a subset of a given 2D frame in a 3x3 neighborhood (eight neighbors).
351 * This function allows to determine the precise position of the individual maximum value positions by application of a callback function determining the individual positions.<br>
352 * @param strengthPositions Resulting non maximum suppressed positions including the strength parameters
353 * @param firstColumn First column to be handled
354 * @param numberColumns Number of columns to be handled
355 * @param lock The lock object that must be defined if this function is executed in parallel on several threads
356 * @param positionCallback Optional callback function allowing to determine the precise position of the individual maximum value positions
357 * @param firstRow First row to be handled
358 * @param numberRows Number of rows to be handled
359 * @tparam tStrictMaximum True, to search for a strict maximum (larger than all eight neighbors); False, to allow equal values in the upper left neighborhood
360 */
361 template <typename TCoordinate, typename TStrength, bool tStrictMaximum>
362 void suppressNonMaximumSubset(StrengthPositions<TCoordinate, TStrength>* strengthPositions, const unsigned int firstColumn, const unsigned int firstRow, Lock* lock, const PositionCallback<TCoordinate, TStrength>* positionCallback, const unsigned int numberColumns, const unsigned int numberRows) const;
363
364 private:
365
366 /// Width of this object.
367 unsigned int width_ = 0u;
368
369 /// All candidate rows.
371};
372
373template <typename TCoordinate, typename TStrength>
375 VectorT2<TCoordinate>(x, y),
376 strength_(strength)
377{
378 // nothing to do here
379}
380
381template <typename TCoordinate, typename TStrength>
383{
384 return strength_;
385}
386
387template <typename TCoordinate, typename TStrength>
388template <bool tLeftLargerThanRight>
390{
391 if constexpr (tLeftLargerThanRight)
392 {
393 return left.strength() > right.strength();
394 }
395 else
396 {
397 return left.strength() < right.strength();
398 }
399}
400
401template <typename T>
403 positionX_(-1),
404 strength_(T())
405{
406 // nothing to do here
407}
408
409template <typename T>
410inline NonMaximumSuppressionT<T>::StrengthCandidate::StrengthCandidate(const unsigned int x, const T& strength) :
411 positionX_(x),
412 strength_(strength)
413{
414 // nothing to do here
415}
416
417template <typename T>
419{
420 return positionX_;
421}
422
423template <typename T>
425{
426 return strength_;
427}
428
429template <typename T>
431{
432 *this = std::move(nonMaximumSuppression);
433}
434
435template <typename T>
437 width_(nonMaximumSuppression.width_),
438 rows_(nonMaximumSuppression.rows_)
439{
440 // nothing to do here
441}
442
443template <typename T>
444NonMaximumSuppressionT<T>::NonMaximumSuppressionT(const unsigned int width, const unsigned int height, const unsigned int yOffset) noexcept :
445 width_(width),
447{
448 // nothing to do here
449}
450
451template <typename T>
452inline unsigned int NonMaximumSuppressionT<T>::width() const
453{
454 return width_;
455}
456
457template <typename T>
458inline unsigned int NonMaximumSuppressionT<T>::height() const
459{
460 return (unsigned int)rows_.size();
461}
462
463template <typename T>
464inline unsigned int NonMaximumSuppressionT<T>::yOffset() const
465{
466 ocean_assert(rows_.firstIndex() >= 0);
467
468 return (unsigned int)(rows_.firstIndex());
469}
470
471template <typename T>
472inline void NonMaximumSuppressionT<T>::addCandidate(const unsigned int x, const unsigned int y, const T& strength)
473{
474 ocean_assert(x < width_);
475
476 ocean_assert(rows_.isValidIndex(y) && y >= (unsigned int)(rows_.firstIndex()) && y <= (unsigned int)(rows_.endIndex()));
477
478 if (rows_[y].empty())
479 {
480 rows_[y].reserve(128);
481 }
482
483 rows_[y].emplace_back(x, strength);
484}
485
486template <typename T>
487void NonMaximumSuppressionT<T>::addCandidates(const T* values, const unsigned int valuesPaddingElements, const unsigned int firstColumn, const unsigned int numberColumns, const unsigned int firstRow, const unsigned int numberRows, const T& minimalThreshold, Worker* worker)
488{
489 ocean_assert(values != nullptr);
490
491 ocean_assert(firstColumn + numberColumns <= width_);
492 ocean_assert(typename StrengthCandidateRows::Index(firstRow) >= rows_.firstIndex());
493 ocean_assert(typename StrengthCandidateRows::Index(firstRow + numberRows) <= rows_.endIndex());
494
495 const unsigned int valuesStrideElements = width_ + valuesPaddingElements;
496
497 if (worker)
498 {
499 worker->executeFunction(Worker::Function::create(*this, &NonMaximumSuppressionT<T>::addCandidatesSubset, values, valuesStrideElements, firstColumn, numberColumns, &minimalThreshold, 0u, 0u), firstRow, numberRows, 5u, 6u, 20u);
500 }
501 else
502 {
503 addCandidatesSubset(values, valuesStrideElements, firstColumn, numberColumns, &minimalThreshold, firstRow, numberRows);
504 }
505}
506
507template <typename T>
508inline void NonMaximumSuppressionT<T>::removeCandidatesRightFrom(const unsigned int x, const unsigned int y)
509{
510 ocean_assert(rows_.isValidIndex(y) && y >= (unsigned int)(rows_.firstIndex()) && y <= (unsigned int)(rows_.endIndex()));
511
512 StrengthCandidateRow& suppressionRow = rows_[y];
513
514 while (!suppressionRow.empty())
515 {
516 if (suppressionRow.back().x() >= x)
517 {
518 suppressionRow.pop_back();
519 }
520 else
521 {
522 break;
523 }
524 }
525}
526
527template <typename T>
528template <typename TCoordinate, typename TStrength, bool tStrictMaximum>
529bool NonMaximumSuppressionT<T>::suppressNonMaximum(const unsigned int firstColumn, const unsigned int numberColumns, const unsigned int firstRow, const unsigned int numberRows, StrengthPositions<TCoordinate, TStrength>& strengthPositions, Worker* worker, const PositionCallback<TCoordinate, TStrength>* positionCallback) const
530{
531 ocean_assert(firstColumn + numberColumns <= width_);
532 ocean_assert(firstRow >= (unsigned int)(rows_.firstIndex()) && firstRow + numberRows <= (unsigned int)(rows_.endIndex()));
533
534 if (firstColumn + numberColumns > width_)
535 {
536 return false;
537 }
538
539 if (firstRow >= (unsigned int)(rows_.endIndex()) || firstRow + numberRows <= (unsigned int)(rows_.firstIndex()))
540 {
541 return false;
542 }
543
544 strengthPositions.reserve(strengthPositions.size() + 128);
545
546 if (worker != nullptr)
547 {
548 Lock lock;
549 worker->executeFunction(Worker::Function::create(*this, &NonMaximumSuppressionT<T>::suppressNonMaximumSubset<TCoordinate, TStrength, tStrictMaximum>, &strengthPositions, firstColumn, numberColumns, &lock, positionCallback, 0u, 0u), firstRow, numberRows, 5u, 6u, 3u);
550 }
551 else
552 {
553 suppressNonMaximumSubset<TCoordinate, TStrength, tStrictMaximum>(&strengthPositions, firstColumn, numberColumns, nullptr, positionCallback, firstRow, numberRows);
554 }
555
556 return true;
557}
558
559template <typename T>
560void NonMaximumSuppressionT<T>::candidates(const unsigned int firstColumn, const unsigned int numberColumns, const unsigned int firstRow, const unsigned int numberRows, NonMaximumSuppressionT<T>::StrengthPositions<unsigned int, T>& strengthPositions)
561{
562 ocean_assert(firstColumn + numberColumns <= width_);
563 ocean_assert(firstRow + numberRows <= (unsigned int)(rows_.endIndex()));
564
565 const unsigned int endColumn = firstColumn + numberColumns;
566
567 for (unsigned int y = firstRow; y < firstRow + numberRows; ++y)
568 {
569 const StrengthCandidateRow& row = rows_[y];
570
571 for (const StrengthCandidate& candidate : row)
572 {
573 if (candidate.x() < firstColumn)
574 {
575 continue;
576 }
577
578 if (candidate.x() >= endColumn)
579 {
580 break;
581 }
582
583 strengthPositions.emplace_back(candidate.x(), y, candidate.strength());
584 }
585 }
586}
587
588template <typename T>
590{
591 for (ptrdiff_t n = rows_.firstIndex(); n < rows_.endIndex(); ++n)
592 {
593 rows_[n].clear();
594 }
595}
596
597template <typename T>
599{
600 if (this != &nonMaximumSuppression)
601 {
602 width_ = nonMaximumSuppression.width_;
603 nonMaximumSuppression.width_ = 0u;
604
605 rows_ = std::move(nonMaximumSuppression.rows_);
606 }
607
608 return *this;
609}
610
611template <typename T>
613{
614 if (this != &nonMaximumSuppression)
615 {
616 width_ = nonMaximumSuppression.width_;
617 rows_ = nonMaximumSuppression.rows_;
618 }
619
620 return *this;
621}
622
623template <typename T>
624template <typename TCoordinate, typename TStrength, bool tStrictMaximum>
626{
627 ocean_assert(width >= 1u && height >= 1u);
628 ocean_assert(radius >= TCoordinate(1));
629
630 const unsigned int binSize = std::max(10u, (unsigned int)(NumericT<TCoordinate>::ceil(radius)));
631
632 const unsigned int horizontalBins = std::max(1u, (width + binSize - 1u) / binSize);
633 const unsigned int verticalBins = std::max(1u, (height + binSize - 1u) / binSize);
634
635 ocean_assert(binSize * horizontalBins >= width);
636 ocean_assert(binSize * verticalBins >= height);
637
638 IndexGroups32 indexGroups(horizontalBins * verticalBins);
639
640 // distributing all strength positions into a regular grid to reduce search space later
641
642 for (size_t n = 0; n < strengthPositions.size(); ++n)
643 {
644 const VectorT2<TCoordinate>& position = strengthPositions[n];
645
646 ocean_assert((unsigned int)(position.x()) < width);
647 ocean_assert((unsigned int)(position.y()) < height);
648
649 const unsigned int xBin = (unsigned int)(position.x()) / binSize;
650 const unsigned int yBin = (unsigned int)(position.y()) / binSize;
651
652 ocean_assert(xBin <= horizontalBins);
653 ocean_assert(yBin <= verticalBins);
654
655 indexGroups[yBin * horizontalBins + xBin].emplace_back(Index32(n));
656 }
657
658 std::vector<uint8_t> validPositions(strengthPositions.size(), 1u);
659
660 const TCoordinate sqrRadius = radius * radius;
661
662 for (size_t nCandidate = 0; nCandidate < strengthPositions.size(); ++nCandidate)
663 {
664 if (validPositions[nCandidate] == 0u)
665 {
666 // the positions is already suppressed
667 continue;
668 }
669
670 const StrengthPosition<TCoordinate, TStrength>& candidatePosition = strengthPositions[nCandidate];
671
672 const unsigned int xCandidateBin = (unsigned int)(candidatePosition.x()) / binSize;
673 const unsigned int yCandidateBin = (unsigned int)(candidatePosition.y()) / binSize;
674
675 ocean_assert(xCandidateBin <= horizontalBins);
676 ocean_assert(yCandidateBin <= verticalBins);
677
678 bool checkNextCandidate = false;
679
680 for (unsigned int yBin = (unsigned int)(max(0, int(yCandidateBin) - 1)); !checkNextCandidate && yBin < min(yCandidateBin + 2u, verticalBins); ++yBin)
681 {
682 for (unsigned int xBin = (unsigned int)(max(0, int(xCandidateBin) - 1)); !checkNextCandidate && xBin < min(xCandidateBin + 2u, horizontalBins); ++xBin)
683 {
684 const Indices32& indices = indexGroups[yBin * horizontalBins + xBin];
685
686 for (const Index32& nTest : indices)
687 {
688 if (nTest == Index32(nCandidate))
689 {
690 continue;
691 }
692
693 const StrengthPosition<TCoordinate, TStrength>& testPosition = strengthPositions[nTest];
694
695 // we do not check whether test position is suppressed already (as the test position may still be the reason to suppress the candidate position)
696
697 if (candidatePosition.sqrDistance(testPosition) <= sqrRadius)
698 {
699 if (candidatePosition.strength() > testPosition.strength())
700 {
701 validPositions[nTest] = 0u;
702 }
703 else if (candidatePosition.strength() < testPosition.strength())
704 {
705 validPositions[nCandidate] = 0u;
706
707 checkNextCandidate = true;
708 break;
709 }
710 else
711 {
712 ocean_assert(candidatePosition.strength() == testPosition.strength());
713
714 if constexpr (tStrictMaximum)
715 {
716 // we suppress both elements, as we seek a strict maximum element
717
718 validPositions[nCandidate] = 0u;
719 validPositions[nTest] = 0u;
720
721 checkNextCandidate = true;
722 break;
723 }
724 else
725 {
726 // we will suppress one of both elements, as we accept a non-strict maximum element
727
728 if (candidatePosition.y() < testPosition.y() || (candidatePosition.y() == testPosition.y() && candidatePosition.x() < testPosition.x()))
729 {
730 // the candidate position will be suppressed as the test position is located to the bottom/right of the candidate position
731
732 validPositions[nCandidate] = 0u;
733
734 checkNextCandidate = true;
735 break;
736 }
737 else
738 {
739 ocean_assert(testPosition.y() < candidatePosition.y() || (testPosition.y() == candidatePosition.y() && testPosition.x() < candidatePosition.x()));
740
741 // the test position will be suppressed as the candidate position is located to the bottom/right of the test position
742
743 validPositions[nTest] = 0u;
744 }
745 }
746 }
747 }
748 }
749 }
750 }
751 }
752
754 remainingPositions.reserve(strengthPositions.size());
755
756 if (validIndices)
757 {
758 ocean_assert(validIndices->empty());
759
760 validIndices->clear();
761 validIndices->reserve(strengthPositions.size());
762
763 for (size_t n = 0; n < validPositions.size(); ++n)
764 {
765 if (validPositions[n])
766 {
767 remainingPositions.emplace_back(strengthPositions[n]);
768 validIndices->emplace_back(Index32(n));
769 }
770 }
771 }
772 else
773 {
774 for (size_t n = 0; n < validPositions.size(); ++n)
775 {
776 if (validPositions[n])
777 {
778 remainingPositions.emplace_back(strengthPositions[n]);
779 }
780 }
781 }
782
783 return remainingPositions;
784}
785
786template <typename T>
787template <typename TFloat>
788bool NonMaximumSuppressionT<T>::determinePrecisePeakLocation1(const T& leftValue, const T& middleValue, const T& rightValue, TFloat& location)
789{
790 static_assert(std::is_floating_point<TFloat>::value, "Invalid floating point data type!");
791
792 // f(x) = f(a) + f`(a) * (x - a)
793
794 // we expect our middle value to be located at a = 0:
795 // f(x) = f(0) + f`(0) * x
796
797 // 0 = f'(x)
798 // = f'(0) + f''(0) * x
799
800 // x = - f'(0) / f''(0)
801
802 // f`(x) = [-1 0 1] * 1/2
803 const TFloat df = (TFloat(rightValue) - TFloat(leftValue)) * TFloat(0.5);
804
805 // f``(x) = [1 -2 1] * 1/1
806 const TFloat dff = TFloat(leftValue) + TFloat(rightValue) - TFloat(middleValue) * TFloat(2);
807
809 {
810 location = TFloat(0);
811 return true;
812 }
813
814 const TFloat x = -df / dff;
815
816 if (x < TFloat(-1) || x > TFloat(1))
817 {
818 return false;
819 }
820
821 location = x;
822 return true;
823}
824
825template <typename T>
826template <typename TFloat>
827bool NonMaximumSuppressionT<T>::determinePrecisePeakLocation2(const T* const topValues, const T* const centerValues, const T* const bottomValues, VectorT2<TFloat>& location)
828{
829 static_assert(std::is_floating_point<TFloat>::value, "Invalid floating point data type!");
830
831 const T& value00 = topValues[0];
832 const T& value01 = topValues[1];
833 const T& value02 = topValues[2];
834
835 const T& value10 = centerValues[0];
836 const T& value11 = centerValues[1];
837 const T& value12 = centerValues[2];
838
839 const T& value20 = bottomValues[0];
840 const T& value21 = bottomValues[1];
841 const T& value22 = bottomValues[2];
842
843#if 0
844 // some response values may not perfectly follow the peak criteria so that we do not use the asserts by default
845 ocean_assert(value11 >= value00 && value11 >= value01 && value11 >= value02);
846 ocean_assert(value11 >= value10 && value11 >= value12);
847 ocean_assert(value11 >= value20 && value11 >= value21 && value11 >= value22);
848#endif
849
850 // [-1 0 1] * 1/2
851 const TFloat dx = TFloat(value12 - value10) * TFloat(0.5);
852 const TFloat dy = TFloat(value21 - value01) * TFloat(0.5);
853
854 // [1 -2 1] * 1/1
855 const TFloat dxx = TFloat(value12 + value10 - value11 * TFloat(2));
856 const TFloat dyy = TFloat(value21 + value01 - value11 * TFloat(2));
857
858 // [ 1 0 -1 ]
859 // [ 0 0 0 ] * 1/4
860 // [-1 0 1 ]
861
862 const TFloat dxy = TFloat(value22 + value00 - value20 - value02) * TFloat(0.25);
863
864 const TFloat denominator = dxx * dyy - dxy * dxy;
865
866 if (NumericT<TFloat>::isEqualEps(denominator))
867 {
868 location = VectorT2<TFloat>(0, 0);
869 return true;
870 }
871
872 const TFloat factor = TFloat(1) / denominator;
873
874 const TFloat offsetX = -(dyy * dx - dxy * dy) * factor;
875 const TFloat offsetY = -(dxx * dy - dxy * dx) * factor;
876
877 if (offsetX < TFloat(-1) || offsetX > TFloat(1) || offsetY < TFloat(-1) || offsetY > TFloat(1))
878 {
879 return false;
880 }
881
882 location = VectorT2<TFloat>(offsetX, offsetY);
883 return true;
884}
885
886template <typename T>
887void NonMaximumSuppressionT<T>::addCandidatesSubset(const T* values, const unsigned int valuesStrideElements, const unsigned int firstColumn, const unsigned int numberColumns, const T* minimalThreshold, const unsigned int firstRow, const unsigned int numberRows)
888{
889 ocean_assert(values != nullptr);
890 ocean_assert(valuesStrideElements >= width_);
891 ocean_assert(firstColumn + numberColumns <= width_);
892
893 ocean_assert(typename StrengthCandidateRows::Index(firstRow) >= rows_.firstIndex());
894 ocean_assert(typename StrengthCandidateRows::Index(firstRow + numberRows) <= rows_.endIndex());
895
896 const T localThreshold = *minimalThreshold;
897
898 values += firstRow * valuesStrideElements;
899
900 for (unsigned int y = firstRow; y < firstRow + numberRows; ++y)
901 {
902 for (unsigned int x = firstColumn; x < firstColumn + numberColumns; ++x)
903 {
904 if (values[x] >= localThreshold)
905 {
906 addCandidate(x, y, values[x]);
907 }
908 }
909
910 values += valuesStrideElements;
911 }
912}
913
914template <typename T>
915template <typename TCoordinate, typename TStrength, bool tStrictMaximum>
916void NonMaximumSuppressionT<T>::suppressNonMaximumSubset(StrengthPositions<TCoordinate, TStrength>* strengthPositions, const unsigned int firstColumn, const unsigned int numberColumns, Lock* lock, const PositionCallback<TCoordinate, TStrength>* positionCallback, const unsigned int firstRow, const unsigned int numberRows) const
917{
918 ocean_assert(strengthPositions);
919
920 ocean_assert(firstColumn + numberColumns <= width_);
921 ocean_assert(firstRow >= (unsigned int)rows_.firstIndex());
922 ocean_assert(firstRow + numberRows <= (unsigned int)rows_.endIndex());
923
924 if (numberColumns < 3u || numberRows < 3u)
925 {
926 return;
927 }
928
929 const unsigned int firstCenterColumn = max(1u, firstColumn);
930 const unsigned int endCenterColumn = min(firstColumn + numberColumns, width_ - 1u);
931
932 const unsigned int firstCenterRow = max((unsigned int)rows_.firstIndex() + 1u, firstRow);
933 const unsigned int endCenterRow = min(firstRow + numberRows, (unsigned int)rows_.lastIndex());
934
935 ocean_assert(firstCenterRow >= 1u);
936
937 StrengthPositions<TCoordinate, TStrength> localStrengthPositions;
938 localStrengthPositions.reserve(100);
939
940 for (unsigned int y = firstCenterRow; y < endCenterRow; ++y)
941 {
942 const StrengthCandidateRow& row0 = rows_[y - 1u];
943 const StrengthCandidateRow& row1 = rows_[y + 0u];
944 const StrengthCandidateRow& row2 = rows_[y + 1u];
945
946 typename StrengthCandidateRow::const_iterator i0 = row0.begin();
947 typename StrengthCandidateRow::const_iterator i2 = row2.begin();
948
949 typename StrengthCandidateRow::const_iterator i1Minus = row1.end();
950 typename StrengthCandidateRow::const_iterator i1Plus = row1.size() > 1 ? row1.begin() + 1 : row1.end();
951
952 for (typename StrengthCandidateRow::const_iterator i1 = row1.begin(); i1 != row1.end(); ++i1)
953 {
954 ocean_assert(i1->x() >= 0u && i1->x() + 1u <= width_);
955
956 // check left candidate (west)
957 if (i1->x() >= firstCenterColumn && i1->x() < endCenterColumn && (i1Minus == row1.end() || i1Minus->x() + 1u != i1->x() || (tStrictMaximum && i1Minus->strength() < i1->strength()) || (!tStrictMaximum && i1Minus->strength() <= i1->strength())))
958 {
959 // check right candidate (east)
960 if (i1Plus == row1.end() || i1Plus->x() != i1->x() + 1u || i1Plus->strength() < i1->strength())
961 {
962 // set the top row iterator to the right position
963 while (i0 != row0.end())
964 {
965 if (i0->x() + 1u < i1->x())
966 {
967 ++i0;
968 }
969 else
970 {
971 break;
972 }
973 }
974
975 // now i0 should point at least to the north west pixel position (or more far east)
976 ocean_assert(i0 == row0.end() || i0->x() + 1u >= i1->x());
977
978 if (i0 != row0.end() && i0->x() <= i1->x() + 1u)
979 {
980 ocean_assert(i0->x() + 1u == i1->x() || i0->x() == i1->x() || i0->x() - 1u == i1->x());
981
982 if ((tStrictMaximum && i0->strength() >= i1->strength()) || (!tStrictMaximum && i0->strength() > i1->strength()))
983 {
984 goto next;
985 }
986
987 // check if there is a further candidate in the north row
988
989 const typename StrengthCandidateRow::const_iterator i0Plus = i0 + 1;
990
991 if (i0Plus != row0.end() && i0Plus->x() <= i1->x() + 1u)
992 {
993 if ((tStrictMaximum && i0Plus->strength() >= i1->strength()) || (!tStrictMaximum && i0Plus->strength() > i1->strength()))
994 {
995 goto next;
996 }
997
998 // check if there is a further candidate in the north row
999
1000 const typename StrengthCandidateRow::const_iterator i0PlusPlus = i0Plus + 1;
1001
1002 if (i0PlusPlus != row0.end() && i0PlusPlus->x() <= i1->x() + 1u)
1003 {
1004 ocean_assert(i0PlusPlus->x() == i1->x() + 1u);
1005
1006 if ((tStrictMaximum && i0PlusPlus->strength() >= i1->strength()) || (!tStrictMaximum && i0PlusPlus->strength() > i1->strength()))
1007 {
1008 goto next;
1009 }
1010 }
1011 }
1012 }
1013
1014
1015 // set the bottom row iterator to the right position
1016 while (i2 != row2.end())
1017 {
1018 if (i2->x() + 1u < i1->x())
1019 {
1020 ++i2;
1021 }
1022 else
1023 {
1024 break;
1025 }
1026 }
1027
1028 // now i2 should point at least to the south west pixel position (or more far east)
1029 ocean_assert(i2 == row2.end() || i2->x() + 1u >= i1->x());
1030
1031 if (i2 != row2.end() && i2->x() <= i1->x() + 1u)
1032 {
1033 ocean_assert(i2->x() + 1u == i1->x() || i2->x() == i1->x() || i2->x() - 1u == i1->x());
1034
1035 if (i2->x() + 1u == i1->x())
1036 {
1037 // i2 points to the south west pixel
1038
1039 if ((tStrictMaximum && i2->strength() >= i1->strength()) || (!tStrictMaximum && i2->strength() > i1->strength()))
1040 {
1041 goto next;
1042 }
1043 }
1044 else
1045 {
1046 if (i2->strength() >= i1->strength())
1047 {
1048 goto next;
1049 }
1050 }
1051
1052 // check if there is a further candidate in the south row
1053
1054 const typename StrengthCandidateRow::const_iterator i2Plus = i2 + 1;
1055
1056 if (i2Plus != row2.end() && i2Plus->x() <= i1->x() + 1u)
1057 {
1058 if (i2Plus->strength() >= i1->strength())
1059 {
1060 goto next;
1061 }
1062
1063 // check if there is a further candidate in the south row
1064
1065 const typename StrengthCandidateRow::const_iterator i2PlusPlus = i2Plus + 1;
1066
1067 if (i2PlusPlus != row2.end() && i2PlusPlus->x() <= i1->x() + 1u)
1068 {
1069 ocean_assert(i2PlusPlus->x() == i1->x() + 1u);
1070
1071 if (i2PlusPlus->strength() >= i1->strength())
1072 {
1073 goto next;
1074 }
1075 }
1076 }
1077 }
1078
1079 if (positionCallback)
1080 {
1081 TCoordinate preciseX, preciseY;
1082 TStrength preciseStrength;
1083 if ((*positionCallback)(i1->x(), y, i1->strength(), preciseX, preciseY, preciseStrength))
1084 {
1085 localStrengthPositions.emplace_back(preciseX, preciseY, preciseStrength);
1086 }
1087 }
1088 else
1089 {
1090 localStrengthPositions.emplace_back(TCoordinate(i1->x()), TCoordinate(y), i1->strength());
1091 }
1092 }
1093 }
1094
1095next:
1096
1097 i1Minus = i1;
1098
1099 if (i1Plus != row1.end())
1100 {
1101 ++i1Plus;
1102 }
1103 }
1104 }
1105
1106 const OptionalScopedLock scopedLock(lock);
1107
1108 strengthPositions->insert(strengthPositions->end(), localStrengthPositions.begin(), localStrengthPositions.end());
1109}
1110
1111}
1112
1113}
1114
1115#endif // META_OCEAN_CV_NON_MAXIMUM_SUPPRESSION_H
This class extends a 2D position by a third parameter storing a strength value.
Definition NonMaximumSuppression.h:42
const TStrength & strength() const
Returns the strength parameter of this object.
Definition NonMaximumSuppression.h:382
StrengthPosition()=default
Creates a new object with default strength parameter.
TStrength strength_
Strength parameter of this object.
Definition NonMaximumSuppression.h:77
static bool compareStrength(const StrengthPosition< TCoordinate, TStrength > &left, const StrengthPosition< TCoordinate, TStrength > &right)
Compares the strength value of two objects.
Definition NonMaximumSuppression.h:389
This class provides base functionality and type definitions for non-maximum-suppression operations.
Definition NonMaximumSuppression.h:32
std::vector< StrengthPosition< TCoordinate, TStrength > > StrengthPositions
Definition of a vector holding strength pixel positions.
Definition NonMaximumSuppression.h:84
This class holds the horizontal position and strength parameter of an interest pixel.
Definition NonMaximumSuppression.h:126
StrengthCandidate()
Creates a new candidate object.
Definition NonMaximumSuppression.h:402
unsigned int positionX_
Horizontal position of this object.
Definition NonMaximumSuppression.h:156
const T & strength() const
Returns the strength parameter of this object.
Definition NonMaximumSuppression.h:424
unsigned int x() const
Returns the horizontal position of this candidate object.
Definition NonMaximumSuppression.h:418
T strength_
Strength parameter of this object.
Definition NonMaximumSuppression.h:159
This class implements the possibility to find local maximum in a 2D array by applying a non-maximum-s...
Definition NonMaximumSuppression.h:104
unsigned int yOffset() const
Returns the optional offset in the vertical direction.
Definition NonMaximumSuppression.h:464
void suppressNonMaximumSubset(StrengthPositions< TCoordinate, TStrength > *strengthPositions, const unsigned int firstColumn, const unsigned int firstRow, Lock *lock, const PositionCallback< TCoordinate, TStrength > *positionCallback, const unsigned int numberColumns, const unsigned int numberRows) const
Applies a non-maximum-suppression search on a subset of a given 2D frame in a 3x3 neighborhood (eight...
Definition NonMaximumSuppression.h:916
void reset()
Removes the gathered non-maximum suppression information so that this object can be reused again (for...
Definition NonMaximumSuppression.h:589
static bool determinePrecisePeakLocation2(const T *const topValues, const T *const centerValues, const T *const bottomValues, VectorT2< TFloat > &location)
Determines the precise peak location in 2D space for nine discrete neighboring measurements at locati...
Definition NonMaximumSuppression.h:827
void addCandidate(const unsigned int x, const unsigned int y, const T &strength)
Adds a new candidate to this object.
Definition NonMaximumSuppression.h:472
static StrengthPositions< TCoordinate, TStrength > suppressNonMaximum(const unsigned int width, const unsigned int height, const StrengthPositions< TCoordinate, TStrength > &strengthPositions, const TCoordinate radius, Indices32 *validIndices=nullptr)
Applies a non-maximum-suppression based on already existing strength positions (just with a custom su...
void removeCandidatesRightFrom(const unsigned int x, const unsigned int y)
Removes all candidates from a specified row having a horizontal location equal or larger than a speci...
Definition NonMaximumSuppression.h:508
void candidates(const unsigned int firstColumn, const unsigned int numberColumns, const unsigned int firstRow, const unsigned int numberRows, StrengthPositions< unsigned int, T > &strengthPositions)
Returns all gathered candidates of this object.
Definition NonMaximumSuppression.h:560
unsigned int height() const
Returns the height of this object.
Definition NonMaximumSuppression.h:458
bool suppressNonMaximum(const unsigned int firstColumn, const unsigned int numberColumns, const unsigned int firstRow, const unsigned int numberRows, StrengthPositions< TCoordinate, TStrength > &strengthPositions, Worker *worker=nullptr, const PositionCallback< TCoordinate, TStrength > *positionCallback=nullptr) const
Applies a non-maximum-suppression search on a given 2D frame in a 3x3 neighborhood (eight neighbors).
Definition NonMaximumSuppression.h:529
static bool determinePrecisePeakLocation1(const T &leftValue, const T &middleValue, const T &rightValue, TFloat &location)
Determines the precise peak location in 1D space for three discrete neighboring measurements at locat...
Definition NonMaximumSuppression.h:788
NonMaximumSuppressionT< T > & operator=(NonMaximumSuppressionT< T > &&nonMaximumSuppression)
Move operator.
Definition NonMaximumSuppression.h:598
std::vector< StrengthCandidate > StrengthCandidateRow
Definition of a vector holding strength candidate objects.
Definition NonMaximumSuppression.h:165
NonMaximumSuppressionT(NonMaximumSuppressionT< T > &&nonMaximumSuppression) noexcept
Move constructor.
Definition NonMaximumSuppression.h:430
void addCandidates(const T *values, const unsigned int valuesPaddingElements, const unsigned int firstColumn, const unsigned int numberColumns, const unsigned int firstRow, const unsigned int numberRows, const T &minimalThreshold, Worker *worker)
Adds new candidates to this object from a given buffer providing one value for each bin/pixel of this...
Definition NonMaximumSuppression.h:487
void addCandidatesSubset(const T *values, const unsigned int valuesStrideElements, const unsigned int firstColumn, const unsigned int numberColumns, const T *minimalThreshold, const unsigned int firstRow, const unsigned int numberRows)
Adds new candidates to this object from a subset of a given buffer providing one value for each bin/p...
Definition NonMaximumSuppression.h:887
StrengthCandidateRows rows_
All candidate rows.
Definition NonMaximumSuppression.h:370
unsigned int width_
Width of this object.
Definition NonMaximumSuppression.h:367
unsigned int width() const
Returns the width of this object.
Definition NonMaximumSuppression.h:452
This class implements a container for callback functions.
Definition Callback.h:3454
static Caller< void > create(CT &object, typename MemberFunctionPointerMaker< CT, 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 member function with no function parameter.
Definition Caller.h:3024
This class implements a recursive lock object.
Definition Lock.h:31
This class provides basic numeric functionalities.
Definition Numeric.h:57
This class implements an optional recursive scoped lock object locking the lock object only if it's d...
Definition Lock.h:387
bool isValidIndex(const Index index) const
Returns whether a specific index is valid for this vector and matches to the current offset layout.
Definition ShiftVector.h:646
size_t size() const
Returns the number of elements that are stored by this object.
Definition ShiftVector.h:490
Index endIndex() const
Returns the index of the element behind the last (excluding) element of this object.
Definition ShiftVector.h:435
std::ptrdiff_t Index
Definition of an element index.
Definition ShiftVector.h:38
void clear()
Clears this object, the specified index shift will be untouched.
Definition ShiftVector.h:658
Iterator begin()
Returns the iterator for the first data element.
Definition ShiftVector.h:670
Index lastIndex() const
Returns the index of the last (including) element of this object.
Definition ShiftVector.h:422
Index firstIndex() const
Returns the index of the first element of this object.
Definition ShiftVector.h:416
This class implements a vector with two elements.
Definition Vector2.h:96
const TCoordinate & x() const noexcept
Returns the x value.
Definition Vector2.h:710
const TCoordinate & y() const noexcept
Returns the y value.
Definition Vector2.h:722
T sqrDistance(const VectorT2< T > &right) const
Returns the square distance between this 2D position and a second 2D position.
Definition Vector2.h:645
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.
std::vector< Index32 > Indices32
Definition of a vector holding 32 bit index values.
Definition Base.h:96
std::vector< Indices32 > IndexGroups32
Definition of a vector holding 32 bit indices, so we have groups of indices.
Definition Base.h:102
uint32_t Index32
Definition of a 32 bit index value.
Definition Base.h:84
The namespace covering the entire Ocean framework.
Definition Accessor.h:15