Ocean
Loading...
Searching...
No Matches
MessengerCodeDetector.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_DETECTOR_MESSENGER_CODE_DETECTOR_H
9#define META_OCEAN_CV_DETECTOR_MESSENGER_CODE_DETECTOR_H
10
12
13#include "ocean/base/Memory.h"
14
16
17#include "ocean/math/Box2.h"
19#include "ocean/math/Vector2.h"
20
21#include <array>
22#include <bitset>
23#include <unordered_set>
24
25namespace Ocean
26{
27
28namespace CV
29{
30
31namespace Detector
32{
33
34/**
35 * This class implements a detector for circular Messenger Codes.
36 * @ingroup cvdetector
37 */
38class OCEAN_CV_DETECTOR_EXPORT MessengerCodeDetector
39{
40 friend class QRCodeDetector;
41
42 public:
43
44 /**
45 * Definition of the number of bits the Messenger Code provides.
46 */
47 static constexpr size_t numberCodeBits = 260;
48
49 /**
50 * Definition of a bitset containing the information of a Messenger Code
51 */
52 typedef std::bitset<numberCodeBits> CodeBits;
53
54 /**
55 * Definition of a vector holding codes.
56 */
57 typedef std::vector<CodeBits> Codes;
58
59 /**
60 * Definition of an index quartet (an array with exactly four indices).
61 */
62 typedef std::array<unsigned int, 4> IndexQuartet;
63
64 /**
65 * Definition of a vector holding index quartets
66 */
67 typedef std::vector<IndexQuartet> IndexQuartets;
68
69 /**
70 * Definition of a class holding a bullseye composed of a location and a radius.
71 */
73 {
74 public:
75
76 /**
77 * Creates an invalid bullseye object.
78 */
79 inline Bullseye();
80
81 /**
82 * Creates a new bullseye object by a given position and radius.
83 * @param position The (center) position of the bullseye within the camera frame
84 * @param radius The radius of the bullseye in pixels, with range (0, infinity)
85 * @param grayThreshold Threshold that was used during the detection, range [0, 255]
86 */
87 inline Bullseye(const Vector2& position, const Scalar& radius, const unsigned int grayThreshold);
88
89 /**
90 * Returns the (center) position of the bullseye.
91 * @return The Bullseye's position within the camera frame
92 */
93 inline const Vector2& position() const;
94
95 /**
96 * Returns the radius of the bullseye.
97 * @return The Bullseye's radius, with range (0, infinity), 0 for an invalid object
98 */
99 inline Scalar radius() const;
100
101 /**
102 * Returns the threshold that was used for the detection of this bullseye
103 * @return The threshold value
104 */
105 inline unsigned int grayThreshold() const;
106
107 protected:
108
109 /// The (center) position of the bullseye within the camera frame.
111
112 /// The radius of the bullseye in pixels, with range (0, infinity).
114
115 /// The threshold that was used during the detection of this bullseye
116 unsigned int grayThreshold_;
117 };
118
119 /**
120 * Definition of a vector holding bullseyes.
121 */
122 typedef std::vector<Bullseye> Bullseyes;
123
124 /**
125 * Data storing debug information.
126 */
127 class OCEAN_CV_DETECTOR_EXPORT DebugInformation
128 {
129 public:
130
131 /**
132 * Data of one detected code.
133 */
135 {
136 /// The four bullseyes
138
139 /// Location of the bits in frame coordinates
141
142 /// Extracted code bits
144 };
145
146 /**
147 * Definition of a vector holding DetectedCode object.
148 */
149 typedef std::vector<DetectedCode> DetectedCodes;
150
151 public:
152
153 /**
154 * Draws the entire debug information into a given frame.
155 * @param frame The frame in which the debug information will be painted, must be valid
156 */
157 void draw(Frame& frame) const;
158
159 /**
160 * Draws the location of a bullseye into a given frame.
161 * @param frame The frame in which the bullseye will be painted, must be valid
162 * @param bullseye The bullseye to be painted
163 * @param color The color to be used, one value for each frame channel, must be valid
164 */
165 static void drawBullseye(Frame& frame, const Bullseye& bullseye, const uint8_t* color);
166
167 public:
168
169 /// Contains information about all detected codes
171
172 /// Contains all detected bullseyes
174 };
175
176 protected:
177
178 /// The intensity threshold between two successive pixels to count as a transition from black to white (or vice versa).
179 static constexpr int deltaThreshold = 20;
180
181 /**
182 * This class implements a simple history for previous pixel transitions (a sliding window of pixel transitions).
183 */
185 {
186 public:
187
188 /**
189 * Creates a new history object.
190 */
191 inline TransitionHistory();
192
193 /**
194 * Returns the history with window size 1.
195 * @return The previous delta
196 */
197 inline int history1();
198
199 /**
200 * Returns the history with window size 2.
201 * @return The sum of the previous two deltas
202 */
203 inline int history2();
204
205 /**
206 * Returns the history with window size 3.
207 * @return The sum of the previous three deltas
208 */
209 inline int history3();
210
211 /**
212 * Adds a new delta object as most recent history.
213 * Existing history objects will be moved by one pixel.
214 * @param newDelta The new delta object to be added
215 */
216 inline void push(const int newDelta);
217
218 /**
219 * Resets the history object.
220 */
221 inline void reset();
222
223 protected:
224
225 /// The previous delta.
227
228 /// The second previous delta.
230
231 /// The third previous delta.
233 };
234
235 public:
236
237 /**
238 * Detects Messenger Codes in a given 8 bit grayscale image.
239 * @param yFrame The 8 bit grayscale frame in which the Messenger code will be detected, with origin in the upper left corner, must be valid
240 * @param width The width of the given grayscale frame in pixel, with range [21, infinity)
241 * @param height The height of the given grayscale frame in pixel, with range [21, infinity)
242 * @param paddingElements Optional number of padding elements at the end of each image row, in elements, with range [0, infinity)
243 * @param worker Optional worker to distribute the computation
244 * @return The detected messenger codes
245 */
246 static Codes detectMessengerCodes(const uint8_t* const yFrame, const unsigned int width, const unsigned int height, const unsigned int paddingElements, Worker* worker = nullptr);
247
248 /**
249 * Detects Messenger Codes in a given 8 bit grayscale image and returns debug information.
250 * @param yFrame The 8 bit grayscale frame in which the Messenger code will be detected, with origin in the upper left corner, must be valid
251 * @param width The width of the given grayscale frame in pixel, with range [21, infinity)
252 * @param height The height of the given grayscale frame in pixel, with range [21, infinity)
253 * @param debugInformation The resulting debug information for the given frame
254 * @param yFramePaddingElements The number of padding elements at the end of each image row, in elements, with range [0, infinity)
255 * @param worker Optional worker to distribute the computation
256 * @return The detected messenger codes
257 */
258 static Codes detectMessengerCodesWithDebugInformation(const uint8_t* const yFrame, const unsigned int width, const unsigned int height, DebugInformation& debugInformation, const unsigned int yFramePaddingElements, Worker* worker = nullptr);
259
260 protected:
261
262 /**
263 * Detects Messenger Codes in a given 8 bit grayscale image.
264 * @param yFrame The 8 bit grayscale frame in which the Messenger code will be detected, must be valid
265 * @param width The width of the given grayscale frame in pixel, with range [21, infinity)
266 * @param height The height of the given grayscale frame in pixel, with range [21, infinity)
267 * @param yFramePaddingElements The number of padding elements at the end of each image row, in elements, with range [0, infinity)
268 * @param debugInformation If specified, debug information will be stored here otherwise it will be ignored (note: `tCreateDebugInformation` must be set to `true` as well)
269 * @param worker Optional worker to distribute the computation
270 * @return The detected messenger codes
271 * @tparam tCreateDebugInformation If true, debug information will be created and returned in 'debugInformation' (note: `debugInformation` must be specified as well)
272 */
273 template <bool tCreateDebugInformation>
274 static Codes detectMessengerCodes(const uint8_t* const yFrame, const unsigned int width, const unsigned int height, const unsigned int yFramePaddingElements, DebugInformation* debugInformation = nullptr, Worker* worker = nullptr);
275
276 /**
277 * Detects Messenger Code bullseyes in a given 8 bit grayscale image.
278 * @param yFrame The 8 bit grayscale frame in which the bullseyes will be detected, must be valid
279 * @param width The width of the given grayscale frame in pixel, with range [21, infinity)
280 * @param height The height of the given grayscale frame in pixel, with range [21, infinity)
281 * @param yFramePaddingElements The number of padding elements at the end of each image row, in elements, with range [0, infinity)
282 * @param worker Optional worker to distribute the computation
283 * @return The detected bullseyes
284 */
285 static inline Bullseyes detectBullseyes(const uint8_t* const yFrame, const unsigned int width, const unsigned int height, const unsigned int yFramePaddingElements, Worker* worker = nullptr);
286
287 /**
288 * Detects Messenger Code bullseyes in subset of a given 8 bit grayscale image.
289 * @param yFrame The 8 bit grayscale frame in which the bullseyes will be detected, must be valid
290 * @param width The width of the given grayscale frame in pixel, with range [21, infinity)
291 * @param height The height of the given grayscale frame in pixel, with range [21, infinity)
292 * @param bullseyes The resulting bullseyes, will be added to the end of the vector
293 * @param multiThreadLock Lock object in case this function is executed in multiple threads concurrently, otherwise nullptr
294 * @param yFramePaddingElements The number of padding elements at the end of each image row, in elements, with range [0, infinity)
295 * @param firstRow The first row to be handled, with range [10, height - 10)
296 * @param numberRows The number of rows to be handled, with range [1, height - 10 - firstRow]
297 */
298 static void detectBullseyesSubset(const uint8_t* const yFrame, const unsigned int width, const unsigned int height, Bullseyes* bullseyes, Lock* multiThreadLock, const unsigned int yFramePaddingElements, const unsigned int firstRow, const unsigned int numberRows);
299
300 /**
301 * Extracts quartets of code bullseyes from a given set of bullseyes.
302 * The indices of a resulting quartet provide the bullseyes in a counter-clock-wise order.
303 * Based on the given bullseyes, any combination of four bullseyes is returned which potentially can define a Messenger Code.
304 * @param bullseyes The center locations of the bullseyes that have been detected in one image, must be valid
305 * @param radii The radii of each individual bullseye, one for each bullseye, in pixel, must be valid
306 * @param size The number of given bullseyes, with range [4, infinity)
307 * @param radiusScaleTolerance The tolerance between radii of matching bullseyes in percent, with range [0, 0.5]
308 * @param distanceScaleTolerance The tolerance between radii of matching bullseyes in percent, with range [0, 3 - (2 * sqrt(2))]
309 * @return The quartets of indices of bullseyes valid candidates for a code
310 */
311 static IndexQuartets extractCodeCandidates(const Vector2* bullseyes, const Scalar* radii, const size_t size, const Scalar radiusScaleTolerance = Scalar(0.35), const Scalar distanceScaleTolerance = Scalar(0.17));
312
313 /**
314 * Calculates the homography rectifying the image content covered (and defined) by four bullseyes.
315 * The orientation of the resulting homography is undefined (in the rectified image, the Messenger Icon can be in any of the four quadrants).
316 * @param bullseyes The four bullseyes for which the homography will be determined, defined in counter-clockwise order, must provide four valid and individual locations of bullseyes, must be valid
317 * @param homography The resulting homography transforming image points defined in the rectified image to image points defined in the camera image: bullseyePoint = homography * rectifiedBullseyePoint
318 * @param codeSize The resulting size of the Messenger Code - the width (and height) of the rectified (sub-)frame containing the Messenger Code only, in pixel, with range (0, infinity)
319 * @return True, if the homography could be calculated
320 * @see correctRotation().
321 */
322 static bool determineHomographyForBullseyeQuartet(const Vector2* bullseyes, SquareMatrix3& homography, Scalar& codeSize);
323
324 /**
325 * Corrects the orientation of a given homography already rectifying the content of a Messenger Code.
326 * The homography will be rotated around the center point of the rectified sub-image so that the Messenger Icon is located in the lower right quadrant of the Messenger Code.<br>
327 * Depending on the input homography, this function either applies a 0-degree, 90-degree, 180-degree, or 270-degree orientation.
328 * @param yFrame The 8 bit gray image in which the Messenger Code is located, must be valid
329 * @param width The width of the given image in pixel, with range [1, infinity)
330 * @param height The height of the given image in pixel, with range [1, infinity)
331 * @param bullseyes The four bullseyes defining the Messenger Code, with counter-clock-wise order, all located inside the given image, must provide four valid and individual locations of bullseyes, must be valid
332 * @param codeSize The size of the rectified image containing the Messenger Code, in pixel, with range (0, infinity)
333 * @param homography The already known homography rectifying the image content, which will be adjusted if necessary, with: bullseyePoint = homography * rectifiedBullseyePoint, must not be singular
334 * @param yFramePaddingElements The number of padding elements at the end of each image row, in elements, with range [0, infinity)
335 * @return True, if succeeded
336 * @see determineHomographyForBullseyeQuartet().
337 */
338 static bool correctRotation(const uint8_t* yFrame, const unsigned int width, const unsigned int height, const Vector2* bullseyes, const Scalar codeSize, SquareMatrix3& homography, const unsigned int yFramePaddingElements);
339
340 /**
341 * Returns whether a Messenger Code, defined by a rectifying homography and code size, is entirely visible in a camera frame.
342 * The bullseyes of a Messenger Code are slightly inside the Code so that an extract border is necessary to see the entire Code.
343 * @param width The width of the camera frame in pixel, with range [1, infinity)
344 * @param height The height of the camera frame in pixel, with range [1, infinity)
345 * @param homography The homography rectifying the image content of the Messenger Code, with transformation: bullseyePoint = homography * rectifiedBullseyePoint, must not be singular
346 * @param codeSize The size of the Messenger Code - the width (and height) of the rectified (sub-)frame containing the Messenger Code only, in pixel, with range (0, infinity)
347 * @return True, if the entire information (e.g., all bit rings) is visible in the camera frame
348 */
349 static bool isCodeInsideFrame(const unsigned int width, const unsigned int height, const SquareMatrix3& homography, const Scalar codeSize);
350
351 /**
352 * Extracts the Messenger Code's bit information.
353 * @param yFrame The 8 bit gray image in which the Messenger Code is located, must be valid
354 * @param width The width of the given image in pixel, with range [1, infinity)
355 * @param height The height of the given image in pixel, with range [1, infinity)
356 * @param codeSize The size of the Messenger Code - the width (and height) of the rectified (sub-)frame containing the Messenger Code only, in pixel, with range (0, infinity)
357 * @param homography The homography rectifying the image content of the Messenger Code, with transformation: bullseyePoint = homography * rectifiedBullseyePoint, must not be singular
358 * @param codeBits The resulting extracted bits of the Messenger Code
359 * @param grayThreshold Threshold that is used to determine if a bit is 0 or 1, range: [0, 255]
360 * @param yFramePaddingElements The number of padding elements at the end of each image row, in elements, with range [0, infinity)
361 * @param codeBitsLocationFrame If specified, the locations in frame coordinates of the code bits are written to this vector, otherwise it will be ignored (note: `tCreateDebugInformation` must be set to `true` as well)
362 * @return True, if succeeded
363 * @tparam tCreateDebugInformation If true, debug information will be created and returned in 'debugInformation' (note: `codeBitsLocationFrame` must be specified as well)
364 */
365 template <bool tCreateDebugInformation>
366 static bool extractCodeBits(const uint8_t* yFrame, const unsigned int width, const unsigned int height, const Scalar codeSize, const SquareMatrix3& homography, CodeBits& codeBits, const unsigned int grayThreshold, const unsigned int yFramePaddingElements, Vectors2* codeBitsLocationFrame = nullptr);
367
368 /**
369 * Detects Messenger Code bullseyes in a row of an grayscale image.
370 * @param yFrame The 8 bit grayscale frame in which the bullseyes will be detected, must be valid
371 * @param width The width of the given grayscale frame in pixel, with range [21, infinity)
372 * @param height The height of the given grayscale frame in pixel, with range [21, infinity)
373 * @param y The index of the row in which the bullseyes will be detected, with range [10, height - 11]
374 * @param bullseyes The resulting detected bullseyes, will be added to the end of the vector
375 * @param yFramePaddingElements The number of padding elements at the end of each image row, in elements, with range [0, infinity)
376 */
377 static void detectBullseyesInRow(const uint8_t* const yFrame, const unsigned int width, const unsigned int height, const unsigned int y, Bullseyes& bullseyes, const unsigned int yFramePaddingElements);
378
379 /**
380 * Checks whether a column contains a bullseye at a specified location.
381 * This function is simply checking for the same bullseye pattern in vertical direction (within a small window).
382 * @param yFrame The 8 bit grayscale frame in which the bullseyes will be detected, must be valid
383 * @param frameStrideElements The number of stride elements in the given image (width + padding elements), in elements, with range [width, infinity)
384 * @param height The height of the given grayscale frame in pixel, with range [21, infinity)
385 * @param xCenter The horizontal location within the frame at which the existence of the bullseye will be checked, in pixels, with range [0, width - 1]
386 * @param yCenter The vertical location within the frame at which the existence of the bullseye will be checked, in pixels, with range [0, height - 1]
387 * @param threshold The grayscale threshold separating a bright pixel from a dark pixel, with range [0, 255]
388 * @param blackRingSegmentMin The minimal size (thickness) of the black ring, in pixel, with range [1, infinity)
389 * @param blackRingSegmentMax The maximal size (thickness) of the black ring, in pixel, with range [blackRingSegmentMin, infinity)
390 * @param whiteRingSegmentMin The minimal size (thickness) of the white ring, in pixel, with range [1, infinity)
391 * @param whiteRingSegmentMax The maximal size (thickness) of the white ring, in pixel, with range [whiteRingSegmentMin, infinity)
392 * @param dotSegmentMin The minimal size (thickness) of the black dot, in pixel, with range [1, infinity)
393 * @param dotSegmentMax The maximal size (thickness) of the black dot, in pixel, with range [dotSegmentMin, infinity)
394 * @return True, if the column contains a bullseye at the specified location
395 */
396 static bool checkBullseyeInColumn(const uint8_t* const yFrame, const unsigned int frameStrideElements, const unsigned int height, 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);
397
398 /**
399 * Checks whether the direction neighborhood contains a bullseye at a specified location.
400 * This function actually samples the neighborhood at sparse locations only instead of applying a dense check for the bullseye pattern.
401 * @param yFrame The 8 bit grayscale frame in which the bullseyes will be detected, must be valid
402 * @param width The width oft he given frame in pixels, with range [21, infinity)
403 * @param height The height of the given frame in pixels, with range [21, infinity)
404 * @param xCenter The horizontal location within the frame at which the existence of the bullseye will be checked, in pixels, with range [0, width - 1]
405 * @param yCenter The vertical location within the frame at which the existence of the bullseye will be checked, in pixels, with range [0, height - 1]
406 * @param threshold The grayscale threshold separating a bright pixel from a dark pixel, with range [0, 255]
407 * @param whiteRingRadius The radius of the white ring (the center of this ring), in pixel, with range [1, infinity)
408 * @param blackRingRadius The radius of the black ring (the center of this ring), in pixel, with range [whiteRingRadius + 1, infinity)
409 * @param whiteBorderRadius The radius of the white border (the outer area around the black ring), in pixel, with range [blackRingRadius + 1u, infinity)
410 * @param yFramePaddingElements The number of padding elements at the end of each image row, in elements, with range [0, infinity)
411 * @return True, if the neighborhood contains a bullseye at the specified location
412 */
413 static bool checkBullseyeInNeighborhood(const uint8_t* const yFrame, const unsigned int width, const unsigned int height, const unsigned int xCenter, const unsigned int yCenter, const unsigned int threshold, const float whiteRingRadius, const float blackRingRadius, const float whiteBorderRadius, const unsigned int yFramePaddingElements);
414
415 /**
416 * Determines the gray threshold separating bright pixels form dark pixels.
417 * The threshold is based on already actual pixel values for which the association is known already.<br>
418 * The provided start position is a pointer to any pixel within the image, with horizontal range [1, width - segmentSize1 - segmentSize2 - segmentSize3 - segmentSize4 - segmentSize5 - 2].
419 * In addition to the pixels covered by the five segments, the fist pixel left of the segments and the last pixel right of the segments are also used for estimation of the threshold.
420 * @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
421 * @param segmentSize1 The number of pixels covering dark pixels, with range [1, width - ...)
422 * @param segmentSize2 The number of pixels covering bright pixels, with range [1, width - ...)
423 * @param segmentSize3 The number of pixels covering dark pixels, with range [1, width - ...)
424 * @param segmentSize4 The number of pixels covering bright pixels, with range [1, width - ...)
425 * @param segmentSize5 The number of pixels covering dark pixels, with range [1, width - segmentSize1 - segmentSize2 - segmentSize3 - segmentSize4 - 2]
426 * @return The threshold separating bright pixels from dark pixels, with range [0, 255], -1 if no valid threshold could be determined
427 */
428 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);
429
430 /**
431 * Checks whether the given pixel is a transition-to-black pixel (whether the direct left neighbor is a bright pixel).
432 * @param pixel The pixel to be checked, must be valid
433 * @param history The history object containing information about previous pixels
434 * @return True, if so
435 */
436 static inline bool isTransitionToBlack(const uint8_t* pixel, TransitionHistory& history);
437
438 /**
439 * Checks whether the given pixel is a transition-to-white pixel (whether the direct left neighbor is a dark pixel).
440 * @param pixel The pixel to be checked, must be valid
441 * @param history The history object containing information about previous pixels
442 * @return True, if so
443 */
444 static inline bool isTransitionToWhite(const uint8_t* pixel, TransitionHistory& history);
445
446 /**
447 * Finds either the next black or the next white pixel towards negative y direction (upwards in an image).
448 * @param yPointer The pointer to the location within the image at which the search will start, must be valid
449 * @param y The current vertical location within the image, with range [1, height - 1]
450 * @param maximalRows The maximal number of rows (vertical pixels) to travel, with range [1, height - y]
451 * @param threshold The threshold separating a bright pixel from a dark pixel, with range [0, 255]
452 * @param frameStrideElements The number of horizontal stride elements in the image, which is width + paddingElements, with range [width, infinity)
453 * @param rows The resulting number of rows that have been traveled until the black or white pixel has been found
454 * @return True, if the black or white pixel have been found within the specified range of [1, maximalRows]
455 * @tparam tFindBlackPixel True, to find the next black pixel; False, to find the next white pixel
456 */
457 template <bool tFindBlackPixel>
458 static bool findNextUpperPixel(const uint8_t* yPointer, const unsigned int y, const unsigned int maximalRows, const unsigned int threshold, const unsigned int frameStrideElements, unsigned int& rows);
459
460 /**
461 * Finds either the next black or the next white pixel towards positive y direction (downwards in an image).
462 * @param yPointer The pointer to the location within the image at which the search will start, must be valid
463 * @param y The current vertical location within the image, with range [1, height - 1]
464 * @param height The height of the frame in pixel, with range [1, infinity)
465 * @param maximalRows The maximal number of rows (vertical pixels) to travel, with range [1, height - y]
466 * @param threshold The threshold separating a bright pixel from a dark pixel, with range [0, 255]
467 * @param frameStrideElements The number of horizontal stride elements in the image, which is width + paddingElements, with range [width, infinity)
468 * @param rows The resulting number of rows that have been traveled until the black or white pixel has been found
469 * @return True, if the black or white pixel have been found within the specified range of [1, maximalRows]
470 * @tparam tFindBlackPixel True, to find the next black pixel; False, to find the next white pixel
471 */
472 template <bool tFindBlackPixel>
473 static bool findNextLowerPixel(const uint8_t* yPointer, const unsigned int y, const unsigned int height, const unsigned int maximalRows, const unsigned int threshold, const unsigned int frameStrideElements, unsigned int& rows);
474
475 /**
476 * Determines the sub-pixel location of the center dot of a known bullseye.
477 * @param yFrame The 8 bit grayscale frame in which the bullseyes will be detected, must be valid
478 * @param width The width of the given frame in pixel, with range [21, infinity)
479 * @param height The height of the given frame in pixel, with range [21, infinity)
480 * @param xBullseye The horizontal location of the bullseye (the center location), the pixel must be black, with range [0, width - 1]
481 * @param yBullseye The vertical location of the bullseye (the center location), the pixel must be black, with range [0, height - 1]
482 * @param threshold The threshold separating a bright pixel from a dark pixel, with range [0, 255]
483 * @param location The resulting sub-pixel location of the center of the bullseye
484 * @param framePaddingElements The number of padding elements at the end of each image row, in elements, with range [0, infinity)
485 * @return True, if the sub-pixel location could be determined
486 */
487 static bool determineAccurateBullseyeLocation(const uint8_t* yFrame, const unsigned int width, const unsigned int height, const unsigned int xBullseye, const unsigned int yBullseye, const unsigned int threshold, Vector2& location, const unsigned int framePaddingElements);
488
489 /**
490 * Returns the short distance between two bullseyes of the same Messenger Code (for neighboring bullseyes) based on the radius of a bullseye.
491 * @param radius The radius of the bullseye for which the corresponding distance will be calculated, with range (0, infinity)
492 * @return The distance between two neighboring bullseyes, with range (0, infinity)
493 */
494 static inline Scalar radius2bullseyesDistanceShort(const Scalar radius);
495
496 /**
497 * Returns the long distance between two bullseyes of the same Messenger Code (for opposite bullseyes) based on the radius of a bullseye.
498 * @param radius The radius of the bullseye for which the corresponding distance will be calculated, with range (0, infinity)
499 * @return The distance between opposite bullseyes, with range (0, infinity)
500 */
501 static inline Scalar radius2bullseyesDistanceLong(const Scalar radius);
502
503 /**
504 * Returns the reference to 260 coordinates of the Messenger Code's bit elements origin in the center of the Code and normalized to a radius of 1.
505 * @return The coordinates at which the Code's bit elements are located
506 */
508
509 /**
510 * Returns 260 coordinates of the Messenger Code's bit elements origin in the center of the Code and normalized to a radius of 1.
511 * @return The coordinates at which the Code's bit elements are located
512 */
514
515 /**
516 * Calculates the bit coordinates of a ring of the Messenger Code.
517 * @param bits The number of bit for which the coordinates will be calculated, with range [1, infinity)
518 * @param bitsToSkip The indices of all bits for which no coordinate will be calculated, with range [0, bits - 1]
519 * @param radius The radius of the ring with range (0, infinity)
520 * @param coordinates The resulting coordinates, will be added to the end of the vector
521 */
522 static void calculateRingBitCoordinates(const unsigned int bits, const IndexSet32& bitsToSkip, const Scalar radius, Vectors2& coordinates);
523};
524
526 position_(-1, -1),
527 radius_(0),
528 grayThreshold_(0u)
529{
530 // nothing to do here
531}
532
533inline MessengerCodeDetector::Bullseye::Bullseye(const Vector2& position, const Scalar& radius, const unsigned int grayThreshold) :
534 position_(position),
535 radius_(radius),
536 grayThreshold_(grayThreshold)
537{
538 // nothing to do here
539}
540
542{
543 return position_;
544}
545
547{
548 return radius_;
549}
550
552{
553 return grayThreshold_;
554}
555
557 deltaMinus1(0),
558 deltaMinus2(0),
559 deltaMinus3(0)
560{
561 // nothing to do here
562}
563
565{
566 return deltaMinus1;
567}
568
570{
571 return deltaMinus1 + deltaMinus2;
572}
573
575{
576 return deltaMinus1 + deltaMinus2 + deltaMinus3;
577}
578
580{
581 deltaMinus3 = deltaMinus2;
582 deltaMinus2 = deltaMinus1;
583 deltaMinus1 = newDelta;
584}
585
587{
588 deltaMinus1 = 0;
589 deltaMinus2 = 0;
590 deltaMinus3 = 0;
591}
592
593inline MessengerCodeDetector::Bullseyes MessengerCodeDetector::detectBullseyes(const uint8_t* yFrame, const unsigned int width, const unsigned int height, const unsigned int yFramePaddingElements, Worker* worker)
594{
595 ocean_assert(yFrame != nullptr);
596 ocean_assert(width >= 21u && height >= 21u);
597
598 Bullseyes bullseyes;
599 bullseyes.reserve(16);
600
601 if (worker && height >= 600u)
602 {
603 Lock multiThreadLock;
604 worker->executeFunction(Worker::Function::createStatic(&MessengerCodeDetector::detectBullseyesSubset, yFrame, width, height, &bullseyes, &multiThreadLock, yFramePaddingElements, 0u, 0u), 10u, height - 20u);
605 }
606 else
607 {
608 detectBullseyesSubset(yFrame, width, height, &bullseyes, nullptr, yFramePaddingElements, 10u, height - 20u);
609 }
610
611 return bullseyes;
612}
613
614inline bool MessengerCodeDetector::isTransitionToBlack(const uint8_t* pixel, TransitionHistory& history)
615{
616 const int currentDelta = int(*(pixel + 0) - *(pixel - 1));
617
618 bool result = false;
619
620 if (currentDelta < -deltaThreshold)
621 {
622 result = true;
623 }
624 else if ((currentDelta + history.history1() < -(deltaThreshold * 5 / 4))
625 || (currentDelta + history.history2() < -(deltaThreshold * 3 / 2))
626 || (currentDelta + history.history3() < -(deltaThreshold * 3 / 2)))
627 {
628 result = true;
629 }
630
631 history.push(currentDelta);
632
633 return result;
634}
635
636inline bool MessengerCodeDetector::isTransitionToWhite(const uint8_t* pixel, TransitionHistory& history)
637{
638 const int currentDelta = int(*(pixel + 0) - *(pixel - 1));
639
640 bool result = false;
641
642 if (currentDelta > deltaThreshold)
643 {
644 result = true;
645 }
646 else if ((currentDelta + history.history1() > (deltaThreshold * 5 / 4))
647 || (currentDelta + history.history2() > (deltaThreshold * 3 / 2))
648 || (currentDelta + history.history3() > (deltaThreshold * 3 / 2)))
649 {
650 result = true;
651 }
652
653 history.push(currentDelta);
654
655 return result;
656}
657
658template <bool tFindBlackPixel>
659bool MessengerCodeDetector::findNextUpperPixel(const uint8_t* yPointer, unsigned int y, const unsigned int maximalRows, const unsigned int threshold, const unsigned int frameStrideElements, unsigned int& rows)
660{
661 ocean_assert(yPointer != nullptr);
662 ocean_assert(maximalRows != 0u);
663 ocean_assert(frameStrideElements != 0u);
664
665 if (y == 0u)
666 {
667 return false;
668 }
669
670 rows = 0u;
671
672 while (int(--y) >= 0 && ++rows <= maximalRows && (tFindBlackPixel ? (int(*(yPointer - frameStrideElements)) > int(threshold)) : (int(*(yPointer - frameStrideElements)) < int(threshold))))
673 {
674 yPointer -= frameStrideElements;
675 }
676
677 return int(y) >= 0 && rows <= maximalRows;
678}
679
680template <bool tFindBlackPixel>
681bool MessengerCodeDetector::findNextLowerPixel(const uint8_t* yPointer, unsigned int y, const unsigned int height, const unsigned int maximalRows, const unsigned int threshold, const unsigned int frameStrideElements, unsigned int& rows)
682{
683 ocean_assert(yPointer != nullptr);
684 ocean_assert(maximalRows != 0u);
685 ocean_assert(y < height);
686 ocean_assert(frameStrideElements != 0u);
687
688 if (y >= height - 1u)
689 {
690 return false;
691 }
692
693 rows = 0u;
694
695 while (++y < height && ++rows <= maximalRows && (tFindBlackPixel ? (int(*(yPointer + frameStrideElements)) > int(threshold)) : (int(*(yPointer + frameStrideElements)) < int(threshold))))
696 {
697 yPointer += frameStrideElements;
698 }
699
700 return y < height && rows <= maximalRows;
701}
702
704{
705 /*
706 * example:
707 * bullseyes radius: 27px
708 * bounding box size: 512px
709 * half bounding box size: 256px
710 * short edge (diagonal): sqrt(2) * 256px
711 */
712
713 return Scalar(1.4142135623730950488016887242097) * radius2bullseyesDistanceLong(radius) * Scalar(0.5);
714}
715
717{
718 /*
719 * example:
720 * bullseyes radius: 27px
721 * bounding box size: 512px
722 */
723
724 return radius * Scalar(512.0 / 27.0); // **TODO**
725}
726
727}
728
729}
730
731}
732
733#endif // META_OCEAN_CV_DETECTOR_MESSENGER_CODE_DETECTOR_H
Definition of a class holding a bullseye composed of a location and a radius.
Definition MessengerCodeDetector.h:73
unsigned int grayThreshold_
The threshold that was used during the detection of this bullseye.
Definition MessengerCodeDetector.h:116
const Vector2 & position() const
Returns the (center) position of the bullseye.
Definition MessengerCodeDetector.h:541
Scalar radius_
The radius of the bullseye in pixels, with range (0, infinity).
Definition MessengerCodeDetector.h:113
Bullseye()
Creates an invalid bullseye object.
Definition MessengerCodeDetector.h:525
unsigned int grayThreshold() const
Returns the threshold that was used for the detection of this bullseye.
Definition MessengerCodeDetector.h:551
Vector2 position_
The (center) position of the bullseye within the camera frame.
Definition MessengerCodeDetector.h:110
Scalar radius() const
Returns the radius of the bullseye.
Definition MessengerCodeDetector.h:546
Data storing debug information.
Definition MessengerCodeDetector.h:128
std::vector< DetectedCode > DetectedCodes
Definition of a vector holding DetectedCode object.
Definition MessengerCodeDetector.h:149
void draw(Frame &frame) const
Draws the entire debug information into a given frame.
DetectedCodes detectedCodes_
Contains information about all detected codes.
Definition MessengerCodeDetector.h:170
static void drawBullseye(Frame &frame, const Bullseye &bullseye, const uint8_t *color)
Draws the location of a bullseye into a given frame.
Bullseyes detectedBullseyes_
Contains all detected bullseyes.
Definition MessengerCodeDetector.h:173
This class implements a simple history for previous pixel transitions (a sliding window of pixel tran...
Definition MessengerCodeDetector.h:185
int deltaMinus3
The third previous delta.
Definition MessengerCodeDetector.h:232
int deltaMinus1
The previous delta.
Definition MessengerCodeDetector.h:226
void push(const int newDelta)
Adds a new delta object as most recent history.
Definition MessengerCodeDetector.h:579
int history2()
Returns the history with window size 2.
Definition MessengerCodeDetector.h:569
int history1()
Returns the history with window size 1.
Definition MessengerCodeDetector.h:564
int history3()
Returns the history with window size 3.
Definition MessengerCodeDetector.h:574
TransitionHistory()
Creates a new history object.
Definition MessengerCodeDetector.h:556
int deltaMinus2
The second previous delta.
Definition MessengerCodeDetector.h:229
void reset()
Resets the history object.
Definition MessengerCodeDetector.h:586
This class implements a detector for circular Messenger Codes.
Definition MessengerCodeDetector.h:39
static IndexQuartets extractCodeCandidates(const Vector2 *bullseyes, const Scalar *radii, const size_t size, const Scalar radiusScaleTolerance=Scalar(0.35), const Scalar distanceScaleTolerance=Scalar(0.17))
Extracts quartets of code bullseyes from a given set of bullseyes.
static bool isCodeInsideFrame(const unsigned int width, const unsigned int height, const SquareMatrix3 &homography, const Scalar codeSize)
Returns whether a Messenger Code, defined by a rectifying homography and code size,...
static void calculateRingBitCoordinates(const unsigned int bits, const IndexSet32 &bitsToSkip, const Scalar radius, Vectors2 &coordinates)
Calculates the bit coordinates of a ring of the Messenger Code.
static Scalar radius2bullseyesDistanceLong(const Scalar radius)
Returns the long distance between two bullseyes of the same Messenger Code (for opposite bullseyes) b...
Definition MessengerCodeDetector.h:716
std::bitset< numberCodeBits > CodeBits
Definition of a bitset containing the information of a Messenger Code.
Definition MessengerCodeDetector.h:52
static bool determineHomographyForBullseyeQuartet(const Vector2 *bullseyes, SquareMatrix3 &homography, Scalar &codeSize)
Calculates the homography rectifying the image content covered (and defined) by four bullseyes.
static void detectBullseyesSubset(const uint8_t *const yFrame, const unsigned int width, const unsigned int height, Bullseyes *bullseyes, Lock *multiThreadLock, const unsigned int yFramePaddingElements, const unsigned int firstRow, const unsigned int numberRows)
Detects Messenger Code bullseyes in subset of a given 8 bit grayscale image.
static Vectors2 calculateBitCoordiantes()
Returns 260 coordinates of the Messenger Code's bit elements origin in the center of the Code and nor...
static void detectBullseyesInRow(const uint8_t *const yFrame, const unsigned int width, const unsigned int height, const unsigned int y, Bullseyes &bullseyes, const unsigned int yFramePaddingElements)
Detects Messenger Code bullseyes in a row of an grayscale image.
std::vector< IndexQuartet > IndexQuartets
Definition of a vector holding index quartets.
Definition MessengerCodeDetector.h:67
static bool checkBullseyeInColumn(const uint8_t *const yFrame, const unsigned int frameStrideElements, const unsigned int height, 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)
Checks whether a column contains a bullseye at a specified location.
static bool isTransitionToBlack(const uint8_t *pixel, TransitionHistory &history)
Checks whether the given pixel is a transition-to-black pixel (whether the direct left neighbor is a ...
Definition MessengerCodeDetector.h:614
static bool determineAccurateBullseyeLocation(const uint8_t *yFrame, const unsigned int width, const unsigned int height, const unsigned int xBullseye, const unsigned int yBullseye, const unsigned int threshold, Vector2 &location, const unsigned int framePaddingElements)
Determines the sub-pixel location of the center dot of a known bullseye.
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)
Determines the gray threshold separating bright pixels form dark pixels.
static const Vectors2 & codeBitCoordinates()
Returns the reference to 260 coordinates of the Messenger Code's bit elements origin in the center of...
static Scalar radius2bullseyesDistanceShort(const Scalar radius)
Returns the short distance between two bullseyes of the same Messenger Code (for neighboring bullseye...
Definition MessengerCodeDetector.h:703
static bool extractCodeBits(const uint8_t *yFrame, const unsigned int width, const unsigned int height, const Scalar codeSize, const SquareMatrix3 &homography, CodeBits &codeBits, const unsigned int grayThreshold, const unsigned int yFramePaddingElements, Vectors2 *codeBitsLocationFrame=nullptr)
Extracts the Messenger Code's bit information.
std::vector< CodeBits > Codes
Definition of a vector holding codes.
Definition MessengerCodeDetector.h:57
std::vector< Bullseye > Bullseyes
Definition of a vector holding bullseyes.
Definition MessengerCodeDetector.h:122
std::array< unsigned int, 4 > IndexQuartet
Definition of an index quartet (an array with exactly four indices).
Definition MessengerCodeDetector.h:62
static bool findNextUpperPixel(const uint8_t *yPointer, const unsigned int y, const unsigned int maximalRows, const unsigned int threshold, const unsigned int frameStrideElements, unsigned int &rows)
Finds either the next black or the next white pixel towards negative y direction (upwards in an image...
Definition MessengerCodeDetector.h:659
static bool checkBullseyeInNeighborhood(const uint8_t *const yFrame, const unsigned int width, const unsigned int height, const unsigned int xCenter, const unsigned int yCenter, const unsigned int threshold, const float whiteRingRadius, const float blackRingRadius, const float whiteBorderRadius, const unsigned int yFramePaddingElements)
Checks whether the direction neighborhood contains a bullseye at a specified location.
static Bullseyes detectBullseyes(const uint8_t *const yFrame, const unsigned int width, const unsigned int height, const unsigned int yFramePaddingElements, Worker *worker=nullptr)
Detects Messenger Code bullseyes in a given 8 bit grayscale image.
Definition MessengerCodeDetector.h:593
static bool findNextLowerPixel(const uint8_t *yPointer, const unsigned int y, const unsigned int height, const unsigned int maximalRows, const unsigned int threshold, const unsigned int frameStrideElements, unsigned int &rows)
Finds either the next black or the next white pixel towards positive y direction (downwards in an ima...
Definition MessengerCodeDetector.h:681
static bool isTransitionToWhite(const uint8_t *pixel, TransitionHistory &history)
Checks whether the given pixel is a transition-to-white pixel (whether the direct left neighbor is a ...
Definition MessengerCodeDetector.h:636
static Codes detectMessengerCodesWithDebugInformation(const uint8_t *const yFrame, const unsigned int width, const unsigned int height, DebugInformation &debugInformation, const unsigned int yFramePaddingElements, Worker *worker=nullptr)
Detects Messenger Codes in a given 8 bit grayscale image and returns debug information.
static Codes detectMessengerCodes(const uint8_t *const yFrame, const unsigned int width, const unsigned int height, const unsigned int paddingElements, Worker *worker=nullptr)
Detects Messenger Codes in a given 8 bit grayscale image.
static constexpr int deltaThreshold
The intensity threshold between two successive pixels to count as a transition from black to white (o...
Definition MessengerCodeDetector.h:179
static bool correctRotation(const uint8_t *yFrame, const unsigned int width, const unsigned int height, const Vector2 *bullseyes, const Scalar codeSize, SquareMatrix3 &homography, const unsigned int yFramePaddingElements)
Corrects the orientation of a given homography already rectifying the content of a Messenger Code.
static Codes detectMessengerCodes(const uint8_t *const yFrame, const unsigned int width, const unsigned int height, const unsigned int yFramePaddingElements, DebugInformation *debugInformation=nullptr, Worker *worker=nullptr)
Detects Messenger Codes in a given 8 bit grayscale image.
static Caller< void > createStatic(typename StaticFunctionPointerMaker< void, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass >::Type function)
Creates a new caller container for a static function with no function parameter.
Definition Caller.h:2876
This class implements Ocean's image class.
Definition Frame.h:1808
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
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::set< Index32 > IndexSet32
Definition of a set holding 32 bit indices.
Definition Base.h:114
float Scalar
Definition of a scalar type.
Definition Math.h:129
std::vector< Vector2 > Vectors2
Definition of a vector holding Vector2 objects.
Definition Vector2.h:64
The namespace covering the entire Ocean framework.
Definition Accessor.h:15
Data of one detected code.
Definition MessengerCodeDetector.h:135
Vectors2 codeBitsLocationFrame_
Location of the bits in frame coordinates.
Definition MessengerCodeDetector.h:140
CodeBits codebits_
Extracted code bits.
Definition MessengerCodeDetector.h:143
Bullseyes bullseyes_
The four bullseyes.
Definition MessengerCodeDetector.h:137