Ocean
QRCodeEncoderBase.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 
11 
13 
14 #include "ocean/math/Vector2.h"
15 
16 #include <cctype>
17 
18 namespace Ocean
19 {
20 
21 namespace CV
22 {
23 
24 namespace Detector
25 {
26 
27 namespace QRCodes
28 {
29 
30 /**
31  * This class implements basic QRCodeEncoder functionality
32  * @ingroup cvdetectorqrcodes
33  */
34 class OCEAN_CV_DETECTOR_QRCODES_EXPORT QRCodeEncoderBase
35 {
36  public:
37 
38  /// Codeword: sequence of 8 bits
39  typedef uint8_t Codeword;
40 
41  /// Vector of codewords
42  typedef std::vector<Codeword> Codewords;
43 
44  /// Typedef for a bit buffer
45  typedef std::vector<uint8_t> BitBuffer;
46 
47  /// Forward declaration of the segment class
48  class Segment;
49 
50  /// Typedef for a vector of segments
51  typedef std::vector<Segment> Segments;
52 
53  /**
54  * Definition of the segment class
55  * A sequence is a sequence of data encoded according to the rules of one ECI or encodation mode
56  */
57  class OCEAN_CV_DETECTOR_QRCODES_EXPORT Segment
58  {
59  public:
60  /**
61  * Constructor for segments
62  * @param mode The data encodation mode of this segment
63  * @param characters The number of characters stored in this segment, range: [0, infinity)
64  * @param bitBuffer The bit sequence representing the data stored in this segment, must be valid
65  */
66  inline Segment(const QRCode::EncodingMode mode, const unsigned int characters, BitBuffer& bitBuffer);
67 
68  /**
69  * Returns the encodation mode set for this segment
70  * @return The encodation mode
71  */
72  inline QRCode::EncodingMode encodationMode() const;
73 
74  /**
75  * Returns the number of characters stored in this segment
76  * @return The number of characters stored in this segment
77  */
78  inline unsigned int characters() const;
79 
80  /**
81  * Returns the encoded bits stored in this segment
82  * @return The encoded bits
83  */
84  inline const BitBuffer& bitBuffer() const;
85 
86  /**
87  * Encode a sequence of digits (0-9) and store it in a segment
88  * @param data The data that will be encoded, must be a sequence of digits, i.e., `isNumeric(data) == true`
89  * @param segments The segment storing the encoded data will be appended to this vector, memory will be initialized internally
90  * @return True if the data was successfully encoded, otherwise false
91  */
92  static bool generateSegmentNumeric(const std::string& data, Segments& segments);
93 
94  /**
95  * Encode a sequence of alphanumeric characters (cf. ISO/IEC 18004:2015, Table 5) and store it in a segment
96  * @param data The data that will be encoded, must be a sequence of alphanumeric characters, i.e., `isAlphanumeric(data) == true`
97  * @param segments The segment storing the encoded data will be appended to this vector, memory will be initialized internally
98  * @return True if the data was successfully encoded, otherwise false
99  */
100  static bool generateSegmentAlphanumeric(const std::string& data, Segments& segments);
101 
102  /**
103  * Encode a sequence of bytes and store it in a segment
104  * @param data The data that will be encoded
105  * @param segments The segment storing the encoded data will be appended to this vector, memory will be initialized internally
106  * @return True if the data was successfully encoded, otherwise false
107  */
108  static bool generateSegmentsBytes(const std::vector<uint8_t>& data, Segments& segments);
109 
110  /**
111  * Helper function to append a certain number of bits of a number to a bit buffer
112  * @param value The value of which the first `N` bits will be appended to the bit buffer
113  * @param bits The number of bits to append, range: [0, sizeof(unsigned int) * 8]
114  * @param bitBuffer The bit buffer to which the `N` bits will be appended
115  */
116  static inline void bitBufferAppend(const unsigned int value, size_t bits, BitBuffer& bitBuffer);
117 
118  /**
119  * Test to check if data is numeric (consisting of only digits)
120  * @param data The data to be tested
121  * @return True if the data consists of only digits, otherwise false
122  */
123  static inline bool isNumericData(const std::string& data);
124 
125  /**
126  * Test to check if data contains only alphanumeric characters
127  * @param data The data to be tested
128  * @return True if the data consists of alphanumeric characters, otherwise false
129  * @sa getAlphanumericCharset()
130  */
131  static inline bool isAlphanumericData(const std::string& data);
132 
133  /**
134  * Returns the character set for the alphanumeric data mode
135  * The character set for the alphanumeric data mode, cf. ISO/IEC 18004:2015, Table 5. The index
136  * of each character in the string corresponds to the value assigned to them in the alphanumeric
137  * encoding/decoding table.
138  * @return The character set for the alphanumeric data mode.
139  */
140  static inline const std::string& getAlphanumericCharset();
141 
142  protected:
143 
144  /// The mode used to encode the data of this segment
146 
147  /// The number of characters stored in this segment
148  unsigned int characters_;
149 
150  /// The actual encoded data (sequence of bits)
152  };
153 
154  /**
155  * Definition of the ReedSolomon class
156  */
158  {
159  public:
160 
161  /// Coefficients of the divisor polynomial, stored from highest to lower power (excluding the leading term which is always 1). Example x^3 + 255x^2 + 8x + 93 is stored as {255, 8, 93}.
162  typedef std::vector<uint8_t> Coefficients;
163 
164  public:
165 
166  /**
167  * Generates the Reed-Solomon coefficients for a divisor polynomial of degree `N`.
168  * @param degree The degree of the divisor polynomial, range: [1, infinity)
169  * @return The coeffiencts of the divisor polynomial (will have `N = degree` elements)
170  */
171  static Coefficients generateCoefficients(const unsigned int degree);
172 
173  /**
174  * Computes the Reed-Solomon error correction codewords for a sequence of data codewords
175  * @param codewords The input codewords for which the error correction will be computed
176  * @param coefficients The coefficients of the Reed-Solomon divisor polynomial of degree `N`
177  * @return The error correction codewords
178  */
179  static Codewords computeRemainders(const Codewords& codewords, const Coefficients& coefficients);
180 
181  protected:
182 
183  /**
184  * Return the product of two fields modulo GF(2^8/0x11D)
185  */
186  static uint8_t multiply(const uint8_t a, const uint8_t b);
187  };
188 
189  /**
190  * Computes the remainder of a polynomial long division for (n, k) BCH codes
191  * Notation:
192  * n : block length in bits
193  * k : number of information/data bits
194  * @param data The data used as the nominator in this division
195  * @return The remainder of the polynomial division
196  * @tparam tBlockLength The length, n, of the BCH code in bits, range: (tDataLength, infinity)
197  * @tparam tDataLength The number the information/data bits, k, range: [1, tBlockLength)
198  * @tparam tGeneratorPolynomial The generator polynomial used by this code for the polynomial division. This is specificied as an integer (or binary number). Range: (0, 2^n - 1) (Important: make sure this value is correct according to the Galois field theory behind it, there are no additional sanity checks)
199  */
200  template <uint32_t tBlockLength, uint32_t tDataLength, uint32_t tGeneratorPolynomial>
201  static inline uint32_t computePolynomialDivisonRemainderBCH(const uint32_t data);
202 
203  /**
204  * Determines the number of 1-bits in an integer value (Hamming weight)
205  * @param value The value of which the 1-bits will be counted
206  * @return The number of 1-bits in the input value
207  */
208  static inline uint32_t computeHammingWeight(uint32_t value);
209 };
210 
211 inline QRCodeEncoderBase::Segment::Segment(const QRCode::EncodingMode mode, const unsigned int characters, BitBuffer& bitBuffer) :
212  encodationMode_(mode),
213  characters_(characters),
214  bitBuffer_(bitBuffer)
215 {
216  // Nothing else to do.
217 }
218 
220 {
221  return encodationMode_;
222 }
223 
224 inline unsigned int QRCodeEncoderBase::Segment::characters() const
225 {
226  return characters_;
227 }
228 
230 {
231  return bitBuffer_;
232 }
233 
234 inline bool QRCodeEncoderBase::Segment::isNumericData(const std::string& data)
235 {
236  for (size_t i = 0; i < data.size(); ++i)
237  {
238  if (std::isdigit((unsigned char)data[i]) == false)
239  {
240  return false;
241  }
242  }
243 
244  return data.size() != 0;
245 }
246 
247 inline bool QRCodeEncoderBase::Segment::isAlphanumericData(const std::string& data)
248 {
249  if (data.empty())
250  {
251  return false;
252  }
253 
254  const std::string& alphanumericCharset = getAlphanumericCharset();
255 
256  for (const char character : data)
257  {
258  if (alphanumericCharset.find(character) == std::string::npos)
259  {
260  return false;
261  }
262  }
263 
264  return true;
265 }
266 
268 {
269  const static std::string alphanumericCharset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
270  ocean_assert(alphanumericCharset.size() == 45);
271 
272  return alphanumericCharset;
273 }
274 
275 inline void QRCodeEncoderBase::Segment::bitBufferAppend(const unsigned int value, size_t bits, BitBuffer& bitBuffer)
276 {
277  ocean_assert(bits <= sizeof(unsigned int) * 8);
278 
279  if (bits == 0u)
280  {
281  return;
282  }
283 
284  for (size_t i = bits - 1; i < bits; --i)
285  {
286  bitBuffer.push_back((value >> i) & 1);
287  }
288 }
289 
290 template <uint32_t tBlockLength, uint32_t tDataLength, uint32_t tGeneratorPolynomial>
292 {
293  static_assert(tBlockLength != 0u && tDataLength != 0u && tBlockLength > tDataLength, "The block length must be larger than the number of data bits and both must be nonzero.");
294  static_assert(tGeneratorPolynomial != 0u && tGeneratorPolynomial >> tBlockLength == 0u, "The generator polynomial cannot be zero and must fit into the block length");
295  ocean_assert(data >> tBlockLength == 0u);
296 
297  // Example for a (15, 5) BCH code:
298  //
299  // * block length: 15
300  // * data length: 5
301  // * generator polynomial = 10100110111 ~ G(x) = x^10 + x^8 + x^5 + x^4 + x^2 + x + 1
302  // * data = 000111101011001, i.e. 00011|1101011001
303  // data | error correction
304  //
305  // remainder0 = 000111101011001
306  // ^ 10100110111 skip step - left-most bit of the remainder is zero
307  // -----------------
308  // remainder1 = 000111101011001
309  // ^ 10100110111 skip step - left-most bit of the remainder is zero
310  // -----------------
311  // remainder2 = 000111101011001
312  // ^ 10100110111 skip step - left-most bit of the remainder is zero
313  // -----------------
314  // remainder3 = 000111101011001
315  // ^ 10100110111
316  // -----------------
317  // remainder4 = 000010100110111
318  // ^ 10100110111
319  // -----------------
320  // remainder = 000000000000000
321 
322  constexpr uint32_t errorCorrectionBits = tBlockLength - tDataLength;
323 
324  uint32_t remainder = data;
325 
326  for (uint32_t i = 0u; i < tDataLength; ++i)
327  {
328  ocean_assert(tDataLength >= i + 1u);
329  const uint32_t shift = tDataLength - i - 1u;
330 
331  if (remainder & (1u << (shift + errorCorrectionBits)))
332  {
333  remainder ^= tGeneratorPolynomial << shift;
334  }
335  }
336 
337  ocean_assert(tGeneratorPolynomial > remainder);
338  return remainder;
339 }
340 
341 inline uint32_t QRCodeEncoderBase::computeHammingWeight(uint32_t value)
342 {
343  uint32_t weight = 0u;
344 
345  while (value > 0u)
346  {
347  value = value & (value - 1u);
348  weight += 1u;
349  }
350 
351  return weight;
352 }
353 
354 } // namespace QRCodes
355 
356 } // namespace Detector
357 
358 } // namespace CV
359 
360 } // namespace Ocean
EncodingMode
Definition of encoding modes.
Definition: QRCodeBase.h:72
Definition of the ReedSolomon class.
Definition: QRCodeEncoderBase.h:158
std::vector< uint8_t > Coefficients
Coefficients of the divisor polynomial, stored from highest to lower power (excluding the leading ter...
Definition: QRCodeEncoderBase.h:162
static Coefficients generateCoefficients(const unsigned int degree)
Generates the Reed-Solomon coefficients for a divisor polynomial of degree N.
static Codewords computeRemainders(const Codewords &codewords, const Coefficients &coefficients)
Computes the Reed-Solomon error correction codewords for a sequence of data codewords.
static uint8_t multiply(const uint8_t a, const uint8_t b)
Return the product of two fields modulo GF(2^8/0x11D)
Definition of the segment class A sequence is a sequence of data encoded according to the rules of on...
Definition: QRCodeEncoderBase.h:58
static bool isAlphanumericData(const std::string &data)
Test to check if data contains only alphanumeric characters.
Definition: QRCodeEncoderBase.h:247
static bool generateSegmentsBytes(const std::vector< uint8_t > &data, Segments &segments)
Encode a sequence of bytes and store it in a segment.
static bool generateSegmentAlphanumeric(const std::string &data, Segments &segments)
Encode a sequence of alphanumeric characters (cf.
QRCode::EncodingMode encodationMode() const
Returns the encodation mode set for this segment.
Definition: QRCodeEncoderBase.h:219
static bool isNumericData(const std::string &data)
Test to check if data is numeric (consisting of only digits)
Definition: QRCodeEncoderBase.h:234
unsigned int characters_
The number of characters stored in this segment.
Definition: QRCodeEncoderBase.h:148
QRCode::EncodingMode encodationMode_
The mode used to encode the data of this segment.
Definition: QRCodeEncoderBase.h:145
static void bitBufferAppend(const unsigned int value, size_t bits, BitBuffer &bitBuffer)
Helper function to append a certain number of bits of a number to a bit buffer.
Definition: QRCodeEncoderBase.h:275
BitBuffer bitBuffer_
The actual encoded data (sequence of bits)
Definition: QRCodeEncoderBase.h:151
unsigned int characters() const
Returns the number of characters stored in this segment.
Definition: QRCodeEncoderBase.h:224
Segment(const QRCode::EncodingMode mode, const unsigned int characters, BitBuffer &bitBuffer)
Constructor for segments.
Definition: QRCodeEncoderBase.h:211
const BitBuffer & bitBuffer() const
Returns the encoded bits stored in this segment.
Definition: QRCodeEncoderBase.h:229
static bool generateSegmentNumeric(const std::string &data, Segments &segments)
Encode a sequence of digits (0-9) and store it in a segment.
static const std::string & getAlphanumericCharset()
Returns the character set for the alphanumeric data mode The character set for the alphanumeric data ...
Definition: QRCodeEncoderBase.h:267
This class implements basic QRCodeEncoder functionality.
Definition: QRCodeEncoderBase.h:35
static uint32_t computePolynomialDivisonRemainderBCH(const uint32_t data)
Computes the remainder of a polynomial long division for (n, k) BCH codes Notation: n : block length ...
Definition: QRCodeEncoderBase.h:291
std::vector< uint8_t > BitBuffer
Typedef for a bit buffer.
Definition: QRCodeEncoderBase.h:45
static uint32_t computeHammingWeight(uint32_t value)
Determines the number of 1-bits in an integer value (Hamming weight)
Definition: QRCodeEncoderBase.h:341
std::vector< Segment > Segments
Typedef for a vector of segments.
Definition: QRCodeEncoderBase.h:48
uint8_t Codeword
Codeword: sequence of 8 bits.
Definition: QRCodeEncoderBase.h:39
std::vector< Codeword > Codewords
Vector of codewords.
Definition: QRCodeEncoderBase.h:42
std::vector< QRCode > QRCodes
Definition of a vector of QR codes.
Definition: QRCode.h:25
The namespace covering the entire Ocean framework.
Definition: Accessor.h:15