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