Ocean
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 
15 #include "ocean/cv/FrameInverter.h"
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 
25 namespace Ocean
26 {
27 
28 namespace CV
29 {
30 
31 namespace Detector
32 {
33 
34 /**
35  * This class implements a detector for circular Messenger Codes.
36  * @ingroup cvdetector
37  */
38 class 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  */
72  class Bullseye
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  */
507  static const Vectors2& codeBitCoordinates();
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 
533 inline 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 
579 inline void MessengerCodeDetector::TransitionHistory::push(const int newDelta)
580 {
581  deltaMinus3 = deltaMinus2;
582  deltaMinus2 = deltaMinus1;
583  deltaMinus1 = newDelta;
584 }
585 
587 {
588  deltaMinus1 = 0;
589  deltaMinus2 = 0;
590  deltaMinus3 = 0;
591 }
592 
593 inline 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 
614 inline 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 
636 inline 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 
658 template <bool tFindBlackPixel>
659 bool 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 
680 template <bool tFindBlackPixel>
681 bool 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 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 const Vectors2 & codeBitCoordinates()
Returns the reference to 260 coordinates of the Messenger Code's bit elements origin in the center of...
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:1792
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:128
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