Ocean
Loading...
Searching...
No Matches
BullseyeDetectorMono.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 OCEAN_CV_DETECTOR_BULLSEYES_BULLSEYEDETECTORMONO_H
9#define OCEAN_CV_DETECTOR_BULLSEYES_BULLSEYEDETECTORMONO_H
10
14
15#include "ocean/base/Frame.h"
16#include "ocean/base/Lock.h"
17#include "ocean/base/Worker.h"
18
19namespace Ocean
20{
21
22namespace CV
23{
24
25namespace Detector
26{
27
28namespace Bullseyes
29{
30
31/**
32 * Implementation of a monocular detector for the bullseye pattern.
33 * @ingroup cvdetectorbullseyes
34 */
35class OCEAN_CV_DETECTOR_BULLSEYES_EXPORT BullseyeDetectorMono
36{
37 public:
38
39 /**
40 * This class holds the most important parameters for the detector.
41 *
42 * Parameter Guide:
43 *
44 * framePyramidPixelThreshold:
45 * Controls when to use multi-scale detection via image pyramids.
46 * - Default: 640 * 480 = 307200 pixels (VGA resolution)
47 * - For images larger than this threshold, pyramid layers are used to detect bullseyes at multiple scales
48 * - Smaller values enable pyramid processing for smaller images (more thorough but slower)
49 * - Larger values disable pyramid processing for more images (faster but may miss small bullseyes)
50 *
51 * framePyramidLayers:
52 * Number of pyramid layers to use for multi-scale detection.
53 * - Default: 3 layers
54 * - More layers detect smaller bullseyes but increase computation time
55 * - Typical range: 2-4 layers
56 *
57 * useAdaptiveRowSpacing:
58 * Whether to skip rows during detection for better performance.
59 * - Default: true (enabled)
60 * - When enabled: Rows are skipped based on image height (height/150)
61 * - When disabled: Every row is scanned (slower but more accurate)
62 * - Recommended: true for real-time applications, false for offline/accuracy-critical applications
63 *
64 * minimumDiameter:
65 * Minimum diameter in pixels for a valid bullseye detection.
66 * - Default: 15 pixels
67 * - Bullseyes smaller than this threshold are rejected
68 * - Helps filter out noise and false positives from tiny patterns
69 */
70 class OCEAN_CV_DETECTOR_BULLSEYES_EXPORT Parameters
71 {
72 public:
73
74 /**
75 * Creates a new valid parameter object.
76 */
77 Parameters() = default;
78
79 /**
80 * Returns whether the parameters are valid.
81 * @return True if the parameters are valid
82 */
83 bool isValid() const noexcept;
84
85 /**
86 * Returns the pixel threshold for frame pyramid creation.
87 * @return The pixel threshold value, with range [0, infinity)
88 */
89 unsigned int framePyramidPixelThreshold() const noexcept;
90
91 /**
92 * Sets the pixel threshold for frame pyramid creation.
93 * @param framePyramidPixelThreshold The pixel threshold value, with range [0, infinity)
94 */
95 void setFramePyramidPixelThreshold(unsigned int framePyramidPixelThreshold) noexcept;
96
97 /**
98 * Returns the number of layers for the frame pyramid.
99 * @return The number of pyramid layers, with range [1, infinity)
100 */
101 unsigned int framePyramidLayers() const noexcept;
102
103 /**
104 * Sets the number of layers for the frame pyramid.
105 * @param framePyramidLayers The number of pyramid layers, with range [1, infinity)
106 */
107 void setFramePyramidLayers(unsigned int framePyramidLayers) noexcept;
108
109 /**
110 * Returns whether adaptive row spacing is enabled during bullseye detection.
111 * @return True if adaptive row spacing is used (performance optimization), false if every row is scanned (higher accuracy)
112 */
113 bool useAdaptiveRowSpacing() const noexcept;
114
115 /**
116 * Sets whether adaptive row spacing should be used during bullseye detection.
117 * When enabled (true), the detector uses adaptive row spacing based on frame height for better performance.
118 * When disabled (false), every row is scanned for higher accuracy but slower performance.
119 * @param useAdaptiveRowSpacing True to enable adaptive spacing, false to scan every row
120 */
121 void setUseAdaptiveRowSpacing(bool useAdaptiveRowSpacing) noexcept;
122
123 /**
124 * Returns the minimum diameter for a valid bullseye detection.
125 * @return The minimum diameter in pixels, with range [1, infinity)
126 */
127 unsigned int minimumDiameter() const noexcept;
128
129 /**
130 * Sets the minimum diameter for a valid bullseye detection.
131 * Bullseyes with a diameter smaller than this value will be rejected.
132 * @param minimumDiameter The minimum diameter in pixels, with range [1, infinity)
133 */
134 void setMinimumDiameter(unsigned int minimumDiameter) noexcept;
135
136 /**
137 * Returns the default parameters for the detector.
138 * @return The default parameters
139 */
140 static Parameters defaultParameters() noexcept;
141
142 protected:
143
144 /// The pixel threshold for frame pyramid creation, with range [0, infinity)
145 unsigned int framePyramidPixelThreshold_ = 640u * 480u;
146
147 /// The number of layers for the frame pyramid, with range [1, infinity)
148 unsigned int framePyramidLayers_ = 3u;
149
150 /// Determines whether adaptive row spacing is used (true) or every row is scanned (false) during bullseye detection
151 bool useAdaptiveRowSpacing_ = true;
152
153 /// The minimum diameter in pixels for a valid bullseye detection, with range [1, infinity)
154 unsigned int minimumDiameter_ = 15u;
155 };
156
157 public:
158
159 /**
160 * Detects bullseyes in a given 8-bit grayscale image.
161 * @param yFrame The 8-bit grayscale frame in which the bullseyes will be detected, with origin in the upper left corner, must be valid
162 * @param bullseyes The resulting detected bullseyes, will be appended to the end of the vector
163 * @param parameters The parameters for the detector, must be valid
164 * @param worker Optional worker to distribute the computation
165 * @return True, if succeeded
166 */
167 static bool detectBullseyes(const Frame& yFrame, Bullseyes& bullseyes, const Parameters& parameters = Parameters::defaultParameters(), Worker* worker = nullptr);
168
169 protected:
170
171 /**
172 * Detects bullseyes in a subset of a given 8-bit grayscale image.
173 * @param yFrame The 8-bit grayscale frame in which the bullseyes will be detected, must be valid
174 * @param bullseyes The resulting bullseyes, will be added to the end of the vector
175 * @param multiThreadLock Lock object in case this function is executed in multiple threads concurrently, otherwise nullptr
176 * @param useAdaptiveRowSpacing True to use adaptive row spacing, false to scan every row
177 * @param pyramidLayer The pyramid layer at which this detection is performed, with range [0, infinity)
178 * @param firstRow The first row to be handled, with range [0, yFrame.height())
179 * @param numberRows The number of rows to be handled, with range [1, yFrame.height() - firstRow]
180 */
181 static void detectBullseyesSubset(const Frame* yFrame, Bullseyes* bullseyes, Lock* multiThreadLock, const bool useAdaptiveRowSpacing, const unsigned int pyramidLayer, const unsigned int firstRow, const unsigned int numberRows);
182
183 /**
184 * Detects bullseyes in a row of a grayscale image.
185 * @param yFrame The 8-bit grayscale frame in which the bullseyes will be detected, must be valid
186 * @param y The index of the row in which the bullseyes will be detected, with range [0, yFrame.height())
187 * @param bullseyes The resulting detected bullseyes, will be added to the end of the vector
188 * @param pyramidLayer The pyramid layer at which this detection is performed, with range [0, infinity)
189 */
190 static void detectBullseyesInRow(const Frame& yFrame, const unsigned int y, Bullseyes& bullseyes, const unsigned int pyramidLayer = 0u);
191
192 /**
193 * Finds either the next black or the next white pixel towards negative y direction (upwards in an image).
194 * @param yFrame The 8-bit grayscale frame in which the pixel will be searched, must be valid
195 * @param x The horizontal location within the frame at which the search will be performed, in pixels, with range [0, yFrame.width())
196 * @param y The current vertical location within the image, with range [0, yFrame.height())
197 * @param maximalRows The maximal number of rows (vertical pixels) to travel, with range [1, y]
198 * @param threshold The threshold separating a bright pixel from a dark pixel, with range [0, 255]
199 * @param rows The resulting number of rows that have been traveled until the black or white pixel has been found
200 * @return True, if the black or white pixel has been found within the specified range of [1, maximalRows]
201 * @tparam tFindBlackPixel True, to find the next black pixel; False, to find the next white pixel
202 */
203 template <bool tFindBlackPixel>
204 static bool findNextUpperPixel(const Frame& yFrame, const unsigned int x, const unsigned int y, const unsigned int maximalRows, const unsigned int threshold, unsigned int& rows);
205
206 /**
207 * Finds either the next black or the next white pixel towards positive y direction (downwards in an image).
208 * @param yFrame The 8-bit grayscale frame in which the pixel will be searched, must be valid
209 * @param x The horizontal location within the frame at which the search will be performed, in pixels, with range [0, yFrame.width())
210 * @param y The current vertical location within the image, with range [0, yFrame.height())
211 * @param maximalRows The maximal number of rows (vertical pixels) to travel, with range [1, yFrame.height() - y]
212 * @param threshold The threshold separating a bright pixel from a dark pixel, with range [0, 255]
213 * @param rows The resulting number of rows that have been traveled until the black or white pixel has been found
214 * @return True, if the black or white pixel has been found within the specified range of [1, maximalRows]
215 * @tparam tFindBlackPixel True, to find the next black pixel; False, to find the next white pixel
216 */
217 template <bool tFindBlackPixel>
218 static bool findNextLowerPixel(const Frame& yFrame, const unsigned int x, const unsigned int y, const unsigned int maximalRows, const unsigned int threshold, unsigned int& rows);
219
220 /**
221 * Determines the gray threshold separating bright pixels from dark pixels.
222 * The threshold is based on actual pixel values for which the association is known already.
223 * The provided position is a pointer to any pixel within the image frame.
224 * In addition to the pixels covered by the five segments, pixels neighboring the segments are also used for estimation of the threshold.
225 * @param yPosition The first pixel within an 8-bit grayscale image for which 5 connected segments are known with black, white, black, white, and black pixels, must be valid
226 * @param segmentSize1 The number of pixels covering dark pixels, with range [1, infinity)
227 * @param segmentSize2 The number of pixels covering bright pixels, with range [1, infinity)
228 * @param segmentSize3 The number of pixels covering dark pixels, with range [1, infinity)
229 * @param segmentSize4 The number of pixels covering bright pixels, with range [1, infinity)
230 * @param segmentSize5 The number of pixels covering dark pixels, with range [1, infinity)
231 * @return The threshold separating bright pixels from dark pixels, with range [0, 255], -1 if no valid threshold could be determined
232 */
233 static unsigned int determineThreshold(const uint8_t* yPosition, const unsigned int segmentSize1, const unsigned int segmentSize2, const unsigned int segmentSize3, const unsigned int segmentSize4, const unsigned int segmentSize5);
234
235 /**
236 * Checks whether a column contains a bullseye at a specified location.
237 * This function is simply checking for the same bullseye pattern in vertical direction (within a small window).
238 * @param yFrame The 8-bit grayscale frame in which the bullseyes will be detected, must be valid
239 * @param xCenter The horizontal location within the frame at which the existence of the bullseye will be checked, in pixels, with range [0, yFrame.width())
240 * @param yCenter The vertical location within the frame at which the existence of the bullseye will be checked, in pixels, with range [0, yFrame.height())
241 * @param threshold The grayscale threshold separating a bright pixel from a dark pixel, with range [0, 255]
242 * @param blackRingSegmentMin The minimal size (thickness) of the black ring, in pixels, with range [1, infinity)
243 * @param blackRingSegmentMax The maximal size (thickness) of the black ring, in pixels, with range [blackRingSegmentMin, infinity)
244 * @param whiteRingSegmentMin The minimal size (thickness) of the white ring, in pixels, with range [1, infinity)
245 * @param whiteRingSegmentMax The maximal size (thickness) of the white ring, in pixels, with range [whiteRingSegmentMin, infinity)
246 * @param dotSegmentMin The minimal size (thickness) of the black dot, in pixels, with range [1, infinity)
247 * @param dotSegmentMax The maximal size (thickness) of the black dot, in pixels, with range [dotSegmentMin, infinity)
248 * @return True, if the column contains a bullseye at the specified location
249 */
250 static bool checkBullseyeInColumn(const Frame& yFrame, const unsigned int xCenter, const unsigned int yCenter, const unsigned int threshold, const unsigned int blackRingSegmentMin, const unsigned int blackRingSegmentMax, const unsigned int whiteRingSegmentMin, const unsigned int whiteRingSegmentMax, const unsigned int dotSegmentMin, const unsigned int dotSegmentMax);
251
252 /**
253 * Checks whether the direct neighborhood contains a bullseye at a specified location.
254 * This function actually samples the neighborhood at sparse locations only instead of applying a dense check for the bullseye pattern.
255 * @param yFrame The 8-bit grayscale frame in which the bullseyes will be detected, must be valid
256 * @param xCenter The horizontal location within the frame at which the existence of the bullseye will be checked, in pixels, with range [0, yFrame.width())
257 * @param yCenter The vertical location within the frame at which the existence of the bullseye will be checked, in pixels, with range [0, yFrame.height())
258 * @param threshold The grayscale threshold separating a bright pixel from a dark pixel, with range [0, 255]
259 * @param whiteRingRadius The radius of the white ring (the center of this ring), in pixels, with range [1, infinity)
260 * @param blackRingRadius The radius of the black ring (the center of this ring), in pixels, with range [whiteRingRadius + 1, infinity)
261 * @param whiteBorderRadius The radius of the white border (the outer area around the black ring), in pixels, with range [blackRingRadius + 1, infinity)
262 * @return True, if the neighborhood contains a bullseye at the specified location
263 */
264 static bool checkBullseyeInNeighborhood(const Frame& yFrame, const unsigned int xCenter, const unsigned int yCenter, const unsigned int threshold, const float whiteRingRadius, const float blackRingRadius, const float whiteBorderRadius);
265
266 /**
267 * Determines the sub-pixel location of the center dot of a known bullseye.
268 * @param yFrame The 8-bit grayscale frame in which the bullseye is located, must be valid
269 * @param xBullseye The horizontal location of the bullseye (the center location), the pixel must be black, with range [0, yFrame.width())
270 * @param yBullseye The vertical location of the bullseye (the center location), the pixel must be black, with range [0, yFrame.height())
271 * @param threshold The threshold separating a bright pixel from a dark pixel, with range [0, 255]
272 * @param location The resulting sub-pixel location of the center of the bullseye
273 * @return True, if the sub-pixel location could be determined
274 */
275 static bool determineAccurateBullseyeLocation(const Frame& yFrame, const unsigned int xBullseye, const unsigned int yBullseye, const unsigned int threshold, Vector2& location);
276};
277
278} // namespace Bullseyes
279
280} // namespace Detector
281
282} // namespace CV
283
284} // namespace Ocean
285
286#endif // OCEAN_CV_DETECTOR_BULLSEYES_BULLSEYEDETECTORMONO_H
This class holds the most important parameters for the detector.
Definition BullseyeDetectorMono.h:71
bool isValid() const noexcept
Returns whether the parameters are valid.
Parameters()=default
Creates a new valid parameter object.
Implementation of a monocular detector for the bullseye pattern.
Definition BullseyeDetectorMono.h:36
This class implements Ocean's image class.
Definition Frame.h:1879
This class implements a recursive lock object.
Definition Lock.h:31
This class implements a worker able to distribute function calls over different threads.
Definition Worker.h:33
std::vector< Bullseye > Bullseyes
Definition of a vector holding bullseyes.
Definition Bullseye.h:113
The namespace covering the entire Ocean framework.
Definition Accessor.h:15