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