Ocean
Loading...
Searching...
No Matches
FinderPatternDetector.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#pragma once
9
14
15#include "ocean/base/Frame.h"
16#include "ocean/base/Memory.h"
17
18#include "ocean/cv/Bresenham.h"
19
21
22#include "ocean/math/Box2.h"
24#include "ocean/math/Vector2.h"
25
26#include <array>
27#include <cstdint>
28
29namespace Ocean
30{
31
32namespace CV
33{
34
35namespace Detector
36{
37
38namespace QRCodes
39{
40
41/**
42 * Definition of a triplet of indices
43 * @ingroup cvdetectorqrcodes
44 */
45typedef std::array<unsigned int, 3> IndexTriplet;
46
47/**
48 * Definition of a vector index triplets
49 * @ingroup cvdetectorqrcodes
50 */
51typedef std::vector<IndexTriplet> IndexTriplets;
52
53/**
54 * Definition of a class for finder patterns of QR codes (squares in the top-left, top-right and bottom-left corners)
55 * @ingroup cvdetectorqrcodes
56 */
58{
59 public:
60
61 /**
62 * Creates an invalid finder pattern object.
63 */
64 inline FinderPattern();
65
66 /**
67 * Creates a new finder pattern object by a given position and edge length.
68 * @param position The (center) position of the finder pattern within the camera frame
69 * @param length The edge length of the finder pattern in pixels, with range (0, infinity)
70 * @param centerIntensity The intensity that has been measured in the center of the finder pattern, range: [0, 255]
71 * @param grayThreshold Threshold that was used during the detection, range [0, 255]
72 * @param symmetryScore Symmetry score of this finder pattern, range: [0, infinity) (lower value = higher symmetry)
73 */
74 inline FinderPattern(const Vector2& position, const Scalar length, const unsigned int centerIntensity, const unsigned int grayThreshold, const Scalar symmetryScore);
75
76 /**
77 * Creates a new finder pattern object by a given position and edge length.
78 * @param position The (center) position of the finder pattern within the camera frame
79 * @param length The edge length of the finder pattern in pixels, with range (0, infinity)
80 * @param centerIntensity The intensity that has been measured in the center of the finder pattern, range: [0, 255]
81 * @param grayThreshold Threshold that was used during the detection, range [0, 255]
82 * @param symmetryScore Symmetry score of this finder pattern, range: [0, infinity) (lower value = higher symmetry)
83 * @param corners The locations of the four corners of this finder pattern, must be valid and have 4 elements
84 * @param orientation Dominant orientation of the finder pattern
85 * @param moduleSize The size of modules (=bits) in pixels
86 */
87 inline FinderPattern(const Vector2& position, const Scalar length, const unsigned int centerIntensity, const unsigned int grayThreshold, const Scalar symmetryScore, const Vector2* corners, const Vector2& orientation, const Scalar moduleSize);
88
89 /**
90 * Returns the (center) position of the finder pattern.
91 * @return The finder pattern's position within the camera frame
92 */
93 inline const Vector2& position() const;
94
95 /**
96 * Returns the radius of the finder pattern.
97 * @return The finder pattern's radius, with range (0, infinity), 0 for an invalid object
98 */
99 inline Scalar length() const;
100
101 /**
102 * Returns the intensity value that was measured in the center of the finder pattern
103 * @return The intensity value, range: [0, 255]
104 */
105 inline unsigned int centerIntensity() const;
106
107 /**
108 * Returns the threshold that was used for the detection of this finder pattern
109 * @return The threshold value, range: [0, 255]
110 */
111 inline unsigned int grayThreshold() const;
112
113 /**
114 * Returns the width of a module (= bit) in pixels
115 * @return The module width
116 */
117 inline Scalar moduleSize() const;
118
119 /**
120 * Returns the symmetry score that was determined when this finder pattern was detected
121 * @return The symmetry score of this finder pattern
122 */
123 inline Scalar symmetryScore() const;
124
125 /**
126 * Returns true if the four corners of this finder pattern are known, otherwise false
127 * @return True, if the four corners are known, otherwise false
128 */
129 inline bool cornersKnown() const;
130
131 /**
132 * Returns a pointer to the four corners of this finder pattern.
133 * @return A constant pointer to the four corners of this finder pattern. These values are undefined if `cornersKnown()` returns false.
134 */
135 inline const Vector2* corners() const;
136
137 /**
138 * Returns the dominant orientation of this finder pattern
139 * @return The vector defining the orientation (will be (1, 0) by default, i.e. if it's not set)
140 */
141 inline const Vector2& orientation() const;
142
143 /**
144 * Returns whether this finder pattern is of normal reflectance
145 * @return True if so, otherwise false
146 */
147 inline bool isNormalReflectance() const;
148
149 /**
150 * Comparator to sort finder patterns based on their location in an image
151 * Pattern `a` comes before pattern `b` if (pseudo-code) `a.y * imageWidth + a.x < b.y * imageWidth + b.x`
152 * @param first The first finder pattern to compare
153 * @param second The second finder pattern to compare
154 * @return True if the first pattern comes before the second pattern, otherwise false
155 */
156 static inline bool comesBefore(const FinderPattern& first, const FinderPattern& second);
157
158 protected:
159
160 /// The (center) position of the finder pattern within the camera frame.
162
163 /// The edge length of the finder pattern in pixels, range: (0, infinity).
165
166 /// The intensity value that has been measured in the center of the finder pattern
167 unsigned int centerIntensity_;
168
169 /// The threshold that was used during the detection of this finder pattern.
170 unsigned int grayThreshold_;
171
172 /// The symmetry score of this finder pattern, range: [0, infinity) (lower score = higher symmetry)
174
175 /// True if the four corners of this finder pattern are known, otherwise false
177
178 /// The four corners of this finder pattern; points are stored in counter-clockwise order but no guarantee on which corner is the first; if `cornersDetected_` is false these values will be undefined
180
181 /// Dominant orientation of this finder pattern
183
184 /// Module width (bit width) in pixels
186};
187
188/**
189 * Definition of a vector holding finder pattern.
190 * @ingroup cvdetectorqrcodes
191 */
192typedef std::vector<FinderPattern> FinderPatterns;
193
194/**
195 * Definition of a 3-tuple of finder patterns
196 * @ingroup cvdetectorqrcodes
197 */
198typedef std::array<FinderPattern, 3> FinderPatternTriplet;
199
200/**
201 * This class implements a detector for finder patterns which are part of QR Codes.
202 * @ingroup cvdetectorqrcodes
203 */
204class OCEAN_CV_DETECTOR_QRCODES_EXPORT FinderPatternDetector
205{
206 protected:
207
208 /// The intensity threshold between two successive pixels to count as a transition from dark to light (or vice versa).
209 static constexpr int deltaThreshold = 30;
210
211 /**
212 * This class implements a simple history for previous pixel transitions (a sliding window of pixel transitions).
213 */
215 {
216 public:
217
218 /**
219 * Creates a new history object.
220 */
221 inline TransitionHistory();
222
223 /**
224 * Returns the history with window size N.
225 * @return The sum of the most recent delta
226 */
227 inline int history1();
228
229 /**
230 * Returns the history with window size N.
231 * @return The sum of the most recent delta
232 */
233 inline int history2();
234
235 /**
236 * Returns the history with window size N.
237 * @return The sum of the most recent delta
238 */
239 inline int history3();
240
241 /**
242 * Returns the history with window size N.
243 * @return The sum of the most recent delta
244 */
245 inline int history4();
246
247 /**
248 * Returns the history with window size N.
249 * @return The sum of the most recent delta
250 */
251 inline int history5();
252
253 /**
254 * Adds a new delta object as most recent history.
255 * Existing history objects will be moved by one pixel.
256 * @param newDelta The new delta object to be added
257 */
258 inline void push(const int newDelta);
259
260 /**
261 * Resets the history object.
262 */
263 inline void reset();
264
265 protected:
266
267 /// The most recent deltas.
268 int deltas_[5] = { 0, 0, 0, 0, 0 };
269 };
270
271 public:
272
273 /**
274 * Detects finder patterns of a QR code in a 8 bit grayscale image.
275 * @param yFrame The 8 bit grayscale frame in which the finder patterns will be detected, must be valid
276 * @param width The width of the given grayscale frame in pixel, with range [15, infinity)
277 * @param height The height of the given grayscale frame in pixel, with range [15, infinity)
278 * @param minimumDistance The minimum distance in pixels that is enforced between any pair of finder patterns, range: [0, infinity), default: 10
279 * @param paddingElements Optional number of padding elements at the end of each image row, in elements, with range [0, infinity), default: 0
280 * @param worker Optional worker to distribute the computation
281 * @return The detected finder patterns
282 */
283 static FinderPatterns detectFinderPatterns(const uint8_t* const yFrame, const unsigned int width, const unsigned int height, const unsigned int minimumDistance = 10u, const unsigned int paddingElements = 0u, Worker* worker = nullptr);
284
285 /**
286 * Extract 3-tuples of finder patterns that form good (plausible) candidates for QR code symbols
287 * @param finderPatterns The list finder patterns in which 3-tuples forming potential QR code symbols are sought, must be valid, minimum size: 3
288 * @param distanceScaleTolerance Scale factor that define how much corners of one finder pattern may deviate from the parallel lines of another finder pattern, range: [0, infinity), default: 0.05
289 * @param moduleSizeScaleTolerance Defines the maximum difference scale of the module size between pairs of finder patterns in order to be considered a match, range: [0, 1], default: 0.35
290 * @param angleTolerance Defines the maximum difference of the dominant orientation of pairs of finder patterns in order to be considered a match, measured in radian, range: [0, PI/4), default: deg2rad(9)
291 * @return A list of 3-tuples of finder patterns, will be empty on failure (or if nothing was found)
292 */
293 static IndexTriplets extractIndexTriplets(const FinderPatterns& finderPatterns, const Scalar distanceScaleTolerance = Scalar(0.175), const Scalar moduleSizeScaleTolerance = Scalar(0.35), const Scalar angleTolerance = Numeric::deg2rad(Scalar(9)));
294
295 protected:
296
297 /**
298 * Detects finder patterns of QR codes in subregion of a given 8 bit grayscale image.
299 * @param yFrame The 8 bit grayscale frame in which the finder patterns will be detected, must be valid
300 * @param width The width of the given grayscale frame in pixel, with range [15, infinity)
301 * @param height The height of the given grayscale frame in pixel, with range [15, infinity)
302 * @param finderPatterns The resulting finderPatterns, will be added to the end of the vector
303 * @param multiThreadLock Lock object in case this function is executed in multiple threads concurrently, otherwise nullptr
304 * @param paddingElements Optional number of padding elements at the end of each image row, in elements, with range [0, infinity)
305 * @param firstRow The first row to be handled, with range [7, height - 7)
306 * @param numberRows The number of rows to be handled, with range [1, height - 7 - firstRow]
307 */
308 static void detectFinderPatternsSubset(const uint8_t* const yFrame, const unsigned int width, const unsigned int height, FinderPatterns* finderPatterns, Lock* multiThreadLock, const unsigned int paddingElements, const unsigned int firstRow, const unsigned int numberRows);
309
310 /**
311 * Detects finder patterns of QR codes in a single row of an grayscale image.
312 * @param yFrame The 8 bit grayscale frame in which the finder patterns will be detected, must be valid
313 * @param width The width of the given grayscale frame in pixel, with range [15, infinity)
314 * @param height The height of the given grayscale frame in pixel, with range [15, infinity)
315 * @param y The index of the row in which the finder patterns will be detected, with range [7, height - 8]
316 * @param finderPatterns The resulting detected finder patterns, will be added to the end of the vector
317 * @param paddingElements Optional number of padding elements at the end of each image row, in elements, with range [0, infinity)
318 */
319 static void detectFinderPatternInRow(const uint8_t* const yFrame, const unsigned int width, const unsigned int height, const unsigned int y, FinderPatterns& finderPatterns, const unsigned int paddingElements);
320
321 /**
322 * Estimates the locations of the corners of finder pattern and computes the dominant orientation of the finder pattern from those corners
323 * @param xCenter The pixel-accurate x-coordinate of the candidate location
324 * @param yCenter The pixel-accurate y-coordinate of the candidate location
325 * @param edgePoints The edge points which will be used to determine the corners of the finder pattern, must be valid and have `edgePointsSize` elements
326 * @param edgePointsSize Number of edge points that are available, range: [2, infinity) must be even
327 * @param location The resulting center location of the finder pattern that is determined from the four corners that will be determined by this function
328 * @param corners The resulting four corners of the finder pattern that will be determined, must be valid, in counter-clockwise order, and must have size of at least 4 elements
329 * @param orientation The resulting main orientation of the finder pattern that will be determined
330 * @param moduleSize The resulting size of the modules in this finder pattern candidate
331 * @param edgePointDistanceTolerance The factor that defines the maximum deviation from the distance between the center and the edge point closest to the center, range: [0, infinity)
332 * @param maxEdgeLineDistance The maximum distance (in pixel) that new edge points may have in order to be accepted as "on the edge line", range: [0, infinity)
333 * @return True on success, otherwise false
334 */
335 static bool estimateFinderPatternCorners(const unsigned int xCenter, const unsigned int yCenter, const Vector2* edgePoints, const unsigned int edgePointsSize, Vector2& location, Vector2* corners, Vector2& orientation, Scalar& moduleSize, const Scalar edgePointDistanceTolerance = 2.25, const Scalar maxEdgeLineDistance = 1.5);
336
337 /**
338 * Refine the location and corners of a finder pattern
339 * @param yFrame Pointer to the input grayscale image, must be valid
340 * @param width The width of the input grayscale image, range: [1, infinity)
341 * @param height The height of the input grayscale image, range:[1, infinity)
342 * @param finderPattern The resulting finder pattern of which its position and corners will be refined
343 * @param yFramePaddingElements The number of padding elements of the input grayscale image, range: [0, infinity)
344 * @return True if the refinement was successful, otherwise false
345 * @sa estimateFinderPatternCorners()
346 */
347 static bool refineFinderPatternLocation(const uint8_t* const yFrame, const unsigned int width, const unsigned int height, FinderPattern& finderPattern, const unsigned int yFramePaddingElements = 0u);
348
349 /**
350 * Performs a check around a given candidate location looking for a correct configuration of light and dark pixels (testing 8 angles each yielding 2 edge points)
351 * @param yFrame The 8 bit grayscale frame in which the finder pattern candidate will be tested, must be valid
352 * @param width The width of the given grayscale frame in pixels, range: [15, infinity)
353 * @param height The height of the given grayscale frame in pixels, range: [15, infinity)
354 * @param paddingElements The number of padding elements in the given image with range [0, infinity)
355 * @param xCenter The horizontal location within the frame at which the existence of the finder pattern will be checked, in pixels, with range [0, width - 1]
356 * @param yCenter The vertical location within the frame at which the existence of the finder pattern will be checked, in pixels, with range [0, height - 1]
357 * @param threshold The grayscale threshold separating a bright pixel from a dark pixel, with range [0, 255]
358 * @param blackSquareSegmentMin Minimum diameter of the outer black square in pixels, range: [1, infinity)
359 * @param blackSquareSegmentMax Maximum diameter of the outer black square in pixels, range: [blackSquareSegmentMin, infinity)
360 * @param whiteSquareSegmentMin Minimum diameter of the inner white square in pixels, range: [1, infinity)
361 * @param whiteSquareSegmentMax Maximum diameter of the inner white square in pixels, range: [whiteSquareSegmentMin, infinity)
362 * @param centerSegmentMin Minimum diameter of the center black square in pixels, range: [1, infinity)
363 * @param centerSegmentMax Maximum diameter of the center black square in pixels, range: [centerSegmentMin, infinity)
364 * @param symmetryScore The resulting symmetry score that is computed for the current candidate location `(xCenter, yCenter)`; this score is based on distances so the lower the score, the better. Range: [0, infinity)
365 * @param edgePoints If specified, will hold the resulting points detected during the directional checks on the outside border of the finder pattern candidate. Must be valid, expected size: `2 * angles`
366 * @return True if all edge points of the finder pattern are found in all scanline directions, otherwise false
367 */
368 static bool checkFinderPatternInNeighborhood(const uint8_t* const yFrame, const unsigned width, const unsigned height, const unsigned int paddingElements, const unsigned int xCenter, const unsigned int yCenter, const unsigned int threshold, const unsigned int blackSquareSegmentMin, const unsigned int blackSquareSegmentMax, const unsigned int whiteSquareSegmentMin, const unsigned int whiteSquareSegmentMax, const unsigned int centerSegmentMin, const unsigned int centerSegmentMax, Scalar& symmetryScore, Vector2* edgePoints);
369
370 /**
371 * Performs a check for a given candidate location in a specified direction (yielding 2 edge points)
372 * @param yFrame The 8 bit grayscale frame in which the finder pattern candidate will be tested, must be valid
373 * @param width The width of the given grayscale frame in pixels, range: [15, infinity)
374 * @param height The height of the given grayscale frame in pixels, range: [15, infinity)
375 * @param paddingElements The number of padding elements in the given image with range [0, infinity)
376 * @param xCenter The horizontal location within the frame at which the existence of the finder pattern will be checked, in pixels, with range [0, width - 1]
377 * @param yCenter The vertical location within the frame at which the existence of the finder pattern will be checked, in pixels, with range [0, height - 1]
378 * @param angle The angle in Radian defining the directions in which edge points will be searched, range: [0, pi)
379 * @param threshold The grayscale threshold separating a bright pixel from a dark pixel, with range [0, 255]
380 * @param blackSquareSegmentMin Minimum diameter of the outer black square in pixels, range: [1, infinity)
381 * @param blackSquareSegmentMax Maximum diameter of the outer black square in pixels, range: [blackSquareSegmentMin, infinity)
382 * @param whiteSquareSegmentMin Minimum diameter of the inner white square in pixels, range: [1, infinity)
383 * @param whiteSquareSegmentMax Maximum diameter of the inner white square in pixels, range: [whiteSquareSegmentMin, infinity)
384 * @param centerSegmentMin Minimum diameter of the center black square in pixels, range: [1, infinity)
385 * @param centerSegmentMax Maximum diameter of the center black square in pixels, range: [centerSegmentMin, infinity)
386 * @param topBorder The resulting location of the last pixel on the current finder pattern in the specified direction of the scanline
387 * @param bottomBorder The resulting location of the last pixel on the current finder pattern in the opposite direction (`angle + pi`) of the specified direction of the scanline
388 * @return True if the two edge points of the finder pattern are found in the specified scanline direction, otherwise false
389 */
390 static bool checkFinderPatternDirectional(const uint8_t* const yFrame, const unsigned int width, const unsigned int height, const unsigned int paddingElements, const unsigned int xCenter, const unsigned int yCenter, const Scalar angle, const unsigned int threshold, const unsigned int blackSquareSegmentMin, const unsigned int blackSquareSegmentMax, const unsigned int whiteSquareSegmentMin, const unsigned int whiteSquareSegmentMax, const unsigned int centerSegmentMin, const unsigned int centerSegmentMax, Vector2& topBorder, Vector2& bottomBorder);
391
392 /**
393 * Checks whether the given pixel is a transition-to-black pixel (whether the direct left neighbor is a bright pixel).
394 * @param pixel The pixel to be checked, must be valid
395 * @param history The history object containing information about previous pixels
396 * @return True, if so
397 */
398 static inline bool isTransitionToBlack(const uint8_t* pixel, TransitionHistory& history);
399
400 /**
401 * Checks whether the given pixel is a transition-to-white pixel (whether the direct left neighbor is a dark pixel).
402 * @param pixel The pixel to be checked, must be valid
403 * @param history The history object containing information about previous pixels
404 * @return True, if so
405 */
406 static inline bool isTransitionToWhite(const uint8_t* pixel, TransitionHistory& history);
407
408 /**
409 * Determines the gray threshold separating bright pixels form dark pixels.
410 * The threshold is based on already actual pixel values for which the association is known already.<br>
411 * The provided start position is a pointer to any pixel within the image, with horizontal range [1, width - segmentSize1 - segmentSize2 - segmentSize3 - segmentSize4 - segmentSize5 - 2].
412 * 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.
413 * @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
414 * @param segmentSize1 The number of pixels covering dark pixels, with range [1, width - ...)
415 * @param segmentSize2 The number of pixels covering bright pixels, with range [1, width - ...)
416 * @param segmentSize3 The number of pixels covering dark pixels, with range [1, width - ...)
417 * @param segmentSize4 The number of pixels covering bright pixels, with range [1, width - ...)
418 * @param segmentSize5 The number of pixels covering dark pixels, with range [1, width - segmentSize1 - segmentSize2 - segmentSize3 - segmentSize4 - 2]
419 * @return The threshold separating bright pixels from dark pixels, with range [0, 255], -1 if no valid threshold could be determined
420 */
421 static inline 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);
422
423 /**
424 * Returns true if a pair of finder patterns is in parallel configuration, i.e., if one is above/below/left of/right of the other (and vice versa)
425 * @param finderPatternA The first finder pattern that will be used
426 * @param finderPatternB The second finder pattern that will be used
427 * @param distanceTolerance A scaling factor that defines how much the configuration may deviate from perfect parallelism, range: [0, infinity), default: 0.05
428 * @return True if the two finder patterns are in a parallel configuration, otherwise false
429 */
430 static inline bool isParallel(const FinderPattern& finderPatternA, const FinderPattern& finderPatternB, const Scalar distanceTolerance = Scalar(0.05));
431
432 /**
433 * Returns true if a pair of finder patterns is in a diagonal configuration, i.e. the center of one pattern lies on one of the two diagonal (infinite) lines of the other finder pattern (and vice versa)
434 * @param finderPatternA The first finder pattern that will be used
435 * @param finderPatternB The second finder pattern that will be used
436 * @param angleTolerance Defines the angle that the centers of the finder pattern may deviate from the actual diagonal infinite lines (in radian), range: [0, PI/2), default: deg2rad(9)
437 * @return True if the two finder patterns are in a diagonal configuration, otherwise false
438 */
439 static inline bool isDiagonal(const FinderPattern& finderPatternA, const FinderPattern& finderPatternB, const Scalar angleTolerance = Numeric::deg2rad(9));
440};
441
443 FinderPattern::FinderPattern(Vector2(-1, -1), Scalar(0), 0u, 0u, Numeric::maxValue())
444{
445 // nothing to do here
446}
447
448inline FinderPattern::FinderPattern(const Vector2& position, const Scalar length, const unsigned int centerIntensity, const unsigned int grayThreshold, const Scalar symmetryScore) :
449 position_(position),
450 length_(length),
451 centerIntensity_(centerIntensity),
452 grayThreshold_(grayThreshold),
453 symmetryScore_(symmetryScore),
454 cornersKnown_(false),
455 orientation_(1, 0),
456 moduleSize_(length / Scalar(7))
457{
458 ocean_assert(centerIntensity_ <= 255u);
459 ocean_assert(grayThreshold_ <= 255u);
460
461 corners_[0] = Vector2(-1, -1);
462 corners_[1] = Vector2(-1, -1);
463 corners_[2] = Vector2(-1, -1);
464 corners_[3] = Vector2(-1, -1);
465}
466
467inline FinderPattern::FinderPattern(const Vector2& position, const Scalar length, const unsigned int centerIntensity, const unsigned int grayThreshold, const Scalar symmetryScore, const Vector2* corners, const Vector2& orientation, const Scalar moduleSize) :
468 position_(position),
469 length_(length),
470 centerIntensity_(centerIntensity),
471 grayThreshold_(grayThreshold),
472 symmetryScore_(symmetryScore),
473 cornersKnown_(true),
474 orientation_(orientation),
475 moduleSize_(moduleSize)
476{
477 ocean_assert(centerIntensity_ <= 255u);
478 ocean_assert(grayThreshold_ <= 255u);
479
480 ocean_assert(corners != nullptr);
481
482 // Expect a counter-clockwise order for the corners
483
484 ocean_assert((corners[1] - corners[0]).cross(corners[3] - corners[0]) <= 0);
485 ocean_assert((corners[2] - corners[1]).cross(corners[0] - corners[1]) <= 0);
486 ocean_assert((corners[3] - corners[2]).cross(corners[1] - corners[2]) <= 0);
487 ocean_assert((corners[0] - corners[3]).cross(corners[2] - corners[3]) <= 0);
488
489 corners_[0] = corners[0];
490 corners_[1] = corners[1];
491 corners_[2] = corners[2];
492 corners_[3] = corners[3];
493}
494
495inline const Vector2& FinderPattern::position() const
496{
497 return position_;
498}
499
501{
502 return length_;
503}
504
505inline unsigned int FinderPattern::centerIntensity() const
506{
507 return centerIntensity_;
508}
509
510inline unsigned int FinderPattern::grayThreshold() const
511{
512 return grayThreshold_;
513}
514
516{
517 return moduleSize_;
518}
519
521{
522 return symmetryScore_;
523}
524
526{
527 return cornersKnown_;
528}
529
530inline const Vector2* FinderPattern::corners() const
531{
532 // Expect a counter-clockwise order for the corners, if the corners are known
533
534 ocean_assert(cornersKnown() == false || (corners_[1] - corners_[0]).cross(corners_[3] - corners_[0]) <= 0);
535 ocean_assert(cornersKnown() == false || (corners_[2] - corners_[1]).cross(corners_[0] - corners_[1]) <= 0);
536 ocean_assert(cornersKnown() == false || (corners_[3] - corners_[2]).cross(corners_[1] - corners_[2]) <= 0);
537 ocean_assert(cornersKnown() == false || (corners_[0] - corners_[3]).cross(corners_[2] - corners_[3]) <= 0);
538
539 return corners_;
540}
541
543{
544 ocean_assert(Numeric::isEqualEps(orientation_.length() - Scalar(1)));
545 return orientation_;
546}
547
552
553inline bool FinderPattern::comesBefore(const FinderPattern& first, const FinderPattern& second)
554{
555 return first.position().y() > second.position().y() || (first.position().y() == second.position().y() && first.position().x() > second.position().x());
556};
557
559 : deltas_{ 0, 0, 0, 0, 0 }
560{
561 // Nothing else to do.
562}
563
565{
566 return deltas_[0];
567}
568
570{
571 return deltas_[0] + deltas_[1];
572}
573
575{
576 return deltas_[0] + deltas_[1] + deltas_[2];
577}
578
580{
581 return deltas_[0] + deltas_[1] + deltas_[2] + deltas_[3];
582}
583
585{
586 return deltas_[0] + deltas_[1] + deltas_[2] + deltas_[3] + deltas_[4];
587}
588
590{
591 deltas_[4] = deltas_[3];
592 deltas_[3] = deltas_[2];
593 deltas_[2] = deltas_[1];
594 deltas_[1] = deltas_[0];
595 deltas_[0] = newDelta;
596}
597
599{
600 deltas_[0] = 0;
601 deltas_[1] = 0;
602 deltas_[2] = 0;
603 deltas_[3] = 0;
604 deltas_[4] = 0;
605}
606
607inline bool FinderPatternDetector::isTransitionToBlack(const uint8_t* pixel, TransitionHistory& history)
608{
609 const int currentDelta = int(*(pixel + 0) - *(pixel - 1));
610
611 bool result = false;
612
613 if (currentDelta < -deltaThreshold)
614 {
615 result = true;
616 }
617 else if ((currentDelta + history.history1() < -deltaThreshold)
618 || (currentDelta + history.history2() < -(deltaThreshold * 5 / 4))
619 || (currentDelta + history.history3() < -(deltaThreshold * 6 / 4))
620 || (currentDelta + history.history4() < -(deltaThreshold * 7 / 4))
621 || (currentDelta + history.history5() < -(deltaThreshold * 8 / 4)))
622 {
623 result = true;
624 }
625
626 history.push(currentDelta);
627
628 return result;
629}
630
631inline bool FinderPatternDetector::isTransitionToWhite(const uint8_t* pixel, TransitionHistory& history)
632{
633 const int currentDelta = int(*(pixel + 0) - *(pixel - 1));
634
635 bool result = false;
636
637 if (currentDelta > deltaThreshold)
638 {
639 result = true;
640 }
641 else if ((currentDelta + history.history1() > deltaThreshold)
642 || (currentDelta + history.history2() > (deltaThreshold * 5 / 4))
643 || (currentDelta + history.history3() > (deltaThreshold * 6 / 4))
644 || (currentDelta + history.history4() > (deltaThreshold * 7 / 4))
645 || (currentDelta + history.history5() > (deltaThreshold * 8 / 4)))
646 {
647 result = true;
648 }
649
650 history.push(currentDelta);
651
652 return result;
653}
654
655inline unsigned int FinderPatternDetector::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)
656{
657 unsigned int sumBlack = 0u;
658 unsigned int sumWhite = 0u;
659
660 sumWhite += *(yPosition - 1);
661
662 for (unsigned int n = 0u; n < segmentSize1; ++n)
663 {
664 sumBlack += *yPosition++;
665 }
666
667 for (unsigned int n = 0u; n < segmentSize2; ++n)
668 {
669 sumWhite += *yPosition++;
670 }
671
672 for (unsigned int n = 0u; n < segmentSize3; ++n)
673 {
674 sumBlack += *yPosition++;
675 }
676
677 for (unsigned int n = 0u; n < segmentSize4; ++n)
678 {
679 sumWhite += *yPosition++;
680 }
681
682 for (unsigned int n = 0u; n < segmentSize5; ++n)
683 {
684 sumBlack += *yPosition++;
685 }
686
687 sumWhite += *yPosition;
688
689 const unsigned int averageBlack = sumBlack / (segmentSize1 + segmentSize3 + segmentSize5);
690 const unsigned int averageWhite = sumWhite / (segmentSize2 + segmentSize4 + 2u);
691
692 if (averageBlack + 2u >= averageWhite)
693 {
694 // the separate between bright and dark pixels is not strong enough
695 return (unsigned int)(-1);
696 }
697
698 return (averageBlack + averageWhite + 1u) / 2u;
699}
700
701inline bool FinderPatternDetector::isParallel(const FinderPattern& finderPatternA, const FinderPattern& finderPatternB, const Scalar distanceTolerance)
702{
703 ocean_assert(finderPatternA.cornersKnown() && finderPatternB.cornersKnown());
704 ocean_assert(finderPatternB.corners() != nullptr && finderPatternA.corners() != nullptr);
705 ocean_assert(distanceTolerance >= 0);
706
707 const Line2 linesB[4] =
708 {
709 Line2(finderPatternB.corners()[1], (finderPatternB.corners()[0] - finderPatternB.corners()[1]).normalized()),
710 Line2(finderPatternB.corners()[2], (finderPatternB.corners()[1] - finderPatternB.corners()[2]).normalized()),
711 Line2(finderPatternB.corners()[3], (finderPatternB.corners()[2] - finderPatternB.corners()[3]).normalized()),
712 Line2(finderPatternB.corners()[0], (finderPatternB.corners()[3] - finderPatternB.corners()[0]).normalized())
713 };
714
715 const Vector2 lineAB = finderPatternB.position() - finderPatternA.position();
716 const Vector2 directionAB = lineAB.normalizedOrZero();
717
718 const Scalar squareDistanceThreshold = (lineAB.length() * distanceTolerance) * (lineAB.length() * distanceTolerance);
719
720 for (unsigned int n = 0u; n < 4u; ++n)
721 {
722 // Reject pairs of lines diverge too much
723 if (Numeric::abs(directionAB * linesB[n].direction()) <= Numeric::cos(Numeric::deg2rad(35)))
724 {
725 continue;
726 }
727
728 // Check if:
729 //
730 // * the corners `i` and `(i+1)` of finder pattern a are both "close enough" to the n-th line of finder pattern b, i.e., is the line between corners `i` and `(i+1)` roughly parallel to the n-th line of finder pattern b.
731 // * the opposite corners in finder pattern a, `(i+2) % 4` and `(i+3) % 4`, and line opposite to the n-th line in finder pattern b (n + 2 % 4) are roughly parallel as well.
732 //
733 // If both is true, finder patterns a and b are considered parallel.
734
735 const Scalar sqrDistanceCornerA0 = linesB[n].sqrDistance(finderPatternA.corners()[0]);
736 const Scalar sqrDistanceCornerA1 = linesB[n].sqrDistance(finderPatternA.corners()[1]);
737
738 if (sqrDistanceCornerA0 < squareDistanceThreshold && sqrDistanceCornerA1 < squareDistanceThreshold)
739 {
740 if (linesB[(n + 2u) & 0b0011u].sqrDistance(finderPatternA.corners()[2]) < squareDistanceThreshold && linesB[(n + 2u) & 0b0011u].sqrDistance(finderPatternA.corners()[3]) < squareDistanceThreshold) // (n + 2u) & 0b0011u == (n + 2u) % 4
741 {
742 return true;
743 }
744 }
745
746 const Scalar sqrDistanceCornerA2 = linesB[n].sqrDistance(finderPatternA.corners()[2]);
747
748 if (sqrDistanceCornerA1 < squareDistanceThreshold && sqrDistanceCornerA2 < squareDistanceThreshold)
749 {
750 if (linesB[(n + 2u) & 0b0011u].sqrDistance(finderPatternA.corners()[3]) < squareDistanceThreshold && linesB[(n + 2u) & 0b0011u].sqrDistance(finderPatternA.corners()[0]) < squareDistanceThreshold) // (n + 2u) & 0b0011u == (n + 2u) % 4
751 {
752 return true;
753 }
754 }
755
756 const Scalar sqrDistanceCornerA3 = linesB[n].sqrDistance(finderPatternA.corners()[3]);
757
758 if (sqrDistanceCornerA2 < squareDistanceThreshold && sqrDistanceCornerA3 < squareDistanceThreshold)
759 {
760 if (linesB[(n + 2u) & 0b0011u].sqrDistance(finderPatternA.corners()[0]) < squareDistanceThreshold && linesB[(n + 2u) & 0b0011u].sqrDistance(finderPatternA.corners()[1]) < squareDistanceThreshold) // (n + 2u) & 0b0011u == (n + 2u) % 4
761 {
762 return true;
763 }
764 }
765
766 if (sqrDistanceCornerA3 < squareDistanceThreshold && sqrDistanceCornerA0 < squareDistanceThreshold)
767 {
768 if (linesB[(n + 2u) & 0b0011u].sqrDistance(finderPatternA.corners()[1]) < squareDistanceThreshold && linesB[(n + 2u) & 0b0011u].sqrDistance(finderPatternA.corners()[2]) < squareDistanceThreshold) // (n + 2u) & 0b0011u == (n + 2u) % 4
769 {
770 return true;
771 }
772 }
773 }
774
775 return false;
776}
777
778inline bool FinderPatternDetector::isDiagonal(const FinderPattern& finderPatternA, const FinderPattern& finderPatternB, const Scalar angleTolerance)
779{
780 ocean_assert(finderPatternA.cornersKnown() && finderPatternB.cornersKnown());
781 ocean_assert(finderPatternB.corners() != nullptr && finderPatternA.corners() != nullptr);
782 ocean_assert(angleTolerance >= 0 && angleTolerance < Numeric::deg2rad(90));
783
784 const Vector2 directionAB = (finderPatternB.position() - finderPatternA.position()).normalizedOrZero();
785 const Scalar angleThreshold = Numeric::abs(Numeric::cos(angleTolerance));
786
787 const Vector2 diagonalsA[2] =
788 {
789 (finderPatternA.corners()[2] - finderPatternA.corners()[0]).normalizedOrZero(),
790 (finderPatternA.corners()[3] - finderPatternA.corners()[1]).normalizedOrZero()
791 };
792
793 unsigned int diagonalEdgeA = (unsigned int)(-1);
794 unsigned int diagonalEdgeB = (unsigned int)(-1);
795
796 if (Numeric::abs(diagonalsA[0] * directionAB) >= angleThreshold)
797 {
798 diagonalEdgeA = 0u;
799 }
800 else if (Numeric::abs(diagonalsA[1] * directionAB) >= angleThreshold)
801 {
802 diagonalEdgeA = 1u;
803 }
804
805 if (diagonalEdgeA >= 2u)
806 {
807 return false;
808 }
809
810 const Vector2 diagonalsB[2] =
811 {
812 (finderPatternB.corners()[2] - finderPatternB.corners()[0]).normalizedOrZero(),
813 (finderPatternB.corners()[3] - finderPatternB.corners()[1]).normalizedOrZero()
814 };
815
816 if (Numeric::abs(diagonalsB[0] * directionAB) >= angleThreshold)
817 {
818 diagonalEdgeB = 0u;
819 }
820 else if (Numeric::abs(diagonalsB[1] * directionAB) >= angleThreshold)
821 {
822 diagonalEdgeB = 1u;
823 }
824
825 return diagonalEdgeA < 2u && diagonalEdgeB < 2u;
826}
827
828} // namespace QRCodes
829
830} // namespace Detector
831
832} // namespace CV
833
834} // namespace Ocean
This class implements a simple history for previous pixel transitions (a sliding window of pixel tran...
Definition FinderPatternDetector.h:215
int history2()
Returns the history with window size N.
Definition FinderPatternDetector.h:569
int history3()
Returns the history with window size N.
Definition FinderPatternDetector.h:574
void push(const int newDelta)
Adds a new delta object as most recent history.
Definition FinderPatternDetector.h:589
void reset()
Resets the history object.
Definition FinderPatternDetector.h:598
int history5()
Returns the history with window size N.
Definition FinderPatternDetector.h:584
TransitionHistory()
Creates a new history object.
Definition FinderPatternDetector.h:558
int history4()
Returns the history with window size N.
Definition FinderPatternDetector.h:579
int history1()
Returns the history with window size N.
Definition FinderPatternDetector.h:564
This class implements a detector for finder patterns which are part of QR Codes.
Definition FinderPatternDetector.h:205
static bool isDiagonal(const FinderPattern &finderPatternA, const FinderPattern &finderPatternB, const Scalar angleTolerance=Numeric::deg2rad(9))
Returns true if a pair of finder patterns is in a diagonal configuration, i.e.
Definition FinderPatternDetector.h:778
static constexpr int deltaThreshold
The intensity threshold between two successive pixels to count as a transition from dark to light (or...
Definition FinderPatternDetector.h:209
static bool checkFinderPatternInNeighborhood(const uint8_t *const yFrame, const unsigned width, const unsigned height, const unsigned int paddingElements, const unsigned int xCenter, const unsigned int yCenter, const unsigned int threshold, const unsigned int blackSquareSegmentMin, const unsigned int blackSquareSegmentMax, const unsigned int whiteSquareSegmentMin, const unsigned int whiteSquareSegmentMax, const unsigned int centerSegmentMin, const unsigned int centerSegmentMax, Scalar &symmetryScore, Vector2 *edgePoints)
Performs a check around a given candidate location looking for a correct configuration of light and d...
static bool refineFinderPatternLocation(const uint8_t *const yFrame, const unsigned int width, const unsigned int height, FinderPattern &finderPattern, const unsigned int yFramePaddingElements=0u)
Refine the location and corners of a finder pattern.
static IndexTriplets extractIndexTriplets(const FinderPatterns &finderPatterns, const Scalar distanceScaleTolerance=Scalar(0.175), const Scalar moduleSizeScaleTolerance=Scalar(0.35), const Scalar angleTolerance=Numeric::deg2rad(Scalar(9)))
Extract 3-tuples of finder patterns that form good (plausible) candidates for QR code symbols.
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 FinderPatternDetector.h:607
static bool checkFinderPatternDirectional(const uint8_t *const yFrame, const unsigned int width, const unsigned int height, const unsigned int paddingElements, const unsigned int xCenter, const unsigned int yCenter, const Scalar angle, const unsigned int threshold, const unsigned int blackSquareSegmentMin, const unsigned int blackSquareSegmentMax, const unsigned int whiteSquareSegmentMin, const unsigned int whiteSquareSegmentMax, const unsigned int centerSegmentMin, const unsigned int centerSegmentMax, Vector2 &topBorder, Vector2 &bottomBorder)
Performs a check for a given candidate location in a specified direction (yielding 2 edge points)
static FinderPatterns detectFinderPatterns(const uint8_t *const yFrame, const unsigned int width, const unsigned int height, const unsigned int minimumDistance=10u, const unsigned int paddingElements=0u, Worker *worker=nullptr)
Detects finder patterns of a QR code in a 8 bit grayscale image.
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.
Definition FinderPatternDetector.h:655
static bool estimateFinderPatternCorners(const unsigned int xCenter, const unsigned int yCenter, const Vector2 *edgePoints, const unsigned int edgePointsSize, Vector2 &location, Vector2 *corners, Vector2 &orientation, Scalar &moduleSize, const Scalar edgePointDistanceTolerance=2.25, const Scalar maxEdgeLineDistance=1.5)
Estimates the locations of the corners of finder pattern and computes the dominant orientation of the...
static bool isParallel(const FinderPattern &finderPatternA, const FinderPattern &finderPatternB, const Scalar distanceTolerance=Scalar(0.05))
Returns true if a pair of finder patterns is in parallel configuration, i.e., if one is above/below/l...
Definition FinderPatternDetector.h:701
static void detectFinderPatternsSubset(const uint8_t *const yFrame, const unsigned int width, const unsigned int height, FinderPatterns *finderPatterns, Lock *multiThreadLock, const unsigned int paddingElements, const unsigned int firstRow, const unsigned int numberRows)
Detects finder patterns of QR codes in subregion of a given 8 bit grayscale image.
static void detectFinderPatternInRow(const uint8_t *const yFrame, const unsigned int width, const unsigned int height, const unsigned int y, FinderPatterns &finderPatterns, const unsigned int paddingElements)
Detects finder patterns of QR codes in a single row of an grayscale image.
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 FinderPatternDetector.h:631
Definition of a class for finder patterns of QR codes (squares in the top-left, top-right and bottom-...
Definition FinderPatternDetector.h:58
const Vector2 * corners() const
Returns a pointer to the four corners of this finder pattern.
Definition FinderPatternDetector.h:530
Scalar symmetryScore() const
Returns the symmetry score that was determined when this finder pattern was detected.
Definition FinderPatternDetector.h:520
const Vector2 & orientation() const
Returns the dominant orientation of this finder pattern.
Definition FinderPatternDetector.h:542
Vector2 corners_[4]
The four corners of this finder pattern; points are stored in counter-clockwise order but no guarante...
Definition FinderPatternDetector.h:179
bool cornersKnown_
True if the four corners of this finder pattern are known, otherwise false.
Definition FinderPatternDetector.h:176
Scalar length() const
Returns the radius of the finder pattern.
Definition FinderPatternDetector.h:500
bool cornersKnown() const
Returns true if the four corners of this finder pattern are known, otherwise false.
Definition FinderPatternDetector.h:525
const Vector2 & position() const
Returns the (center) position of the finder pattern.
Definition FinderPatternDetector.h:495
unsigned int grayThreshold() const
Returns the threshold that was used for the detection of this finder pattern.
Definition FinderPatternDetector.h:510
Vector2 orientation_
Dominant orientation of this finder pattern.
Definition FinderPatternDetector.h:182
bool isNormalReflectance() const
Returns whether this finder pattern is of normal reflectance.
Definition FinderPatternDetector.h:548
static bool comesBefore(const FinderPattern &first, const FinderPattern &second)
Comparator to sort finder patterns based on their location in an image Pattern a comes before pattern...
Definition FinderPatternDetector.h:553
Scalar symmetryScore_
The symmetry score of this finder pattern, range: [0, infinity) (lower score = higher symmetry)
Definition FinderPatternDetector.h:173
Vector2 position_
The (center) position of the finder pattern within the camera frame.
Definition FinderPatternDetector.h:161
Scalar moduleSize_
Module width (bit width) in pixels.
Definition FinderPatternDetector.h:185
FinderPattern()
Creates an invalid finder pattern object.
Definition FinderPatternDetector.h:442
Scalar length_
The edge length of the finder pattern in pixels, range: (0, infinity).
Definition FinderPatternDetector.h:164
unsigned int grayThreshold_
The threshold that was used during the detection of this finder pattern.
Definition FinderPatternDetector.h:170
unsigned int centerIntensity_
The intensity value that has been measured in the center of the finder pattern.
Definition FinderPatternDetector.h:167
Scalar moduleSize() const
Returns the width of a module (= bit) in pixels.
Definition FinderPatternDetector.h:515
unsigned int centerIntensity() const
Returns the intensity value that was measured in the center of the finder pattern.
Definition FinderPatternDetector.h:505
static bool isBlack(const T &intensityValue, const T &threshold)
Determines whether an intensity value is black according to threshold value.
Definition TransitionDetector.h:181
This class implements an infinite line in 2D space.
Definition Line2.h:83
T sqrDistance(const VectorT2< T > &point) const
Returns the square distance between the line and a given point.
Definition Line2.h:498
This class implements a recursive lock object.
Definition Lock.h:31
This class provides basic numeric functionalities.
Definition Numeric.h:57
static constexpr T deg2rad(const T deg)
Converts deg to rad.
Definition Numeric.h:3232
static T abs(const T value)
Returns the absolute value of a given value.
Definition Numeric.h:1220
static constexpr bool isEqualEps(const T value)
Returns whether a value is smaller than or equal to a small epsilon.
Definition Numeric.h:2087
static T cos(const T value)
Returns the cosine of a given value.
Definition Numeric.h:1584
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
VectorT2< T > normalized() const
Returns the normalized vector.
Definition Vector2.h:570
VectorT2< T > normalizedOrZero() const
Returns the normalized vector.
Definition Vector2.h:584
T length() const
Returns the length of the vector.
Definition Vector2.h:627
This class implements a worker able to distribute function calls over different threads.
Definition Worker.h:33
unsigned int sqrDistance(const char first, const char second)
Returns the square distance between two values.
Definition base/Utilities.h:1089
std::array< FinderPattern, 3 > FinderPatternTriplet
Definition of a 3-tuple of finder patterns.
Definition FinderPatternDetector.h:198
std::vector< FinderPattern > FinderPatterns
Definition of a vector holding finder pattern.
Definition FinderPatternDetector.h:192
std::vector< IndexTriplet > IndexTriplets
Definition of a vector index triplets.
Definition FinderPatternDetector.h:51
std::array< unsigned int, 3 > IndexTriplet
Definition of a triplet of indices.
Definition FinderPatternDetector.h:45
float Scalar
Definition of a scalar type.
Definition Math.h:129
LineT2< Scalar > Line2
Definition of the Line2 object, depending on the OCEAN_MATH_USE_SINGLE_PRECISION either with single o...
Definition Line2.h:28
VectorT2< Scalar > Vector2
Definition of a 2D vector.
Definition Vector2.h:28
std::vector< QRCode > QRCodes
Definition of a vector of QR codes.
Definition QRCode.h:28
The namespace covering the entire Ocean framework.
Definition Accessor.h:15