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
14#include "ocean/base/Worker.h"
15
16#include "ocean/math/Vector2.h"
17
18#include <functional>
19
20namespace Ocean
21{
22
23namespace CV
24{
25
26/**
27 * This class provides base functionality and type definitions for non-maximum-suppression operations.
28 * It defines common types like StrengthPosition and StrengthPositions used by the template class NonMaximumSuppressionT.
29 * @see NonMaximumSuppressionT
30 * @ingroup cv
31 */
33{
34 public:
35
36 /**
37 * Definition of individual suppression modes for extremum search.
38 */
39 enum SuppressionMode : uint32_t
40 {
41 /// Finds the maximum values, any value is allowed.
43 /// Finds the minimum values, any value is allowed.
45 /// Finds the maximum values, only positive values are allowed (> 0).
47 /// Finds the minimum values, only negative values are allowed (< 0).
49 };
50
51 /**
52 * This class extends a 2D position by a third parameter storing a strength value.
53 * @tparam TCoordinate The data type of a scalar coordinate
54 * @tparam TStrength The data type of the strength parameter
55 */
56 template <typename TCoordinate, typename TStrength>
57 class StrengthPosition : public VectorT2<TCoordinate>
58 {
59 public:
60
61 /**
62 * Creates a new object with default strength parameter.
63 */
64 StrengthPosition() = default;
65
66 /**
67 * Creates a new object with explicit position and strength parameter.
68 * @param x Horizontal position
69 * @param y Vertical position
70 * @param strength The strength parameter
71 */
72 inline StrengthPosition(const TCoordinate x, const TCoordinate y, const TStrength& strength);
73
74 /**
75 * Returns the strength parameter of this object.
76 * @return Strength parameter
77 */
78 inline const TStrength& strength() const;
79
80 /**
81 * Compares the strength value of two objects.
82 * @param left The left object to compare
83 * @param right The right object to compare
84 * @return True, if so
85 * @tparam tLeftLargerThanRight True, to return whether left's strength is larger than right's strength; False, to return whether right is larger than left
86 */
87 template <bool tLeftLargerThanRight = true>
89
90 private:
91
92 /// Strength parameter of this object.
93 TStrength strength_ = TStrength();
94 };
95
96 /**
97 * Definition of a vector holding strength pixel positions.
98 */
99 template <typename TCoordinate, typename TStrength>
100 using StrengthPositions = std::vector<StrengthPosition<TCoordinate, TStrength>>;
101};
102
103/**
104 * This class implements the possibility to find local maximum in a 2D array by applying a non-maximum-suppression search.
105 * The search is done within a 3x3 neighborhood (centered around the point of interest).<br>
106 * Use this class to determine e.g. reliable feature points.<br>
107 * The class supports bin accuracy (pixel accuracy) and sub-bin accuracy (sub-pixel accuracy).
108 *
109 * The non-maximum-suppression search is implemented by a vertical list holding maps of horizontal array elements.<br>
110 * The performance depends on the number of elements inserted into the individual maps.<br>
111 * Thus, do not add data elements with negligible value.
112 *
113 * 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>
114 * The performance boost comes with a simultaneous determination of filter responses and the insertion of possible candidates for maximum locations.
115 * @tparam T The data type of the individual elements that are applied for the non-maximum-suppression search.
116 * @ingroup cv
117 */
118template <typename T>
120{
121 public:
122
123 /**
124 * Definition of a callback function used to determine the precise sub-pixel position of a specific point.
125 * @param x The horizontal position of the point, with range [0, width - 1]
126 * @param y The vertical position of the point, with range [0, height - 1]
127 * @param strength The strength parameter already known for the position of the point
128 * @param preciseX The resulting precise horizontal position of the point, with range (x - 1, x + 1)
129 * @param preciseY The resulting precise vertical position of the point, with range (y - 1, y + 1)
130 * @param preciseStrength The resulting precise strength parameter of the precise point
131 * @return True, if the precise position could be determined
132 * @tparam TCoordinate The data type of a scalar coordinate
133 * @tparam TStrength The data type of the strength parameter
134 */
135 template <typename TCoordinate, typename TStrength>
136 using PositionCallback = std::function<bool(const unsigned int x, const unsigned int y, const TStrength strength, TCoordinate& preciseX, TCoordinate& preciseY, TStrength& preciseStrength)>;
137
138 protected:
139
140 /**
141 * This class holds the horizontal position and strength parameter of an interest pixel.
142 */
144 {
145 public:
146
147 /**
148 * Creates a new candidate object.
149 */
150 inline StrengthCandidate();
151
152 /**
153 * Creates a new candidate object with horizontal position and strength parameter.
154 * @param x Horizontal position in pixel
155 * @param strength The strength parameter
156 */
157 inline StrengthCandidate(const unsigned int x, const T& strength);
158
159 /**
160 * Returns the horizontal position of this candidate object.
161 * @return Horizontal position in pixel
162 */
163 inline unsigned int x() const;
164
165 /**
166 * Returns the strength parameter of this object.
167 * @return Strength parameter
168 */
169 inline const T& strength() const;
170
171 private:
172
173 /// Horizontal position of this object.
174 unsigned int positionX_ = (unsigned int)(-1);
175
176 /// Strength parameter of this object.
177 T strength_ = T();
178 };
179
180 /**
181 * Definition of a vector holding strength candidate objects.
182 */
183 using StrengthCandidateRow = std::vector<StrengthCandidate>;
184
185 /**
186 * Definition of a vector holding a vector of strength candidates.
187 */
189
190 public:
191
192 /**
193 * Move constructor.
194 * @param nonMaximumSuppression The object to be moved
195 */
196 NonMaximumSuppressionT(NonMaximumSuppressionT<T>&& nonMaximumSuppression) noexcept;
197
198 /**
199 * Copy constructor.
200 * @param nonMaximumSuppression The object to be moved
201 */
202 NonMaximumSuppressionT(const NonMaximumSuppressionT<T>& nonMaximumSuppression) noexcept;
203
204 /**
205 * Creates a new maximum suppression object with a predefined size.
206 * @param width The width of this object in pixel, with range [3, infinity)
207 * @param height The height of this object in pixel, with range [3, infinity)
208 * @param yOffset Optional offset in the vertical direction moving the suppression region by the specified number of rows, with range [0, infinity)
209 */
210 NonMaximumSuppressionT(const unsigned int width, const unsigned int height, const unsigned int yOffset = 0u) noexcept;
211
212 /**
213 * Returns the width of this object.
214 * @return Width in pixel
215 */
216 inline unsigned int width() const;
217
218 /**
219 * Returns the height of this object.
220 * @return Height in pixel
221 */
222 inline unsigned int height() const;
223
224 /**
225 * Returns the optional offset in the vertical direction.
226 * @return Optional vertical direction offset, 0 by default
227 */
228 inline unsigned int yOffset() const;
229
230 /**
231 * Adds a new candidate to this object.
232 * Beware: Due to performance issues do no add candidates with negligible strength parameter.
233 * @param x Horizontal position in pixel, with range [0, width() - 1]
234 * @param y Vertical position in pixel, with range [yOffset(), yOffset() + height() - 1]
235 * @param strength The strength parameter
236 * @see addCandidates(), removeCandidatesFromRight().
237 */
238 inline void addCandidate(const unsigned int x, const unsigned int y, const T& strength);
239
240 /**
241 * Adds new candidates to this object from a given buffer providing one value for each bin/pixel of this object.
242 * 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>
243 * @param values The from which candidates will be added, must be width() * height() elements
244 * @param valuesPaddingElements The number of padding elements at the end of each values row, in elements, with range [0, infinity)
245 * @param firstColumn First column to be handled, with range [0, width() - 1]
246 * @param numberColumns Number of columns to be handled, with range [1, width() - firstColumn]
247 * @param firstRow First row to be handled, with range [yOffset(), height() - 1]
248 * @param numberRows Number of rows to be handled, with range [1, height() - firstRow]
249 * @param minimalThreshold The minimal threshold so that a value counts as candidate
250 * @param worker Optional worker object to distribute the computation
251 * @see addCandidate(), removeCandidatesFromRight().
252 */
253 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);
254
255 /**
256 * Removes all candidates from a specified row having a horizontal location equal or larger than a specified coordinate.
257 * @param x The horizontal coordinate specifying which candidates will be removed, all candidates with horizontal location >= x will be removed, with range [0, infinity)
258 * @param y The index of the row in which the candidates will be removed, with range [yOffset(), yOffset() + height() - 1]
259 */
260 inline void removeCandidatesRightFrom(const unsigned int x, const unsigned int y);
261
262 /**
263 * Returns the strength value of a candidate at a specified position.
264 * The function uses binary search on the x-coordinate within the specified row, as candidates are sorted by horizontal position.
265 * @param x The horizontal position to look up, with range [0, width() - 1]
266 * @param y The vertical position (row) to look up, with range [yOffset(), yOffset() + height() - 1]
267 * @param strength The resulting strength value if a candidate exists at the specified position
268 * @return True, if a candidate exists at the specified position; False, if no candidate was found
269 */
270 bool candidate(const unsigned int x, const unsigned int y, T& strength) const;
271
272 /**
273 * Returns all gathered candidates of this object.
274 * The resulting candidates are raw candidates without any suppression.
275 * @param firstColumn The first column from which candidates will be returned, with range [0, width() - 1]
276 * @param numberColumns The number of columns for which candidates will be returned, with range [1, width() - firstColumn]
277 * @param firstRow The first row from which candidates will be returned, with range [yOffset(), height() - 1]
278 * @param numberRows The number of rows for which candidates will be returned, with range [1, height() - firstRow]
279 * @param strengthPositions The resulting strength positions
280 */
281 void candidates(const unsigned int firstColumn, const unsigned int numberColumns, const unsigned int firstRow, const unsigned int numberRows, StrengthPositions<unsigned int, T>& strengthPositions);
282
283 /**
284 * Applies a non-maximum-suppression search on a given 2D frame in a 3x3 neighborhood (eight neighbors).
285 * 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>
286 * @param firstColumn First column to be handled, with range [1, width() - 1)
287 * @param numberColumns Number of columns to be handled
288 * @param firstRow First row to be handled, with range [yOffset() + 1, height() - 1)
289 * @param numberRows Number of rows to be handled
290 * @param strengthPositions The resulting non maximum suppressed positions including the strength parameters, will be appended
291 * @param worker Optional worker object to distribute the computation
292 * @param positionCallback Optional callback function allowing to determine the precise position of the individual maximum value positions
293 * @return True, if succeeded
294 * @tparam TCoordinate The data type of a scalar coordinate
295 * @tparam TStrength The data type of the strength parameter
296 * @tparam tStrictMaximum True, to search for a strict maximum (larger than all eight neighbors); False, to allow equal values in the upper left neighborhood
297 * @tparam tSuppressionMode The suppression mode defining whether to find maximum/minimum and whether to enforce sign constraints
298 */
299 template <typename TCoordinate, typename TStrength, bool tStrictMaximum = true, NonMaximumSuppression::SuppressionMode tSuppressionMode = NonMaximumSuppression::SM_MAXIMUM>
300 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;
301
302 /**
303 * Removes the gathered non-maximum suppression information so that this object can be reused again (for the same task with same resolution etc.).
304 * The allocated memory will remain so that reusing this object may improve performance.
305 */
306 void reset();
307
308 /**
309 * Move operator.
310 * @param nonMaximumSuppression Object to be moved
311 * @return Reference to this object
312 */
314
315 /**
316 * Copy operator.
317 * @param nonMaximumSuppression The object to be copied
318 * @return Reference to this object
319 */
321
322 /**
323 * Applies a non-maximum-suppression based on already existing strength positions (just with a custom suppression radius) e.g., as a post-processing step.
324 * @param width The width of the image/domain in which the strength positions are located, e.g., in pixel, with range [1, infinity)
325 * @param height The height of the image/domain in which the strength positions are located, e.g., in pixel, with range [1, infinity)
326 * @param strengthPositions The strength positions for which a custom suppression-radius will be applied
327 * @param radius The suppression radius to be applied, with range [1, infinity)
328 * @param validIndices Optional resulting indices of all strength positions which remain after suppression
329 * @return The resulting strength positions
330 * @tparam TCoordinate The data type of a scalar coordinate
331 * @tparam TStrength The data type of the strength parameter
332 * @tparam tStrictMaximum True, to search for a strict maximum (larger than all eight neighbors); False, to allow equal values in the upper left neighborhood
333 */
334 template <typename TCoordinate, typename TStrength, bool tStrictMaximum>
335 static StrengthPositions<TCoordinate, TStrength> suppressNonMaximum(const unsigned int width, const unsigned int height, const StrengthPositions<TCoordinate, TStrength>& strengthPositions, const TCoordinate radius, Indices32* validIndices = nullptr);
336
337 /**
338 * Determines the precise peak location in 1D space for three discrete neighboring measurements at location x == 0.
339 * The precise peak is determined based on the first and second derivatives of the measurement values.
340 * @param leftValue The left discrete value, e.g., at location x - 1, with range (-infinity, middleValue]
341 * @param middleValue The middle discrete value, e.g., at location x, with range (-infinity, infinity)
342 * @param rightValue The discrete value, e.g., at location x + 1, with range (-infinity, middleValue]
343 * @param location The location of the precise peak of all values, with range (-1, 1)
344 * @return True, if succeeded
345 * @tparam TFloat The floating point data type to be used for calculation, either 'float' or 'double'
346 */
347 template <typename TFloat>
348 static bool determinePrecisePeakLocation1(const T& leftValue, const T& middleValue, const T& rightValue, TFloat& location);
349
350 /**
351 * Determines the precise peak location in 2D space for nine discrete neighboring measurements at location x == 0, y == 0.
352 * The precise peak is determined based on the first and second derivatives of the measurement values.
353 * @param topValues The three discrete values in the top row, must be valid
354 * @param centerValues The three discrete values in the center row, must be valid
355 * @param bottomValues The three discrete values in the bottom row, must be valid
356 * @param location The location of the precise peak of all values, with range (-1, 1)
357 * @return True, if succeeded
358 * @tparam TFloat The floating point data type to be used for calculation, either 'float' or 'double'
359 */
360 template <typename TFloat>
361 static bool determinePrecisePeakLocation2(const T* const topValues, const T* const centerValues, const T* const bottomValues, VectorT2<TFloat>& location);
362
363 private:
364
365 /**
366 * Adds new candidates to this object from a subset of a given buffer providing one value for each bin/pixel of this object.
367 * @param values The from which candidates will be added, must be width() * height() elements
368 * @param valuesStrideElements The number of elements between two values rows, in elements, with range [0, infinity)
369 * @param minimalThreshold The minimal threshold so that a value counts as candidate
370 * @param firstColumn First column to be handled, with range [0, width() - 1]
371 * @param numberColumns Number of columns to be handled, with range [1, width() - firstColumn]
372 * @param firstRow The first row in the buffer from which the candidates will be added, with range [yOffset(), height())
373 * @param numberRows The number of rows to be handled, with range [1, height() - firstRow]
374 * @see addCandidates().
375 */
376 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);
377
378 /**
379 * Applies a non-maximum-suppression search on a subset of a given 2D frame in a 3x3 neighborhood (eight neighbors).
380 * 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>
381 * @param strengthPositions Resulting non maximum suppressed positions including the strength parameters
382 * @param firstColumn First column to be handled
383 * @param numberColumns Number of columns to be handled
384 * @param lock Optional lock when executed in parallel
385 * @param positionCallback Optional callback function allowing to determine the precise position of the individual maximum value positions
386 * @param firstRow First row to be handled
387 * @param numberRows Number of rows to be handled
388 * @tparam TCoordinate The data type of a scalar coordinate
389 * @tparam TStrength The data type of the strength parameter
390 * @tparam tStrictMaximum True, to search for a strict maximum (larger than all eight neighbors); False, to allow equal values in the upper left neighborhood
391 * @tparam tOnlyPositive True, to only consider positive values (> 0); False, to consider all values
392 */
393 template <typename TCoordinate, typename TStrength, bool tStrictMaximum, bool tOnlyPositive = false>
394 void 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;
395
396 /**
397 * Applies a non-minimum-suppression search on a subset of a given 2D frame in a 3x3 neighborhood (eight neighbors).
398 * This function allows to determine the precise position of the individual minimum value positions by application of a callback function determining the individual positions.<br>
399 * @param strengthPositions Resulting non minimum suppressed positions including the strength parameters
400 * @param firstColumn First column to be handled
401 * @param numberColumns Number of columns to be handled
402 * @param lock Optional lock when executed in parallel
403 * @param positionCallback Optional callback function allowing to determine the precise position of the individual minimum value positions
404 * @param firstRow First row to be handled
405 * @param numberRows Number of rows to be handled
406 * @tparam TCoordinate The data type of a scalar coordinate
407 * @tparam TStrength The data type of the strength parameter
408 * @tparam tStrictMaximum True, to search for a strict minimum (smaller than all eight neighbors); False, to allow equal values in the lower right neighborhood
409 * @tparam tOnlyNegative True, to only consider negative values (< 0); False, to consider all values
410 */
411 template <typename TCoordinate, typename TStrength, bool tStrictMaximum, bool tOnlyNegative = false>
412 void suppressNonMinimumSubset(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;
413
414 private:
415
416 /// Width of this object.
417 unsigned int width_ = 0u;
418
419 /// All candidate rows.
421};
422
423template <typename TCoordinate, typename TStrength>
425 VectorT2<TCoordinate>(x, y),
426 strength_(strength)
427{
428 // nothing to do here
429}
430
431template <typename TCoordinate, typename TStrength>
433{
434 return strength_;
435}
436
437template <typename TCoordinate, typename TStrength>
438template <bool tLeftLargerThanRight>
440{
441 if constexpr (tLeftLargerThanRight)
442 {
443 return left.strength() > right.strength();
444 }
445 else
446 {
447 return left.strength() < right.strength();
448 }
449}
450
451template <typename T>
453 positionX_(-1),
454 strength_(T())
455{
456 // nothing to do here
457}
458
459template <typename T>
460inline NonMaximumSuppressionT<T>::StrengthCandidate::StrengthCandidate(const unsigned int x, const T& strength) :
461 positionX_(x),
462 strength_(strength)
463{
464 // nothing to do here
465}
466
467template <typename T>
469{
470 return positionX_;
471}
472
473template <typename T>
475{
476 return strength_;
477}
478
479template <typename T>
481{
482 *this = std::move(nonMaximumSuppression);
483}
484
485template <typename T>
487 width_(nonMaximumSuppression.width_),
488 rows_(nonMaximumSuppression.rows_)
489{
490 // nothing to do here
491}
492
493template <typename T>
494NonMaximumSuppressionT<T>::NonMaximumSuppressionT(const unsigned int width, const unsigned int height, const unsigned int yOffset) noexcept :
495 width_(width),
497{
498 // nothing to do here
499}
500
501template <typename T>
502inline unsigned int NonMaximumSuppressionT<T>::width() const
503{
504 return width_;
505}
506
507template <typename T>
508inline unsigned int NonMaximumSuppressionT<T>::height() const
509{
510 return (unsigned int)rows_.size();
511}
512
513template <typename T>
514inline unsigned int NonMaximumSuppressionT<T>::yOffset() const
515{
516 ocean_assert(rows_.firstIndex() >= 0);
517
518 return (unsigned int)(rows_.firstIndex());
519}
520
521template <typename T>
522inline void NonMaximumSuppressionT<T>::addCandidate(const unsigned int x, const unsigned int y, const T& strength)
523{
524 ocean_assert(x < width_);
525
526 ocean_assert(rows_.isValidIndex(y) && y >= (unsigned int)(rows_.firstIndex()) && y <= (unsigned int)(rows_.endIndex()));
527
528 if (rows_[y].empty())
529 {
530 rows_[y].reserve(128);
531 }
532
533 rows_[y].emplace_back(x, strength);
534}
535
536template <typename T>
537void 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)
538{
539 ocean_assert(values != nullptr);
540
541 ocean_assert(firstColumn + numberColumns <= width_);
542 ocean_assert(typename StrengthCandidateRows::Index(firstRow) >= rows_.firstIndex());
543 ocean_assert(typename StrengthCandidateRows::Index(firstRow + numberRows) <= rows_.endIndex());
544
545 const unsigned int valuesStrideElements = width_ + valuesPaddingElements;
546
547 if (worker != nullptr)
548 {
549 worker->executeFunction(Worker::Function::create(*this, &NonMaximumSuppressionT<T>::addCandidatesSubset, values, valuesStrideElements, firstColumn, numberColumns, &minimalThreshold, 0u, 0u), firstRow, numberRows, 5u, 6u, 20u);
550 }
551 else
552 {
553 addCandidatesSubset(values, valuesStrideElements, firstColumn, numberColumns, &minimalThreshold, firstRow, numberRows);
554 }
555}
556
557template <typename T>
558inline void NonMaximumSuppressionT<T>::removeCandidatesRightFrom(const unsigned int x, const unsigned int y)
559{
560 ocean_assert(rows_.isValidIndex(y) && y >= (unsigned int)(rows_.firstIndex()) && y <= (unsigned int)(rows_.endIndex()));
561
562 StrengthCandidateRow& suppressionRow = rows_[y];
563
564 while (!suppressionRow.empty())
565 {
566 if (suppressionRow.back().x() >= x)
567 {
568 suppressionRow.pop_back();
569 }
570 else
571 {
572 break;
573 }
574 }
575}
576
577template <typename T>
578bool NonMaximumSuppressionT<T>::candidate(const unsigned int x, const unsigned int y, T& strength) const
579{
580 ocean_assert(x < width_);
581 ocean_assert(rows_.isValidIndex(y) && y >= (unsigned int)(rows_.firstIndex()) && y <= (unsigned int)(rows_.endIndex()));
582
583 const StrengthCandidateRow& suppressionRow = rows_[y];
584
585 if (suppressionRow.empty())
586 {
587 return false;
588 }
589
590 size_t left = 0;
591 size_t right = suppressionRow.size() - 1;
592
593 while (left <= right)
594 {
595 const size_t mid = left + (right - left) / 2;
596
597 const unsigned int midX = suppressionRow[mid].x();
598
599 if (midX == x)
600 {
601 strength = suppressionRow[mid].strength();
602 return true;
603 }
604 else if (midX < x)
605 {
606 left = mid + 1;
607 }
608 else
609 {
610 if (mid == 0)
611 {
612 break;
613 }
614
615 right = mid - 1;
616 }
617 }
618
619 return false;
620}
621
622template <typename T>
623void 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)
624{
625 ocean_assert(firstColumn + numberColumns <= width_);
626 ocean_assert(firstRow + numberRows <= (unsigned int)(rows_.endIndex()));
627
628 const unsigned int endColumn = firstColumn + numberColumns;
629
630 for (unsigned int y = firstRow; y < firstRow + numberRows; ++y)
631 {
632 const StrengthCandidateRow& row = rows_[y];
633
634 for (const StrengthCandidate& candidate : row)
635 {
636 if (candidate.x() < firstColumn)
637 {
638 continue;
639 }
640
641 if (candidate.x() >= endColumn)
642 {
643 break;
644 }
645
646 strengthPositions.emplace_back(candidate.x(), y, candidate.strength());
647 }
648 }
649}
650
651template <typename T>
652template <typename TCoordinate, typename TStrength, bool tStrictMaximum, NonMaximumSuppression::SuppressionMode tSuppressionMode>
653bool 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
654{
655 ocean_assert(firstColumn + numberColumns <= width_);
656 ocean_assert(firstRow >= (unsigned int)(rows_.firstIndex()) && firstRow + numberRows <= (unsigned int)(rows_.endIndex()));
657
658 if (firstColumn + numberColumns > width_)
659 {
660 return false;
661 }
662
663 if (firstRow >= (unsigned int)(rows_.endIndex()) || firstRow + numberRows <= (unsigned int)(rows_.firstIndex()))
664 {
665 return false;
666 }
667
668 strengthPositions.reserve(strengthPositions.size() + 128);
669
670 if constexpr (tSuppressionMode == NonMaximumSuppression::SM_MAXIMUM || tSuppressionMode == NonMaximumSuppression::SM_MAXIMUM_POSITIVE_ONLY)
671 {
672 constexpr bool tOnlyPositive = tSuppressionMode == NonMaximumSuppression::SM_MAXIMUM_POSITIVE_ONLY;
673
674 if (worker != nullptr)
675 {
676 Lock lock;
677 worker->executeFunction(Worker::Function::create(*this, &NonMaximumSuppressionT<T>::suppressNonMaximumSubset<TCoordinate, TStrength, tStrictMaximum, tOnlyPositive>, &strengthPositions, firstColumn, numberColumns, &lock, positionCallback, 0u, 0u), firstRow, numberRows, 5u, 6u, 3u);
678 }
679 else
680 {
681 suppressNonMaximumSubset<TCoordinate, TStrength, tStrictMaximum, tOnlyPositive>(&strengthPositions, firstColumn, numberColumns, nullptr, positionCallback, firstRow, numberRows);
682 }
683 }
684 else
685 {
686 static_assert(tSuppressionMode == NonMaximumSuppression::SM_MINIMUM || tSuppressionMode == NonMaximumSuppression::SM_MINIMUM_NEGATIVE_ONLY);
687
688 constexpr bool tOnlyNegative = tSuppressionMode == NonMaximumSuppression::SM_MINIMUM_NEGATIVE_ONLY;
689
690 if (worker != nullptr)
691 {
692 Lock lock;
693 worker->executeFunction(Worker::Function::create(*this, &NonMaximumSuppressionT<T>::suppressNonMinimumSubset<TCoordinate, TStrength, tStrictMaximum, tOnlyNegative>, &strengthPositions, firstColumn, numberColumns, &lock, positionCallback, 0u, 0u), firstRow, numberRows, 5u, 6u, 3u);
694 }
695 else
696 {
697 suppressNonMinimumSubset<TCoordinate, TStrength, tStrictMaximum, tOnlyNegative>(&strengthPositions, firstColumn, numberColumns, nullptr, positionCallback, firstRow, numberRows);
698 }
699 }
700
701 return true;
702}
703
704template <typename T>
706{
707 for (ptrdiff_t n = rows_.firstIndex(); n < rows_.endIndex(); ++n)
708 {
709 rows_[n].clear();
710 }
711}
712
713template <typename T>
715{
716 if (this != &nonMaximumSuppression)
717 {
718 width_ = nonMaximumSuppression.width_;
719 nonMaximumSuppression.width_ = 0u;
720
721 rows_ = std::move(nonMaximumSuppression.rows_);
722 }
723
724 return *this;
725}
726
727template <typename T>
729{
730 if (this != &nonMaximumSuppression)
731 {
732 width_ = nonMaximumSuppression.width_;
733 rows_ = nonMaximumSuppression.rows_;
734 }
735
736 return *this;
737}
738
739template <typename T>
740template <typename TCoordinate, typename TStrength, bool tStrictMaximum>
742{
743 ocean_assert(width >= 1u && height >= 1u);
744 ocean_assert(radius >= TCoordinate(1));
745
746 const unsigned int binSize = std::max(10u, (unsigned int)(NumericT<TCoordinate>::ceil(radius)));
747
748 const unsigned int horizontalBins = std::max(1u, (width + binSize - 1u) / binSize);
749 const unsigned int verticalBins = std::max(1u, (height + binSize - 1u) / binSize);
750
751 ocean_assert(binSize * horizontalBins >= width);
752 ocean_assert(binSize * verticalBins >= height);
753
754 IndexGroups32 indexGroups(horizontalBins * verticalBins);
755
756 // distributing all strength positions into a regular grid to reduce search space later
757
758 for (size_t n = 0; n < strengthPositions.size(); ++n)
759 {
760 const VectorT2<TCoordinate>& position = strengthPositions[n];
761
762 ocean_assert((unsigned int)(position.x()) < width);
763 ocean_assert((unsigned int)(position.y()) < height);
764
765 const unsigned int xBin = (unsigned int)(position.x()) / binSize;
766 const unsigned int yBin = (unsigned int)(position.y()) / binSize;
767
768 ocean_assert(xBin <= horizontalBins);
769 ocean_assert(yBin <= verticalBins);
770
771 indexGroups[yBin * horizontalBins + xBin].emplace_back(Index32(n));
772 }
773
774 std::vector<uint8_t> validPositions(strengthPositions.size(), 1u);
775
776 const TCoordinate sqrRadius = radius * radius;
777
778 for (size_t nCandidate = 0; nCandidate < strengthPositions.size(); ++nCandidate)
779 {
780 if (validPositions[nCandidate] == 0u)
781 {
782 // the positions is already suppressed
783 continue;
784 }
785
786 const StrengthPosition<TCoordinate, TStrength>& candidatePosition = strengthPositions[nCandidate];
787
788 const unsigned int xCandidateBin = (unsigned int)(candidatePosition.x()) / binSize;
789 const unsigned int yCandidateBin = (unsigned int)(candidatePosition.y()) / binSize;
790
791 ocean_assert(xCandidateBin <= horizontalBins);
792 ocean_assert(yCandidateBin <= verticalBins);
793
794 bool checkNextCandidate = false;
795
796 for (unsigned int yBin = (unsigned int)(max(0, int(yCandidateBin) - 1)); !checkNextCandidate && yBin < min(yCandidateBin + 2u, verticalBins); ++yBin)
797 {
798 for (unsigned int xBin = (unsigned int)(max(0, int(xCandidateBin) - 1)); !checkNextCandidate && xBin < min(xCandidateBin + 2u, horizontalBins); ++xBin)
799 {
800 const Indices32& indices = indexGroups[yBin * horizontalBins + xBin];
801
802 for (const Index32& nTest : indices)
803 {
804 if (nTest == Index32(nCandidate))
805 {
806 continue;
807 }
808
809 const StrengthPosition<TCoordinate, TStrength>& testPosition = strengthPositions[nTest];
810
811 // we do not check whether test position is suppressed already (as the test position may still be the reason to suppress the candidate position)
812
813 if (candidatePosition.sqrDistance(testPosition) <= sqrRadius)
814 {
815 if (candidatePosition.strength() > testPosition.strength())
816 {
817 validPositions[nTest] = 0u;
818 }
819 else if (candidatePosition.strength() < testPosition.strength())
820 {
821 validPositions[nCandidate] = 0u;
822
823 checkNextCandidate = true;
824 break;
825 }
826 else
827 {
828 ocean_assert(candidatePosition.strength() == testPosition.strength());
829
830 if constexpr (tStrictMaximum)
831 {
832 // we suppress both elements, as we seek a strict maximum element
833
834 validPositions[nCandidate] = 0u;
835 validPositions[nTest] = 0u;
836
837 checkNextCandidate = true;
838 break;
839 }
840 else
841 {
842 // we will suppress one of both elements, as we accept a non-strict maximum element
843
844 if (candidatePosition.y() < testPosition.y() || (candidatePosition.y() == testPosition.y() && candidatePosition.x() < testPosition.x()))
845 {
846 // the candidate position will be suppressed as the test position is located to the bottom/right of the candidate position
847
848 validPositions[nCandidate] = 0u;
849
850 checkNextCandidate = true;
851 break;
852 }
853 else
854 {
855 ocean_assert(testPosition.y() < candidatePosition.y() || (testPosition.y() == candidatePosition.y() && testPosition.x() < candidatePosition.x()));
856
857 // the test position will be suppressed as the candidate position is located to the bottom/right of the test position
858
859 validPositions[nTest] = 0u;
860 }
861 }
862 }
863 }
864 }
865 }
866 }
867 }
868
870 remainingPositions.reserve(strengthPositions.size());
871
872 if (validIndices)
873 {
874 ocean_assert(validIndices->empty());
875
876 validIndices->clear();
877 validIndices->reserve(strengthPositions.size());
878
879 for (size_t n = 0; n < validPositions.size(); ++n)
880 {
881 if (validPositions[n])
882 {
883 remainingPositions.emplace_back(strengthPositions[n]);
884 validIndices->emplace_back(Index32(n));
885 }
886 }
887 }
888 else
889 {
890 for (size_t n = 0; n < validPositions.size(); ++n)
891 {
892 if (validPositions[n])
893 {
894 remainingPositions.emplace_back(strengthPositions[n]);
895 }
896 }
897 }
898
899 return remainingPositions;
900}
901
902template <typename T>
903template <typename TFloat>
904bool NonMaximumSuppressionT<T>::determinePrecisePeakLocation1(const T& leftValue, const T& middleValue, const T& rightValue, TFloat& location)
905{
906 static_assert(std::is_floating_point<TFloat>::value, "Invalid floating point data type!");
907
908 // f(x) = f(a) + f`(a) * (x - a)
909
910 // we expect our middle value to be located at a = 0:
911 // f(x) = f(0) + f`(0) * x
912
913 // 0 = f'(x)
914 // = f'(0) + f''(0) * x
915
916 // x = - f'(0) / f''(0)
917
918 // f`(x) = [-1 0 1] * 1/2
919 const TFloat df = (TFloat(rightValue) - TFloat(leftValue)) * TFloat(0.5);
920
921 // f``(x) = [1 -2 1] * 1/1
922 const TFloat dff = TFloat(leftValue) + TFloat(rightValue) - TFloat(middleValue) * TFloat(2);
923
925 {
926 location = TFloat(0);
927 return true;
928 }
929
930 const TFloat x = -df / dff;
931
932 if (x < TFloat(-1) || x > TFloat(1))
933 {
934 return false;
935 }
936
937 location = x;
938 return true;
939}
940
941template <typename T>
942template <typename TFloat>
943bool NonMaximumSuppressionT<T>::determinePrecisePeakLocation2(const T* const topValues, const T* const centerValues, const T* const bottomValues, VectorT2<TFloat>& location)
944{
945 static_assert(std::is_floating_point<TFloat>::value, "Invalid floating point data type!");
946
947 const T& value00 = topValues[0];
948 const T& value01 = topValues[1];
949 const T& value02 = topValues[2];
950
951 const T& value10 = centerValues[0];
952 const T& value11 = centerValues[1];
953 const T& value12 = centerValues[2];
954
955 const T& value20 = bottomValues[0];
956 const T& value21 = bottomValues[1];
957 const T& value22 = bottomValues[2];
958
959#if 0
960 // some response values may not perfectly follow the peak criteria so that we do not use the asserts by default
961 ocean_assert(value11 >= value00 && value11 >= value01 && value11 >= value02);
962 ocean_assert(value11 >= value10 && value11 >= value12);
963 ocean_assert(value11 >= value20 && value11 >= value21 && value11 >= value22);
964#endif
965
966 // [-1 0 1] * 1/2
967 const TFloat dx = TFloat(value12 - value10) * TFloat(0.5);
968 const TFloat dy = TFloat(value21 - value01) * TFloat(0.5);
969
970 // [1 -2 1] * 1/1
971 const TFloat dxx = TFloat(value12 + value10) - TFloat(value11) * TFloat(2);
972 const TFloat dyy = TFloat(value21 + value01) - TFloat(value11) * TFloat(2);
973
974 // [ 1 0 -1 ]
975 // [ 0 0 0 ] * 1/4
976 // [-1 0 1 ]
977
978 const TFloat dxy = TFloat(value22 + value00 - value20 - value02) * TFloat(0.25);
979
980 const TFloat denominator = dxx * dyy - dxy * dxy;
981
982 if (NumericT<TFloat>::isEqualEps(denominator))
983 {
984 location = VectorT2<TFloat>(0, 0);
985 return true;
986 }
987
988 const TFloat factor = TFloat(1) / denominator;
989
990 const TFloat offsetX = -(dyy * dx - dxy * dy) * factor;
991 const TFloat offsetY = -(dxx * dy - dxy * dx) * factor;
992
993 if (offsetX < TFloat(-1) || offsetX > TFloat(1) || offsetY < TFloat(-1) || offsetY > TFloat(1))
994 {
995 return false;
996 }
997
998 location = VectorT2<TFloat>(offsetX, offsetY);
999 return true;
1000}
1001
1002template <typename T>
1003void 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)
1004{
1005 ocean_assert(values != nullptr);
1006 ocean_assert(valuesStrideElements >= width_);
1007 ocean_assert(firstColumn + numberColumns <= width_);
1008
1009 ocean_assert(typename StrengthCandidateRows::Index(firstRow) >= rows_.firstIndex());
1010 ocean_assert(typename StrengthCandidateRows::Index(firstRow + numberRows) <= rows_.endIndex());
1011
1012 const T localThreshold = *minimalThreshold;
1013
1014 values += firstRow * valuesStrideElements;
1015
1016 for (unsigned int y = firstRow; y < firstRow + numberRows; ++y)
1017 {
1018 for (unsigned int x = firstColumn; x < firstColumn + numberColumns; ++x)
1019 {
1020 if (values[x] >= localThreshold)
1021 {
1022 addCandidate(x, y, values[x]);
1023 }
1024 }
1025
1026 values += valuesStrideElements;
1027 }
1028}
1029
1030template <typename T>
1031template <typename TCoordinate, typename TStrength, bool tStrictMaximum, bool tOnlyPositive>
1032void 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
1033{
1034 ocean_assert(strengthPositions);
1035
1036 ocean_assert(firstColumn + numberColumns <= width_);
1037 ocean_assert(firstRow >= (unsigned int)(rows_.firstIndex()));
1038 ocean_assert(firstRow + numberRows <= (unsigned int)rows_.endIndex());
1039
1040 if (numberColumns < 3u || numberRows < 3u)
1041 {
1042 return;
1043 }
1044
1045 const unsigned int firstCenterColumn = max(1u, firstColumn);
1046 const unsigned int endCenterColumn = min(firstColumn + numberColumns, width_ - 1u);
1047
1048 const unsigned int firstCenterRow = max((unsigned int)rows_.firstIndex() + 1u, firstRow);
1049 const unsigned int endCenterRow = min(firstRow + numberRows, (unsigned int)rows_.lastIndex());
1050
1051 ocean_assert(firstCenterRow >= 1u);
1052
1053 StrengthPositions<TCoordinate, TStrength> localStrengthPositions;
1054 localStrengthPositions.reserve(100);
1055
1056 for (unsigned int y = firstCenterRow; y < endCenterRow; ++y)
1057 {
1058 const StrengthCandidateRow& row0 = rows_[y - 1u];
1059 const StrengthCandidateRow& row1 = rows_[y + 0u];
1060 const StrengthCandidateRow& row2 = rows_[y + 1u];
1061
1062 typename StrengthCandidateRow::const_iterator iRow0 = row0.begin();
1063 typename StrengthCandidateRow::const_iterator iRow2 = row2.begin();
1064
1065 typename StrengthCandidateRow::const_iterator iRow1Minus = row1.end();
1066 typename StrengthCandidateRow::const_iterator iRow1Plus = row1.size() > 1 ? row1.begin() + 1 : row1.end();
1067
1068 for (typename StrengthCandidateRow::const_iterator iRow1 = row1.begin(); iRow1 != row1.end(); ++iRow1)
1069 {
1070 ocean_assert(iRow1->x() >= 0u && iRow1->x() + 1u <= width_);
1071
1072 // check value constraint if enabled
1073 if constexpr (tOnlyPositive)
1074 {
1075 if (iRow1->strength() <= T(0))
1076 {
1077 goto next;
1078 }
1079 }
1080
1081 // check left candidate (west)
1082 if (iRow1->x() >= firstCenterColumn && iRow1->x() < endCenterColumn && (iRow1Minus == row1.end() || iRow1Minus->x() + 1u != iRow1->x() || (tStrictMaximum && iRow1Minus->strength() < iRow1->strength()) || (!tStrictMaximum && iRow1Minus->strength() <= iRow1->strength())))
1083 {
1084 // check right candidate (east)
1085 if (iRow1Plus == row1.end() || iRow1Plus->x() != iRow1->x() + 1u || iRow1Plus->strength() < iRow1->strength())
1086 {
1087 // set the top row iterator to the right position
1088 while (iRow0 != row0.end())
1089 {
1090 if (iRow0->x() + 1u < iRow1->x())
1091 {
1092 ++iRow0;
1093 }
1094 else
1095 {
1096 break;
1097 }
1098 }
1099
1100 // now iRow0 should point at least to the north west pixel position (or more far east)
1101 ocean_assert(iRow0 == row0.end() || iRow0->x() + 1u >= iRow1->x());
1102
1103 if (iRow0 != row0.end() && iRow0->x() <= iRow1->x() + 1u)
1104 {
1105 ocean_assert(iRow0->x() + 1u == iRow1->x() || iRow0->x() == iRow1->x() || iRow0->x() - 1u == iRow1->x());
1106
1107 if ((tStrictMaximum && iRow0->strength() >= iRow1->strength()) || (!tStrictMaximum && iRow0->strength() > iRow1->strength()))
1108 {
1109 goto next;
1110 }
1111
1112 // check if there is a further candidate in the north row
1113
1114 const typename StrengthCandidateRow::const_iterator iRow0Plus = iRow0 + 1;
1115
1116 if (iRow0Plus != row0.end() && iRow0Plus->x() <= iRow1->x() + 1u)
1117 {
1118 if ((tStrictMaximum && iRow0Plus->strength() >= iRow1->strength()) || (!tStrictMaximum && iRow0Plus->strength() > iRow1->strength()))
1119 {
1120 goto next;
1121 }
1122
1123 // check if there is a further candidate in the north row
1124
1125 const typename StrengthCandidateRow::const_iterator iRow0PlusPlus = iRow0Plus + 1;
1126
1127 if (iRow0PlusPlus != row0.end() && iRow0PlusPlus->x() <= iRow1->x() + 1u)
1128 {
1129 ocean_assert(iRow0PlusPlus->x() == iRow1->x() + 1u);
1130
1131 if ((tStrictMaximum && iRow0PlusPlus->strength() >= iRow1->strength()) || (!tStrictMaximum && iRow0PlusPlus->strength() > iRow1->strength()))
1132 {
1133 goto next;
1134 }
1135 }
1136 }
1137 }
1138
1139
1140 // set the bottom row iterator to the right position
1141 while (iRow2 != row2.end())
1142 {
1143 if (iRow2->x() + 1u < iRow1->x())
1144 {
1145 ++iRow2;
1146 }
1147 else
1148 {
1149 break;
1150 }
1151 }
1152
1153 // now iRow2 should point at least to the south west pixel position (or more far east)
1154 ocean_assert(iRow2 == row2.end() || iRow2->x() + 1u >= iRow1->x());
1155
1156 if (iRow2 != row2.end() && iRow2->x() <= iRow1->x() + 1u)
1157 {
1158 ocean_assert(iRow2->x() + 1u == iRow1->x() || iRow2->x() == iRow1->x() || iRow2->x() - 1u == iRow1->x());
1159
1160 if (iRow2->x() + 1u == iRow1->x())
1161 {
1162 // iRow2 points to the south west pixel
1163
1164 if ((tStrictMaximum && iRow2->strength() >= iRow1->strength()) || (!tStrictMaximum && iRow2->strength() > iRow1->strength()))
1165 {
1166 goto next;
1167 }
1168 }
1169 else
1170 {
1171 if (iRow2->strength() >= iRow1->strength())
1172 {
1173 goto next;
1174 }
1175 }
1176
1177 // check if there is a further candidate in the south row
1178
1179 const typename StrengthCandidateRow::const_iterator iRow2Plus = iRow2 + 1;
1180
1181 if (iRow2Plus != row2.end() && iRow2Plus->x() <= iRow1->x() + 1u)
1182 {
1183 if (iRow2Plus->strength() >= iRow1->strength())
1184 {
1185 goto next;
1186 }
1187
1188 // check if there is a further candidate in the south row
1189
1190 const typename StrengthCandidateRow::const_iterator iRow2PlusPlus = iRow2Plus + 1;
1191
1192 if (iRow2PlusPlus != row2.end() && iRow2PlusPlus->x() <= iRow1->x() + 1u)
1193 {
1194 ocean_assert(iRow2PlusPlus->x() == iRow1->x() + 1u);
1195
1196 if (iRow2PlusPlus->strength() >= iRow1->strength())
1197 {
1198 goto next;
1199 }
1200 }
1201 }
1202 }
1203
1204 if (positionCallback != nullptr)
1205 {
1206 TCoordinate preciseX;
1207 TCoordinate preciseY;
1208 TStrength preciseStrength;
1209
1210 if ((*positionCallback)(iRow1->x(), y, iRow1->strength(), preciseX, preciseY, preciseStrength))
1211 {
1212 localStrengthPositions.emplace_back(preciseX, preciseY, preciseStrength);
1213 }
1214 }
1215 else
1216 {
1217 localStrengthPositions.emplace_back(TCoordinate(iRow1->x()), TCoordinate(y), iRow1->strength());
1218 }
1219 }
1220 }
1221
1222next:
1223
1224 iRow1Minus = iRow1;
1225
1226 if (iRow1Plus != row1.end())
1227 {
1228 ++iRow1Plus;
1229 }
1230 }
1231 }
1232
1233 const OptionalScopedLock scopedLock(lock);
1234
1235 strengthPositions->insert(strengthPositions->end(), localStrengthPositions.begin(), localStrengthPositions.end());
1236}
1237
1238template <typename T>
1239template <typename TCoordinate, typename TStrength, bool tStrictMaximum, bool tOnlyNegative>
1240void NonMaximumSuppressionT<T>::suppressNonMinimumSubset(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
1241{
1242 ocean_assert(strengthPositions);
1243
1244 ocean_assert(firstColumn + numberColumns <= width_);
1245 ocean_assert(firstRow >= (unsigned int)(rows_.firstIndex()));
1246 ocean_assert(firstRow + numberRows <= (unsigned int)rows_.endIndex());
1247
1248 if (numberColumns < 3u || numberRows < 3u)
1249 {
1250 return;
1251 }
1252
1253 const unsigned int firstCenterColumn = max(1u, firstColumn);
1254 const unsigned int endCenterColumn = min(firstColumn + numberColumns, width_ - 1u);
1255
1256 const unsigned int firstCenterRow = max((unsigned int)rows_.firstIndex() + 1u, firstRow);
1257 const unsigned int endCenterRow = min(firstRow + numberRows, (unsigned int)rows_.lastIndex());
1258
1259 ocean_assert(firstCenterRow >= 1u);
1260
1261 StrengthPositions<TCoordinate, TStrength> localStrengthPositions;
1262 localStrengthPositions.reserve(100);
1263
1264 for (unsigned int y = firstCenterRow; y < endCenterRow; ++y)
1265 {
1266 const StrengthCandidateRow& row0 = rows_[y - 1u];
1267 const StrengthCandidateRow& row1 = rows_[y + 0u];
1268 const StrengthCandidateRow& row2 = rows_[y + 1u];
1269
1270 typename StrengthCandidateRow::const_iterator iRow0 = row0.begin();
1271 typename StrengthCandidateRow::const_iterator iRow2 = row2.begin();
1272
1273 typename StrengthCandidateRow::const_iterator iRow1Minus = row1.end();
1274 typename StrengthCandidateRow::const_iterator iRow1Plus = row1.size() > 1 ? row1.begin() + 1 : row1.end();
1275
1276 for (typename StrengthCandidateRow::const_iterator iRow1 = row1.begin(); iRow1 != row1.end(); ++iRow1)
1277 {
1278 ocean_assert(iRow1->x() >= 0u && iRow1->x() + 1u <= width_);
1279
1280 // check value constraint if enabled
1281 if constexpr (tOnlyNegative)
1282 {
1283 if (iRow1->strength() >= T(0))
1284 {
1285 goto nextMinimum;
1286 }
1287 }
1288
1289 // check left candidate (west)
1290 if (iRow1->x() >= firstCenterColumn && iRow1->x() < endCenterColumn && (iRow1Minus == row1.end() || iRow1Minus->x() + 1u != iRow1->x() || (tStrictMaximum && iRow1Minus->strength() > iRow1->strength()) || (!tStrictMaximum && iRow1Minus->strength() >= iRow1->strength())))
1291 {
1292 // check right candidate (east)
1293 if (iRow1Plus == row1.end() || iRow1Plus->x() != iRow1->x() + 1u || iRow1Plus->strength() > iRow1->strength())
1294 {
1295 // set the top row iterator to the right position
1296 while (iRow0 != row0.end())
1297 {
1298 if (iRow0->x() + 1u < iRow1->x())
1299 {
1300 ++iRow0;
1301 }
1302 else
1303 {
1304 break;
1305 }
1306 }
1307
1308 // now iRow0 should point at least to the north west pixel position (or more far east)
1309 ocean_assert(iRow0 == row0.end() || iRow0->x() + 1u >= iRow1->x());
1310
1311 if (iRow0 != row0.end() && iRow0->x() <= iRow1->x() + 1u)
1312 {
1313 ocean_assert(iRow0->x() + 1u == iRow1->x() || iRow0->x() == iRow1->x() || iRow0->x() - 1u == iRow1->x());
1314
1315 if ((tStrictMaximum && iRow0->strength() <= iRow1->strength()) || (!tStrictMaximum && iRow0->strength() < iRow1->strength()))
1316 {
1317 goto nextMinimum;
1318 }
1319
1320 // check if there is a further candidate in the north row
1321
1322 const typename StrengthCandidateRow::const_iterator iRow0Plus = iRow0 + 1;
1323
1324 if (iRow0Plus != row0.end() && iRow0Plus->x() <= iRow1->x() + 1u)
1325 {
1326 if ((tStrictMaximum && iRow0Plus->strength() <= iRow1->strength()) || (!tStrictMaximum && iRow0Plus->strength() < iRow1->strength()))
1327 {
1328 goto nextMinimum;
1329 }
1330
1331 // check if there is a further candidate in the north row
1332
1333 const typename StrengthCandidateRow::const_iterator iRow0PlusPlus = iRow0Plus + 1;
1334
1335 if (iRow0PlusPlus != row0.end() && iRow0PlusPlus->x() <= iRow1->x() + 1u)
1336 {
1337 ocean_assert(iRow0PlusPlus->x() == iRow1->x() + 1u);
1338
1339 if ((tStrictMaximum && iRow0PlusPlus->strength() <= iRow1->strength()) || (!tStrictMaximum && iRow0PlusPlus->strength() < iRow1->strength()))
1340 {
1341 goto nextMinimum;
1342 }
1343 }
1344 }
1345 }
1346
1347
1348 // set the bottom row iterator to the right position
1349 while (iRow2 != row2.end())
1350 {
1351 if (iRow2->x() + 1u < iRow1->x())
1352 {
1353 ++iRow2;
1354 }
1355 else
1356 {
1357 break;
1358 }
1359 }
1360
1361 // now iRow2 should point at least to the south west pixel position (or more far east)
1362 ocean_assert(iRow2 == row2.end() || iRow2->x() + 1u >= iRow1->x());
1363
1364 if (iRow2 != row2.end() && iRow2->x() <= iRow1->x() + 1u)
1365 {
1366 ocean_assert(iRow2->x() + 1u == iRow1->x() || iRow2->x() == iRow1->x() || iRow2->x() - 1u == iRow1->x());
1367
1368 if (iRow2->x() + 1u == iRow1->x())
1369 {
1370 // iRow2 points to the south west pixel
1371
1372 if ((tStrictMaximum && iRow2->strength() <= iRow1->strength()) || (!tStrictMaximum && iRow2->strength() < iRow1->strength()))
1373 {
1374 goto nextMinimum;
1375 }
1376 }
1377 else
1378 {
1379 if (iRow2->strength() <= iRow1->strength())
1380 {
1381 goto nextMinimum;
1382 }
1383 }
1384
1385 // check if there is a further candidate in the south row
1386
1387 const typename StrengthCandidateRow::const_iterator iRow2Plus = iRow2 + 1;
1388
1389 if (iRow2Plus != row2.end() && iRow2Plus->x() <= iRow1->x() + 1u)
1390 {
1391 if (iRow2Plus->strength() <= iRow1->strength())
1392 {
1393 goto nextMinimum;
1394 }
1395
1396 // check if there is a further candidate in the south row
1397
1398 const typename StrengthCandidateRow::const_iterator iRow2PlusPlus = iRow2Plus + 1;
1399
1400 if (iRow2PlusPlus != row2.end() && iRow2PlusPlus->x() <= iRow1->x() + 1u)
1401 {
1402 ocean_assert(iRow2PlusPlus->x() == iRow1->x() + 1u);
1403
1404 if (iRow2PlusPlus->strength() <= iRow1->strength())
1405 {
1406 goto nextMinimum;
1407 }
1408 }
1409 }
1410 }
1411
1412 if (positionCallback != nullptr)
1413 {
1414 TCoordinate preciseX;
1415 TCoordinate preciseY;
1416 TStrength preciseStrength;
1417
1418 if ((*positionCallback)(iRow1->x(), y, iRow1->strength(), preciseX, preciseY, preciseStrength))
1419 {
1420 localStrengthPositions.emplace_back(preciseX, preciseY, preciseStrength);
1421 }
1422 }
1423 else
1424 {
1425 localStrengthPositions.emplace_back(TCoordinate(iRow1->x()), TCoordinate(y), iRow1->strength());
1426 }
1427 }
1428 }
1429
1430nextMinimum:
1431
1432 iRow1Minus = iRow1;
1433
1434 if (iRow1Plus != row1.end())
1435 {
1436 ++iRow1Plus;
1437 }
1438 }
1439 }
1440
1441 const OptionalScopedLock scopedLock(lock);
1442
1443 strengthPositions->insert(strengthPositions->end(), localStrengthPositions.begin(), localStrengthPositions.end());
1444}
1445
1446}
1447
1448}
1449
1450#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:58
const TStrength & strength() const
Returns the strength parameter of this object.
Definition NonMaximumSuppression.h:432
StrengthPosition()=default
Creates a new object with default strength parameter.
TStrength strength_
Strength parameter of this object.
Definition NonMaximumSuppression.h:93
static bool compareStrength(const StrengthPosition< TCoordinate, TStrength > &left, const StrengthPosition< TCoordinate, TStrength > &right)
Compares the strength value of two objects.
Definition NonMaximumSuppression.h:439
This class provides base functionality and type definitions for non-maximum-suppression operations.
Definition NonMaximumSuppression.h:33
SuppressionMode
Definition of individual suppression modes for extremum search.
Definition NonMaximumSuppression.h:40
@ SM_MAXIMUM
Finds the maximum values, any value is allowed.
Definition NonMaximumSuppression.h:42
@ SM_MINIMUM
Finds the minimum values, any value is allowed.
Definition NonMaximumSuppression.h:44
@ SM_MAXIMUM_POSITIVE_ONLY
Finds the maximum values, only positive values are allowed (> 0).
Definition NonMaximumSuppression.h:46
@ SM_MINIMUM_NEGATIVE_ONLY
Finds the minimum values, only negative values are allowed (< 0).
Definition NonMaximumSuppression.h:48
std::vector< StrengthPosition< TCoordinate, TStrength > > StrengthPositions
Definition of a vector holding strength pixel positions.
Definition NonMaximumSuppression.h:100
This class holds the horizontal position and strength parameter of an interest pixel.
Definition NonMaximumSuppression.h:144
StrengthCandidate()
Creates a new candidate object.
Definition NonMaximumSuppression.h:452
unsigned int positionX_
Horizontal position of this object.
Definition NonMaximumSuppression.h:174
const T & strength() const
Returns the strength parameter of this object.
Definition NonMaximumSuppression.h:474
unsigned int x() const
Returns the horizontal position of this candidate object.
Definition NonMaximumSuppression.h:468
T strength_
Strength parameter of this object.
Definition NonMaximumSuppression.h:177
This class implements the possibility to find local maximum in a 2D array by applying a non-maximum-s...
Definition NonMaximumSuppression.h:120
void 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
Applies a non-maximum-suppression search on a subset of a given 2D frame in a 3x3 neighborhood (eight...
Definition NonMaximumSuppression.h:1032
unsigned int yOffset() const
Returns the optional offset in the vertical direction.
Definition NonMaximumSuppression.h:514
void reset()
Removes the gathered non-maximum suppression information so that this object can be reused again (for...
Definition NonMaximumSuppression.h:705
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:943
void addCandidate(const unsigned int x, const unsigned int y, const T &strength)
Adds a new candidate to this object.
Definition NonMaximumSuppression.h:522
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:558
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:623
unsigned int height() const
Returns the height of this object.
Definition NonMaximumSuppression.h:508
bool candidate(const unsigned int x, const unsigned int y, T &strength) const
Returns the strength value of a candidate at a specified position.
Definition NonMaximumSuppression.h:578
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:904
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:653
NonMaximumSuppressionT< T > & operator=(NonMaximumSuppressionT< T > &&nonMaximumSuppression)
Move operator.
Definition NonMaximumSuppression.h:714
std::vector< StrengthCandidate > StrengthCandidateRow
Definition of a vector holding strength candidate objects.
Definition NonMaximumSuppression.h:183
std::function< bool(const unsigned int x, const unsigned int y, const TStrength strength, TCoordinate &preciseX, TCoordinate &preciseY, TStrength &preciseStrength)> PositionCallback
Definition of a callback function used to determine the precise sub-pixel position of a specific poin...
Definition NonMaximumSuppression.h:136
void suppressNonMinimumSubset(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
Applies a non-minimum-suppression search on a subset of a given 2D frame in a 3x3 neighborhood (eight...
Definition NonMaximumSuppression.h:1240
NonMaximumSuppressionT(NonMaximumSuppressionT< T > &&nonMaximumSuppression) noexcept
Move constructor.
Definition NonMaximumSuppression.h:480
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:537
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:1003
StrengthCandidateRows rows_
All candidate rows.
Definition NonMaximumSuppression.h:420
unsigned int width_
Width of this object.
Definition NonMaximumSuppression.h:417
unsigned int width() const
Returns the width of this object.
Definition NonMaximumSuppression.h:502
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