Ocean
Loading...
Searching...
No Matches
PointDetector.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_CALIBRATION_POINT_DETECTOR_H
9#define META_OCEAN_CV_CALIBRATION_POINT_DETECTOR_H
10
13
14#include "ocean/base/Frame.h"
16#include "ocean/base/Worker.h"
17
20
22
23namespace Ocean
24{
25
26namespace CV
27{
28
29namespace Calibration
30{
31
32/**
33 * This class implements a point detector for marker points.
34 * The detector identifies both dark points on bright backgrounds and bright points on dark backgrounds.<br>
35 * It uses a border-based filter that compares the intensity of a center pixel with the intensities of surrounding border pixels.<br>
36 * The filter response is the sum of clamped squared differences between center and border pixels.<br>
37 * Detected candidates undergo non-maximum suppression followed by sub-pixel refinement.
38 * @ingroup cvcalibration
39 */
40class OCEAN_CV_CALIBRATION_EXPORT PointDetector
41{
43
44 public:
45
46 /**
47 * Definition of a pair combining an index with a distance.
48 */
49 using IndexDistancePair = std::pair<Index32, Scalar>;
50
51 /**
52 * Definition of a static vector holding IndexDistancePair objects.
53 * @tparam tNumber The number of elements the vector can hold at most, with range [1, infinity)
54 */
55 template <unsigned int tNumber>
57
58 /**
59 * Definition of the border shape of the detection filter.
60 */
61 enum BorderShape : uint32_t
62 {
63 /// Invalid border shape.
64 BS_INVALID = 0u,
65 /// Square-shaped border (all pixels along the perimeter of a square).
67 /// Circle-shaped border (all pixels on a Bresenham circle).
68 BS_CIRCLE
69 };
70
71 protected:
72
73 /**
74 * This class manages the border pixel offsets used by the point detection filter.
75 * The offsets define the relative positions of the border pixels around a center pixel.<br>
76 * Offsets are cached and reused as long as the filter parameters and frame layout remain unchanged.
77 */
79 {
80 public:
81
82 /**
83 * Updates the border offsets for a given filter configuration and frame layout.
84 * If the parameters match the cached state, no recalculation is performed.
85 * @param filterSize The size of the filter, in pixels, must be odd, with range [3, infinity)
86 * @param borderShape The shape of the border, must not be BS_INVALID
87 * @param width The width of the frame in which the filter will be applied, in pixels, with range [filterSize, infinity)
88 * @param paddingElements The number of padding elements at the end of each frame row, in elements, with range [0, infinity)
89 * @return True, if the offsets were successfully updated or already up to date
90 */
91 bool update(const unsigned int filterSize, const BorderShape borderShape, const unsigned int width, const unsigned int paddingElements);
92
93 /**
94 * Returns the 1D border offsets for the current filter configuration.
95 * @return The border offsets, empty if not yet initialized
96 */
97 inline const Indices32& offsets() const;
98
99 /**
100 * Determines the 2D border offsets for a given filter size and border shape.
101 * The resulting offsets are relative to the center pixel of the filter.
102 * @param filterSize The size of the filter, in pixels, must be odd, with range [3, infinity)
103 * @param borderShape The shape of the border, must not be BS_INVALID
104 * @param borderOffsets The resulting 2D border offsets, must be empty
105 * @return True, if succeeded
106 */
107 static bool determineBorderOffsets(const unsigned int filterSize, const BorderShape borderShape, CV::PixelPositionsI& borderOffsets);
108
109 /**
110 * Determines the 1D border offsets for a given filter size, border shape, and frame stride.
111 * The first offset is the negative offset from the center pixel to the top-left starting position.<br>
112 * All subsequent offsets are positive, each relative to the previous position.
113 * @param strideElements The stride of the frame, in elements (width + padding), with range [filterSize, infinity)
114 * @param filterSize The size of the filter, in pixels, must be odd, with range [3, infinity)
115 * @param borderShape The shape of the border, must not be BS_INVALID
116 * @param unsignedBorderOffsets The resulting 1D border offsets, must be empty
117 * @return True, if succeeded
118 */
119 static bool determineBorderOffsets(const unsigned int strideElements, const unsigned int filterSize, const BorderShape borderShape, Indices32& unsignedBorderOffsets);
120
121 protected:
122
123 /// The cached filter size, 0 if not yet initialized.
124 unsigned int filterSize_ = 0u;
125
126 /// The cached border shape.
127 BorderShape borderShape_ = BS_INVALID;
128
129 /// The cached frame width, in pixels.
130 unsigned int width_ = 0u;
131
132 /// The cached number of padding elements.
133 unsigned int paddingElements_ = 0u;
134
135 /// The border offsets for the current filter configuration.
137 };
138
139 /**
140 * This class implements a peak detector that refines the sub-pixel position of a detected point.
141 * The refinement uses iterative gradient-based optimization on the grayscale image.
142 */
144 {
145 public:
146
147 /**
148 * Creates a new peak detector for a given grayscale frame.
149 * @param yFrame The grayscale frame in which peaks will be refined, with pixel format FORMAT_Y8, must be valid and must exist as long as this object exists
150 */
151 explicit PointPeakDetector(const Frame& yFrame);
152
153 /**
154 * Determines the precise sub-pixel position of a detected point.
155 * @param x The horizontal pixel position of the detected point, with range [1, width - 2]
156 * @param y The vertical pixel position of the detected point, with range [1, height - 2]
157 * @param strength The integer strength of the detected point (positive for dark points, negative for bright points)
158 * @param preciseX The resulting precise horizontal position
159 * @param preciseY The resulting precise vertical position
160 * @param preciseStrength The resulting precise strength (currently set to the input strength)
161 * @return True, if succeeded
162 */
163 bool determinePrecisePosition(const unsigned int x, const unsigned int y, const int32_t strength, Scalar& preciseX, Scalar& preciseY, int32_t& preciseStrength) const;
164
165 protected:
166
167 /// The grayscale frame used for peak refinement with pixel format FORMAT_Y8.
169 };
170
171 /**
172 * Definition of the non-maximum suppression type used by this detector.
173 * Uses int32_t votes to support both positive (dark point) and negative (bright point) responses.
174 */
176
177 public:
178
179 /**
180 * Creates a new point detector.
181 */
182 PointDetector() = default;
183
184 /**
185 * Detects points in a new frame.
186 * Previously detected points will be replaced.
187 * @param yFrame The frame in which the points will be detected, with pixel format FORMAT_Y8, must be valid
188 * @param worker Optional worker object to distribute the computation
189 * @return True, if succeeded
190 */
191 bool detectPoints(const Frame& yFrame, Worker* worker = nullptr);
192
193 /**
194 * Returns the points detected in the latest frame.
195 * @return The latest's frame points
196 */
197 inline const Points& points() const;
198
199 /**
200 * Returns the spatial distribution array of the points detected in the latest frame.
201 * @return The latest's frame points distribution array
202 */
203 inline const Geometry::SpatialDistribution::DistributionArray& pointsDistributionArray() const;
204
205 /**
206 * Detects points in a grayscale frame using a specified filter size and border shape.
207 * @param yFrame The grayscale frame in which the points will be detected, with pixel format FORMAT_Y8, must be valid
208 * @param filterSize The size of the detection filter, in pixels, must be odd, with range [3, infinity)
209 * @param points The resulting detected points
210 * @param borderShape The shape of the detection filter border
211 * @param minimalDifference The minimal intensity difference between the center pixel and each border pixel to accept a candidate, with range [1, infinity)
212 * @param maximalDifference The maximal intensity difference used for clamping the squared differences, with range [minimalDifference, infinity)
213 * @param worker Optional worker object to distribute the computation
214 * @return True, if succeeded
215 */
216 static bool detectPoints(const Frame& yFrame, const unsigned int filterSize, Points& points, const BorderShape borderShape, const int32_t minimalDifference = 25, const int32_t maximalDifference = 25 * 4, Worker* worker = nullptr);
217
218 /**
219 * Detects points in a grayscale frame using pre-computed border offsets.
220 * @param yFrame The grayscale frame in which the points will be detected, with pixel format FORMAT_Y8, must be valid
221 * @param points The resulting detected points
222 * @param borderOffsets The pre-computed 1D border offsets, must be valid
223 * @param numberBorderOffsets The number of border offsets, with range [1, infinity)
224 * @param filterSize The size of the detection filter, in pixels, must be odd, with range [3, infinity)
225 * @param minimalDifference The minimal intensity difference between the center pixel and each border pixel to accept a candidate, with range [1, infinity)
226 * @param maximalDifference The maximal intensity difference used for clamping the squared differences, with range [minimalDifference, infinity)
227 * @param worker Optional worker object to distribute the computation
228 * @return True, if succeeded
229 */
230 static bool detectPoints(const Frame& yFrame, Points& points, const uint32_t* borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference = 25, const int32_t maximalDifference = 25 * 4, Worker* worker = nullptr);
231
232 /**
233 * Creates a filter response frame for visualization or debugging.
234 * Each pixel in the response frame contains the signed filter response: positive for dark points, negative for bright points, zero for non-detections.
235 * @param yFrame The grayscale input frame, with pixel format FORMAT_Y8, must be valid
236 * @param responseFrame The resulting filter response frame with pixel format genericPixelFormat<int32_t, 1u>
237 * @param filterSize The size of the detection filter, in pixels, must be odd, with range [3, infinity)
238 * @param borderShape The shape of the detection filter border
239 * @param minimalDifference The minimal intensity difference between the center pixel and each border pixel, with range [1, infinity)
240 * @param maximalDifference The maximal intensity difference used for clamping the squared differences, with range [minimalDifference, infinity)
241 * @param worker Optional worker object to distribute the computation
242 * @return True, if succeeded
243 */
244 static bool createFilterResponseFrame(const Frame& yFrame, Frame& responseFrame, const unsigned int filterSize, const BorderShape borderShape, const int32_t minimalDifference = 25, const int32_t maximalDifference = 25 * 4, Worker* worker = nullptr);
245
246 /**
247 * Refines the sub-pixel position of a point by snapping it to the nearest peak in the image.
248 * The function rounds the given sub-pixel position to the nearest pixel, then applies iterative peak refinement to determine the precise sub-pixel center of the dot.
249 * @param yFrame The grayscale frame in which the point will be refined, with pixel format FORMAT_Y8, must be valid
250 * @param roughPosition The rough sub-pixel position of the point to be refined, with range [1, width - 2]x[1, height - 2]
251 * @param positiveSign True, if the point is a dark dot on a bright background (positive sign); False, if the point is a bright dot on a dark background (negative sign)
252 * @param refinedPosition The resulting refined sub-pixel position
253 * @return True, if the refinement succeeded
254 */
255 static bool refinePointPosition(const Frame& yFrame, const Vector2& roughPosition, const bool positiveSign, Vector2& refinedPosition);
256
257 /**
258 * Returns the closest point to a given point.
259 * @param queryPoint The query point for which the closest point will be determined
260 * @param sign The sign of the point the closest point must have, true for dark points, false for bright points
261 * @param pointsDistributionArray The distribution array of the points to be used
262 * @param points The points from which the closest point will be determined
263 * @param maxSqrDistance The maximal square distance between the given point and the closest point, with range [0, infinity)
264 * @return The index of the closest point, -1 if no point could be found
265 */
266 static size_t closestPoint(const Vector2& queryPoint, const bool sign, const Geometry::SpatialDistribution::DistributionArray& pointsDistributionArray, const Points& points, const Scalar maxSqrDistance);
267
268 /**
269 * Returns the closest points to a given point.
270 * @param pointsDistributionArray The distribution array of the points to be used
271 * @param queryPointIndex The index of the query point for which the closest points will be determined, with range [0, points.size() - 1]
272 * @param points The points from which the closest points will be determined
273 * @param indexDistancePairs The resulting index/distance pairs of the closest points
274 * @param maxSqrDistance The maximal square distance between the given point and the closest point, with range [0, infinity)
275 * @tparam tNumber The number of closest points to be determined, with range [1, infinity)
276 * @tparam tMatchSign True, to determine closest points with same sign as the query point; False, to determine closest points with any sign
277 */
278 template <unsigned int tNumber, bool tMatchSign>
279 static void closestPoints(const Geometry::SpatialDistribution::DistributionArray& pointsDistributionArray, const size_t queryPointIndex, const Points& points, IndexDistancePairs<tNumber>& indexDistancePairs, const Scalar maxSqrDistance);
280
281 /**
282 * Returns the two closest points to a given point.
283 * @param queryPoint The query point for which the closest points will be determined
284 * @param pointsDistributionArray The distribution array of the points to be used
285 * @param points The points from which the closest points will be determined
286 * @param closestPointIndex The resulting index of the closest point, with range [0, points.size() - 1], -1 if no point could be found
287 * @param secondClosestPointIndex The resulting index of the second closest point, with range [0, points.size() - 1], -1 if no second closest point could be found
288 * @param closestSqrDistance The resulting square distance between the query point and the closest point, with range [0, infinity)
289 * @param secondClosestSqrDistance The resulting square distance between the query point and the second closest point, with range [0, infinity)
290 * @return True, if at least one closest point could be found
291 */
292 static bool closestPoints(const Vector2& queryPoint, const Geometry::SpatialDistribution::DistributionArray& pointsDistributionArray, const Points& points, Index32& closestPointIndex, Index32& secondClosestPointIndex, Scalar& closestSqrDistance, Scalar& secondClosestSqrDistance);
293
294 protected:
295
296 /**
297 * Detects point candidates in a subset of rows and adds them to a non-maximum suppression object.
298 * This is a worker function for parallel execution.
299 * @param yFrame The grayscale frame, with pixel format FORMAT_Y8, must be valid
300 * @param nonMaximumSuppression The non-maximum suppression object to which detected candidates will be added, must be valid
301 * @param borderOffsets The pre-computed 1D border offsets, must be valid
302 * @param numberBorderOffsets The number of border offsets, with range [1, infinity)
303 * @param filterSize The size of the detection filter, in pixels, must be odd, with range [3, infinity)
304 * @param minimalDifference The minimal intensity difference between the center pixel and each border pixel, with range [1, infinity)
305 * @param maximalDifference The maximal intensity difference used for clamping, with range [minimalDifference, infinity)
306 * @param firstRow The first row to process, with range [filterSize / 2, height - filterSize / 2)
307 * @param numberRows The number of rows to process, with range [1, height - filterSize / 2 - firstRow]
308 */
309 static void detectPointCandidatesSubset(const Frame* yFrame, NonMaximumSuppressionVote* nonMaximumSuppression, const uint32_t* borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference, const unsigned int firstRow, const unsigned int numberRows);
310
311 /**
312 * Creates filter responses for a subset of rows and writes them into the response frame.
313 * This is a worker function for parallel execution.
314 * @param yFrame The grayscale frame, with pixel format FORMAT_Y8, must be valid
315 * @param responseFrame The response frame with pixel format genericPixelFormat<int32_t, 1u>, must be valid
316 * @param borderOffsets The pre-computed 1D border offsets, must be valid
317 * @param numberBorderOffsets The number of border offsets, with range [1, infinity)
318 * @param filterSize The size of the detection filter, in pixels, must be odd, with range [3, infinity)
319 * @param minimalDifference The minimal intensity difference between the center pixel and each border pixel, with range [1, infinity)
320 * @param maximalDifference The maximal intensity difference used for clamping, with range [minimalDifference, infinity)
321 * @param firstRow The first row to process, with range [0, height)
322 * @param numberRows The number of rows to process, with range [1, height - firstRow]
323 */
324 static void createFilterResponseFrameSubset(const Frame* yFrame, Frame* responseFrame, const uint32_t* borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference, const unsigned int firstRow, const unsigned int numberRows);
325
326 /**
327 * Determines point candidates for a single row (single-sign: dark points only).
328 * @param y The row index, with range [filterSize / 2, height - filterSize / 2)
329 * @param yRow Pointer to the beginning of the row in the grayscale frame, must be valid
330 * @param nonMaximumSuppression The non-maximum suppression object to which detected candidates will be added, must be valid
331 * @param borderOffsets The pre-computed 1D border offsets, must be valid
332 * @param numberBorderOffsets The number of border offsets, with range [1, infinity)
333 * @param filterSize The size of the detection filter, in pixels, must be odd, with range [3, infinity)
334 * @param minimalDifference The minimal intensity difference between the center pixel and each border pixel, with range [1, infinity)
335 * @param maximalDifference The maximal intensity difference used for clamping, with range [minimalDifference, infinity)
336 */
337 static void determinePointCandidatesRow(const unsigned int y, const uint8_t* yRow, NonMaximumSuppressionVote* nonMaximumSuppression, const uint32_t* borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference);
338
339 /**
340 * Determines point candidates for a single row (dual-sign: both dark and bright points).
341 * Dark points produce a positive response; bright points produce a negative response.
342 * @param y The row index, with range [filterSize / 2, height - filterSize / 2)
343 * @param yRow Pointer to the beginning of the row in the grayscale frame, must be valid
344 * @param nonMaximumSuppression The non-maximum suppression object to which detected candidates will be added, must be valid
345 * @param borderOffsets The pre-computed 1D border offsets, must be valid
346 * @param numberBorderOffsets The number of border offsets, with range [1, infinity)
347 * @param filterSize The size of the detection filter, in pixels, must be odd, with range [3, infinity)
348 * @param minimalDifference The minimal intensity difference between the center pixel and each border pixel, with range [1, infinity)
349 * @param maximalDifference The maximal intensity difference used for clamping, with range [minimalDifference, infinity)
350 */
351 static void determinePointCandidatesRowDual(const unsigned int y, const uint8_t* yRow, NonMaximumSuppressionVote* nonMaximumSuppression, const uint32_t* borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference);
352
353 /**
354 * Creates a filter response row (single-sign: dark points only).
355 * @param yRow Pointer to the beginning of the row in the grayscale frame, must be valid
356 * @param responseRow The resulting response row, must be valid
357 * @param width The width of the row, in pixels, with range [filterSize, infinity)
358 * @param borderOffsets The pre-computed 1D border offsets, must be valid
359 * @param numberBorderOffsets The number of border offsets, with range [1, infinity)
360 * @param filterSize The size of the detection filter, in pixels, must be odd, with range [3, infinity)
361 * @param minimalDifference The minimal intensity difference between the center pixel and each border pixel, with range [1, infinity)
362 * @param maximalDifference The maximal intensity difference used for clamping the squared differences, with range [minimalDifference, infinity)
363 */
364 static void createFilterResponseRow(const uint8_t* yRow, int32_t* responseRow, const unsigned int width, const uint32_t* borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference);
365
366 /**
367 * Creates a filter response row (dual-sign: both dark and bright points).
368 * Dark points produce a positive response; bright points produce a negative response.
369 * @param yRow Pointer to the beginning of the row in the grayscale frame, must be valid
370 * @param responseRow The resulting response row, must be valid
371 * @param width The width of the row, in pixels, with range [filterSize, infinity)
372 * @param borderOffsets The pre-computed 1D border offsets, must be valid
373 * @param numberBorderOffsets The number of border offsets, with range [1, infinity)
374 * @param filterSize The size of the detection filter, in pixels, must be odd, with range [3, infinity)
375 * @param minimalDifference The minimal intensity difference between the center pixel and each border pixel, with range [1, infinity)
376 * @param maximalDifference The maximal intensity difference used for clamping the squared differences, with range [minimalDifference, infinity)
377 */
378 static void createFilterResponseRowDual(const uint8_t* yRow, int32_t* responseRow, const unsigned int width, const uint32_t* borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference);
379
380#if defined(OCEAN_HARDWARE_SSE_VERSION) && OCEAN_HARDWARE_SSE_VERSION >= 41
381
382 /**
383 * Creates a filter response row using SSE4.1 SIMD instructions (single-sign: dark points only).
384 * This is an optimized implementation that processes 8 pixels per iteration.
385 * @param yRow Pointer to the beginning of the row in the grayscale frame, must be valid
386 * @param responseRow The resulting response row, must be valid
387 * @param width The width of the row, in pixels, with range [filterSize / 2 + 8 + filterSize / 2, infinity)
388 * @param borderOffsets The pre-computed 1D border offsets, must be valid
389 * @param numberBorderOffsets The number of border offsets, with range [1, infinity)
390 * @param filterSize The size of the detection filter, in pixels, must be odd, with range [3, infinity)
391 * @param minimalDifference The minimal intensity difference between the center pixel and each border pixel, with range [1, infinity)
392 * @param maximalDifference The maximal intensity difference used for clamping the squared differences, with range [minimalDifference, infinity)
393 */
394 static void createFilterResponseRowSSE(const uint8_t* yRow, int32_t* responseRow, const unsigned int width, const uint32_t* borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference);
395
396 /**
397 * Creates a filter response row using SSE4.1 SIMD instructions (dual-sign: both dark and bright points).
398 * Detects both dark points on bright backgrounds (positive response) and bright points on dark backgrounds (negative response).
399 * This is an optimized implementation that processes 8 pixels per iteration.
400 * @param yRow Pointer to the beginning of the row in the grayscale frame, must be valid
401 * @param responseRow The resulting response row, must be valid
402 * @param width The width of the row, in pixels, with range [filterSize / 2 + 8 + filterSize / 2, infinity)
403 * @param borderOffsets The pre-computed 1D border offsets, must be valid
404 * @param numberBorderOffsets The number of border offsets, with range [1, infinity)
405 * @param filterSize The size of the detection filter, in pixels, must be odd, with range [3, infinity)
406 * @param minimalDifference The minimal intensity difference between the center pixel and each border pixel, with range [1, infinity)
407 * @param maximalDifference The maximal intensity difference used for clamping the squared differences, with range [minimalDifference, infinity)
408 */
409 static void createFilterResponseRowSSEDual(const uint8_t* yRow, int32_t* responseRow, const unsigned int width, const uint32_t* borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference);
410
411 /**
412 * Determines point candidates for a single row using SSE4.1 SIMD instructions (single-sign: dark points only).
413 * This is an optimized implementation that processes 8 pixels per iteration.
414 * @param y The row index, with range [filterSize / 2, height - filterSize / 2)
415 * @param yRow Pointer to the beginning of the row in the grayscale frame, must be valid
416 * @param nonMaximumSuppression The non-maximum suppression object to which detected candidates will be added, must be valid
417 * @param borderOffsets The pre-computed 1D border offsets, must be valid
418 * @param numberBorderOffsets The number of border offsets, with range [1, infinity)
419 * @param filterSize The size of the detection filter, in pixels, must be odd, with range [3, infinity)
420 * @param minimalDifference The minimal intensity difference between the center pixel and each border pixel, with range [1, infinity)
421 * @param maximalDifference The maximal intensity difference used for clamping the squared differences, with range [minimalDifference, infinity)
422 */
423 static void determinePointCandidatesRowSSE(const unsigned int y, const uint8_t* yRow, NonMaximumSuppressionVote* nonMaximumSuppression, const uint32_t* borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference);
424
425 /**
426 * Determines point candidates for a single row using SSE4.1 SIMD instructions (dual-sign: both dark and bright points).
427 * This is an optimized implementation that processes 8 pixels per iteration.
428 * @param y The row index, with range [filterSize / 2, height - filterSize / 2)
429 * @param yRow Pointer to the beginning of the row in the grayscale frame, must be valid
430 * @param nonMaximumSuppression The non-maximum suppression object to which detected candidates will be added, must be valid
431 * @param borderOffsets The pre-computed 1D border offsets, must be valid
432 * @param numberBorderOffsets The number of border offsets, with range [1, infinity)
433 * @param filterSize The size of the detection filter, in pixels, must be odd, with range [3, infinity)
434 * @param minimalDifference The minimal intensity difference between the center pixel and each border pixel, with range [1, infinity)
435 * @param maximalDifference The maximal intensity difference used for clamping the squared differences, with range [minimalDifference, infinity)
436 */
437 static void determinePointCandidatesRowSSEDual(const unsigned int y, const uint8_t* yRow, NonMaximumSuppressionVote* nonMaximumSuppression, const uint32_t* borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference);
438
439#endif // OCEAN_HARDWARE_SSE_VERSION >= 41
440
441#if defined(OCEAN_HARDWARE_NEON_VERSION) && OCEAN_HARDWARE_NEON_VERSION >= 10
442
443 /**
444 * Creates a filter response row using NEON SIMD instructions (single-sign: dark points only).
445 * This is an optimized implementation that processes 8 pixels per iteration.
446 * @param yRow Pointer to the beginning of the row in the grayscale frame, must be valid
447 * @param responseRow The resulting response row, must be valid
448 * @param width The width of the row, in pixels, with range [filterSize / 2 + 8 + filterSize / 2, infinity)
449 * @param borderOffsets The pre-computed 1D border offsets, must be valid
450 * @param numberBorderOffsets The number of border offsets, with range [1, infinity)
451 * @param filterSize The size of the detection filter, in pixels, must be odd, with range [3, infinity)
452 * @param minimalDifference The minimal intensity difference between the center pixel and each border pixel, with range [1, infinity)
453 * @param maximalDifference The maximal intensity difference used for clamping the squared differences, with range [minimalDifference, infinity)
454 */
455 static void createFilterResponseRowNEON(const uint8_t* yRow, int32_t* responseRow, const unsigned int width, const uint32_t* borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference);
456
457 /**
458 * Creates a filter response row using NEON SIMD instructions (dual-sign: both dark and bright points).
459 * Detects both dark points on bright backgrounds (positive response) and bright points on dark backgrounds (negative response).
460 * This is an optimized implementation that processes 8 pixels per iteration.
461 * @param yRow Pointer to the beginning of the row in the grayscale frame, must be valid
462 * @param responseRow The resulting response row, must be valid
463 * @param width The width of the row, in pixels, with range [filterSize / 2 + 8 + filterSize / 2, infinity)
464 * @param borderOffsets The pre-computed 1D border offsets, must be valid
465 * @param numberBorderOffsets The number of border offsets, with range [1, infinity)
466 * @param filterSize The size of the detection filter, in pixels, must be odd, with range [3, infinity)
467 * @param minimalDifference The minimal intensity difference between the center pixel and each border pixel, with range [1, infinity)
468 * @param maximalDifference The maximal intensity difference used for clamping the squared differences, with range [minimalDifference, infinity)
469 */
470 static void createFilterResponseRowNEONDual(const uint8_t* yRow, int32_t* responseRow, const unsigned int width, const uint32_t* borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference);
471
472 /**
473 * Determines point candidates for a single row using NEON SIMD instructions (single-sign: dark points only).
474 * This is an optimized implementation that processes 8 pixels per iteration.
475 * @param y The row index, with range [filterSize / 2, height - filterSize / 2)
476 * @param yRow Pointer to the beginning of the row in the grayscale frame, must be valid
477 * @param nonMaximumSuppression The non-maximum suppression object to which detected candidates will be added, must be valid
478 * @param borderOffsets The pre-computed 1D border offsets, must be valid
479 * @param numberBorderOffsets The number of border offsets, with range [1, infinity)
480 * @param filterSize The size of the detection filter, in pixels, must be odd, with range [3, infinity)
481 * @param minimalDifference The minimal intensity difference between the center pixel and each border pixel, with range [1, infinity)
482 * @param maximalDifference The maximal intensity difference used for clamping the squared differences, with range [minimalDifference, infinity)
483 */
484 static void determinePointCandidatesRowNEON(const unsigned int y, const uint8_t* yRow, NonMaximumSuppressionVote* nonMaximumSuppression, const uint32_t* borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference);
485
486 /**
487 * Determines point candidates for a single row using NEON SIMD instructions (dual-sign: both dark and bright points).
488 * This is an optimized implementation that processes 8 pixels per iteration.
489 * @param y The row index, with range [filterSize / 2, height - filterSize / 2)
490 * @param yRow Pointer to the beginning of the row in the grayscale frame, must be valid
491 * @param nonMaximumSuppression The non-maximum suppression object to which detected candidates will be added, must be valid
492 * @param borderOffsets The pre-computed 1D border offsets, must be valid
493 * @param numberBorderOffsets The number of border offsets, with range [1, infinity)
494 * @param filterSize The size of the detection filter, in pixels, must be odd, with range [3, infinity)
495 * @param minimalDifference The minimal intensity difference between the center pixel and each border pixel, with range [1, infinity)
496 * @param maximalDifference The maximal intensity difference used for clamping the squared differences, with range [minimalDifference, infinity)
497 */
498 static void determinePointCandidatesRowNEONDual(const unsigned int y, const uint8_t* yRow, NonMaximumSuppressionVote* nonMaximumSuppression, const uint32_t* borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference);
499
500#endif // OCEAN_HARDWARE_NEON_VERSION >= 10
501
502 protected:
503
504 /**
505 * Removes duplicated points from a given set of points.
506 * @param width The width of the frame in which the points have been detected, in pixel, with range [1, infinity)
507 * @param height The height of the frame in which the points have been detected, in pixel, with range [1, infinity)
508 * @param points The points from which the duplicated points will be removed
509 * @param maxDistance The maximal distance between two points to be considered as duplicated, with range [0, infinity)
510 */
511 static void removeDuplicatedPoints(const unsigned int width, const unsigned int height, Points& points, const Scalar maxDistance);
512
513 /**
514 * Returns whether a query point is close to another point.
515 * @param queryPoint The query point to check
516 * @param pointsDistributionArray The distribution array of the points to be used
517 * @param points The points from which the closest point will be determined
518 * @param maxSqrDistance The maximal square distance between the given point and the closest point, with range [0, infinity)
519 * @return True, if so
520 */
521 static bool hasClosePoint(const Vector2& queryPoint, const Geometry::SpatialDistribution::DistributionArray& pointsDistributionArray, const Points& points, const Scalar maxSqrDistance);
522
523 /**
524 * Returns the closest points to a query point.
525 * @param queryPoint The query point for which the closest points will be determined
526 * @param pointsDistributionArray The distribution array of the points to be used
527 * @param points The points from which the closest point will be determined
528 * @param maxSqrDistance The maximal square distance between the given point and the closest point, with range [0, infinity)
529 * @param pointIndices The resulting indices of the closest points
530 * @return True, if at least one closest point could be found
531 */
532 static bool closestPoints(const Vector2& queryPoint, const Geometry::SpatialDistribution::DistributionArray& pointsDistributionArray, const Points& points, const Scalar maxSqrDistance, Indices32& pointIndices);
533
534 protected:
535
536 /// The size of the detection filter, in pixels, must be odd, with range [3, infinity).
537 unsigned int filterSize_ = 7u;
538
539 /// The minimal intensity difference between the center pixel and each border pixel, with range [1, infinity).
540 int32_t minimalDifference_ = 25;
541
542 /// The maximal intensity difference used for clamping the squared differences, with range [minimalDifference_, infinity).
543 int32_t maximalDifference_ = 25 * 4;
544
545 /// The cached border offsets for the current filter configuration.
547
548 /// The maximal distance between two points to be considered as duplicated, with range [0, infinity).
549 Scalar maxDistanceBetweenDuplicatePoints_ = Scalar(2);
550
551 /// The precise points detected in the latest frame.
553
554 /// The spatial distribution array of the points detected in the latest frame.
556};
557
559{
560 return offsets_;
561}
562
563template <unsigned int tNumber, bool tMatchSign>
564void PointDetector::closestPoints(const Geometry::SpatialDistribution::DistributionArray& pointsDistributionArray, const size_t queryPointIndex, const Points& points, IndexDistancePairs<tNumber>& indexDistancePairs, const Scalar maxSqrDistance)
565{
566 static_assert(tNumber >= 1u, "Invalid number of points!");
567
568 ocean_assert(indexDistancePairs.empty());
569 ocean_assert(queryPointIndex < points.size());
570
571 const Vector2& point = points[queryPointIndex].observation();
572
573 const unsigned int xBinCenter = pointsDistributionArray.horizontalBin(point.x());
574 const unsigned int yBinCenter = pointsDistributionArray.verticalBin(point.y());
575
576 for (unsigned int xBin = pointsDistributionArray.beginBinHorizontal<1u>(xBinCenter); xBin < pointsDistributionArray.endBinHorizontal<1u>(xBinCenter); ++xBin)
577 {
578 for (unsigned int yBin = pointsDistributionArray.beginBinVertical<1u>(yBinCenter); yBin < pointsDistributionArray.endBinVertical<1u>(yBinCenter); ++yBin)
579 {
580 const Indices32& indices = pointsDistributionArray(xBin, yBin);
581
582 for (const Index32& index : indices)
583 {
584 if (index == Index32(queryPointIndex))
585 {
586 continue;
587 }
588
589 if constexpr (tMatchSign)
590 {
591 if (points[queryPointIndex].sign() != points[index].sign())
592 {
593 continue;
594 }
595 }
596
597 const Scalar sqrDistance = point.sqrDistance(points[index].observation());
598
599 if (sqrDistance > maxSqrDistance)
600 {
601 continue;
602 }
603
604 bool inserted = false;
605
606 if (!indexDistancePairs.empty() && sqrDistance < indexDistancePairs.back().second)
607 {
608 for (size_t nCandidate = 0; nCandidate < indexDistancePairs.size(); ++nCandidate)
609 {
610 if (sqrDistance < indexDistancePairs[nCandidate].second)
611 {
612 if (indexDistancePairs.size() != tNumber)
613 {
614 indexDistancePairs.weakResize(indexDistancePairs.size() + 1);
615 }
616
617 for (size_t n = indexDistancePairs.size() - 1; n > nCandidate; --n)
618 {
619 indexDistancePairs[n] = indexDistancePairs[n - 1];
620 }
621
622 indexDistancePairs[nCandidate] = IndexDistancePair(index, sqrDistance);
623
624 inserted = true;
625 break;
626
627 }
628 }
629 }
630
631 if (!inserted && indexDistancePairs.size() < tNumber)
632 {
633 indexDistancePairs.pushBack(IndexDistancePair(index, sqrDistance));
634 }
635 }
636 }
637 }
638}
639
640inline const Points& PointDetector::points() const
641{
642 return points_;
643}
644
649
650}
651
652}
653
654}
655
656#endif // META_OCEAN_CV_CALIBRATION_POINT_DETECTOR_H
This class implements debug elements for the calibration library.
Definition CalibrationDebugElements.h:43
This class manages the border pixel offsets used by the point detection filter.
Definition PointDetector.h:79
static bool determineBorderOffsets(const unsigned int strideElements, const unsigned int filterSize, const BorderShape borderShape, Indices32 &unsignedBorderOffsets)
Determines the 1D border offsets for a given filter size, border shape, and frame stride.
const Indices32 & offsets() const
Returns the 1D border offsets for the current filter configuration.
Definition PointDetector.h:558
static bool determineBorderOffsets(const unsigned int filterSize, const BorderShape borderShape, CV::PixelPositionsI &borderOffsets)
Determines the 2D border offsets for a given filter size and border shape.
Indices32 offsets_
The border offsets for the current filter configuration.
Definition PointDetector.h:136
bool update(const unsigned int filterSize, const BorderShape borderShape, const unsigned int width, const unsigned int paddingElements)
Updates the border offsets for a given filter configuration and frame layout.
This class implements a peak detector that refines the sub-pixel position of a detected point.
Definition PointDetector.h:144
bool determinePrecisePosition(const unsigned int x, const unsigned int y, const int32_t strength, Scalar &preciseX, Scalar &preciseY, int32_t &preciseStrength) const
Determines the precise sub-pixel position of a detected point.
PointPeakDetector(const Frame &yFrame)
Creates a new peak detector for a given grayscale frame.
const Frame & yFrame_
The grayscale frame used for peak refinement with pixel format FORMAT_Y8.
Definition PointDetector.h:168
This class implements a point detector for marker points.
Definition PointDetector.h:41
Points points_
The precise points detected in the latest frame.
Definition PointDetector.h:552
static bool createFilterResponseFrame(const Frame &yFrame, Frame &responseFrame, const unsigned int filterSize, const BorderShape borderShape, const int32_t minimalDifference=25, const int32_t maximalDifference=25 *4, Worker *worker=nullptr)
Creates a filter response frame for visualization or debugging.
static void removeDuplicatedPoints(const unsigned int width, const unsigned int height, Points &points, const Scalar maxDistance)
Removes duplicated points from a given set of points.
static bool detectPoints(const Frame &yFrame, const unsigned int filterSize, Points &points, const BorderShape borderShape, const int32_t minimalDifference=25, const int32_t maximalDifference=25 *4, Worker *worker=nullptr)
Detects points in a grayscale frame using a specified filter size and border shape.
static void createFilterResponseRowDual(const uint8_t *yRow, int32_t *responseRow, const unsigned int width, const uint32_t *borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference)
Creates a filter response row (dual-sign: both dark and bright points).
PointDetector()=default
Creates a new point detector.
static void determinePointCandidatesRowNEONDual(const unsigned int y, const uint8_t *yRow, NonMaximumSuppressionVote *nonMaximumSuppression, const uint32_t *borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference)
Determines point candidates for a single row using NEON SIMD instructions (dual-sign: both dark and b...
BorderShape
Definition of the border shape of the detection filter.
Definition PointDetector.h:62
@ BS_SQUARE
Square-shaped border (all pixels along the perimeter of a square).
Definition PointDetector.h:66
Geometry::SpatialDistribution::DistributionArray pointsDistributionArray_
The spatial distribution array of the points detected in the latest frame.
Definition PointDetector.h:555
std::pair< Index32, Scalar > IndexDistancePair
Definition of a pair combining an index with a distance.
Definition PointDetector.h:49
static bool closestPoints(const Vector2 &queryPoint, const Geometry::SpatialDistribution::DistributionArray &pointsDistributionArray, const Points &points, const Scalar maxSqrDistance, Indices32 &pointIndices)
Returns the closest points to a query point.
static bool hasClosePoint(const Vector2 &queryPoint, const Geometry::SpatialDistribution::DistributionArray &pointsDistributionArray, const Points &points, const Scalar maxSqrDistance)
Returns whether a query point is close to another point.
static void determinePointCandidatesRowSSEDual(const unsigned int y, const uint8_t *yRow, NonMaximumSuppressionVote *nonMaximumSuppression, const uint32_t *borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference)
Determines point candidates for a single row using SSE4.1 SIMD instructions (dual-sign: both dark and...
static bool refinePointPosition(const Frame &yFrame, const Vector2 &roughPosition, const bool positiveSign, Vector2 &refinedPosition)
Refines the sub-pixel position of a point by snapping it to the nearest peak in the image.
static bool detectPoints(const Frame &yFrame, Points &points, const uint32_t *borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference=25, const int32_t maximalDifference=25 *4, Worker *worker=nullptr)
Detects points in a grayscale frame using pre-computed border offsets.
static void createFilterResponseRow(const uint8_t *yRow, int32_t *responseRow, const unsigned int width, const uint32_t *borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference)
Creates a filter response row (single-sign: dark points only).
static void determinePointCandidatesRowSSE(const unsigned int y, const uint8_t *yRow, NonMaximumSuppressionVote *nonMaximumSuppression, const uint32_t *borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference)
Determines point candidates for a single row using SSE4.1 SIMD instructions (single-sign: dark points...
bool detectPoints(const Frame &yFrame, Worker *worker=nullptr)
Detects points in a new frame.
static void createFilterResponseRowNEON(const uint8_t *yRow, int32_t *responseRow, const unsigned int width, const uint32_t *borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference)
Creates a filter response row using NEON SIMD instructions (single-sign: dark points only).
static size_t closestPoint(const Vector2 &queryPoint, const bool sign, const Geometry::SpatialDistribution::DistributionArray &pointsDistributionArray, const Points &points, const Scalar maxSqrDistance)
Returns the closest point to a given point.
static void determinePointCandidatesRowNEON(const unsigned int y, const uint8_t *yRow, NonMaximumSuppressionVote *nonMaximumSuppression, const uint32_t *borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference)
Determines point candidates for a single row using NEON SIMD instructions (single-sign: dark points o...
static void determinePointCandidatesRowDual(const unsigned int y, const uint8_t *yRow, NonMaximumSuppressionVote *nonMaximumSuppression, const uint32_t *borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference)
Determines point candidates for a single row (dual-sign: both dark and bright points).
const Geometry::SpatialDistribution::DistributionArray & pointsDistributionArray() const
Returns the spatial distribution array of the points detected in the latest frame.
Definition PointDetector.h:645
static void createFilterResponseRowSSEDual(const uint8_t *yRow, int32_t *responseRow, const unsigned int width, const uint32_t *borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference)
Creates a filter response row using SSE4.1 SIMD instructions (dual-sign: both dark and bright points)...
PointBorderOffsets pointBorderOffsets_
The cached border offsets for the current filter configuration.
Definition PointDetector.h:546
static void createFilterResponseRowSSE(const uint8_t *yRow, int32_t *responseRow, const unsigned int width, const uint32_t *borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference)
Creates a filter response row using SSE4.1 SIMD instructions (single-sign: dark points only).
const Points & points() const
Returns the points detected in the latest frame.
Definition PointDetector.h:640
static void determinePointCandidatesRow(const unsigned int y, const uint8_t *yRow, NonMaximumSuppressionVote *nonMaximumSuppression, const uint32_t *borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference)
Determines point candidates for a single row (single-sign: dark points only).
static void createFilterResponseRowNEONDual(const uint8_t *yRow, int32_t *responseRow, const unsigned int width, const uint32_t *borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference)
Creates a filter response row using NEON SIMD instructions (dual-sign: both dark and bright points).
static bool closestPoints(const Vector2 &queryPoint, const Geometry::SpatialDistribution::DistributionArray &pointsDistributionArray, const Points &points, Index32 &closestPointIndex, Index32 &secondClosestPointIndex, Scalar &closestSqrDistance, Scalar &secondClosestSqrDistance)
Returns the two closest points to a given point.
static void detectPointCandidatesSubset(const Frame *yFrame, NonMaximumSuppressionVote *nonMaximumSuppression, const uint32_t *borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference, const unsigned int firstRow, const unsigned int numberRows)
Detects point candidates in a subset of rows and adds them to a non-maximum suppression object.
static void createFilterResponseFrameSubset(const Frame *yFrame, Frame *responseFrame, const uint32_t *borderOffsets, const size_t numberBorderOffsets, const unsigned int filterSize, const int32_t minimalDifference, const int32_t maximalDifference, const unsigned int firstRow, const unsigned int numberRows)
Creates filter responses for a subset of rows and writes them into the response frame.
static void closestPoints(const Geometry::SpatialDistribution::DistributionArray &pointsDistributionArray, const size_t queryPointIndex, const Points &points, IndexDistancePairs< tNumber > &indexDistancePairs, const Scalar maxSqrDistance)
Returns the closest points to a given point.
Definition PointDetector.h:564
This class implements the possibility to find local maximum in a 2D array by applying a non-maximum-s...
Definition NonMaximumSuppression.h:138
This class implements Ocean's image class.
Definition Frame.h:1879
int horizontalBin(const Scalar x) const
Returns the horizontal bin of a given horizontal position.
Definition SpatialDistribution.h:1148
int verticalBin(const Scalar y) const
Returns the vertical bin of a given vertical position.
Definition SpatialDistribution.h:1154
unsigned int endBinHorizontal(const unsigned int centerBinX) const
Returns the (exclusive) end bin in horizontal direction for a neighborhood search.
Definition SpatialDistribution.h:1183
unsigned int beginBinHorizontal(const unsigned int centerBinX) const
Returns the (inclusive) begin bin in horizontal direction for a neighborhood search.
Definition SpatialDistribution.h:1173
unsigned int beginBinVertical(const unsigned int centerBinY) const
Returns the (inclusive) begin bin in vertical direction for a neighborhood search.
Definition SpatialDistribution.h:1193
unsigned int endBinVertical(const unsigned int centerBinY) const
Returns the (exclusive) end bin in vertical direction for a neighborhood search.
Definition SpatialDistribution.h:1203
This class implements a distribution array.
Definition SpatialDistribution.h:272
This class implements a static vector that has a fixed capacity.
Definition StaticVector.h:25
size_t size() const
Returns the size of this vector.
Definition StaticVector.h:390
void weakResize(const size_t size)
Resizes this vector.
Definition StaticVector.h:645
bool empty() const
Returns whether this vector hold no element.
Definition StaticVector.h:584
void pushBack(const T &value)
Adds a new element to this vector.
Definition StaticVector.h:402
const T & back() const
Returns the last elements of this vector.
Definition StaticVector.h:544
const T & x() const noexcept
Returns the x value.
Definition Vector2.h:710
const T & 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
unsigned int sqrDistance(const char first, const char second)
Returns the square distance between two values.
Definition base/Utilities.h:1159
std::vector< Index32 > Indices32
Definition of a vector holding 32 bit index values.
Definition Base.h:96
uint32_t Index32
Definition of a 32 bit index value.
Definition Base.h:84
std::vector< PixelPositionI > PixelPositionsI
Definition of a vector holding pixel positions (with positive and negative coordinate values).
Definition PixelPosition.h:53
std::vector< Point > Points
Definition of a vector holding points.
Definition cv/calibration/Point.h:31
float Scalar
Definition of a scalar type.
Definition Math.h:129
The namespace covering the entire Ocean framework.
Definition Accessor.h:15