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  * Enum for the error codes returned by the encoding functions
55  */
56  enum StatusCode : uint32_t
57  {
58  /// The encoding was successful
59  SC_SUCCESS = 0u,
60 
61  /// The data given exceeds the maximum capacity of the QR code
63 
64  /// The data given is not valid
66 
67  // The error correction level is not valid
69 
70  /// The encoding failed due to an unknown reason
71  SC_UNKNOWN_ERROR
72  };
73 
74  /**
75  * Definition of the segment class
76  * A sequence is a sequence of data encoded according to the rules of one ECI or encodation mode
77  */
78  class OCEAN_CV_DETECTOR_QRCODES_EXPORT Segment
79  {
80  public:
81  /**
82  * Constructor for segments
83  * @param mode The data encodation mode of this segment
84  * @param characters The number of characters stored in this segment, range: [0, infinity)
85  * @param bitBuffer The bit sequence representing the data stored in this segment, must be valid
86  */
87  inline Segment(const QRCode::EncodingMode mode, const unsigned int characters, BitBuffer& bitBuffer);
88 
89  /**
90  * Returns the encodation mode set for this segment
91  * @return The encodation mode
92  */
93  inline QRCode::EncodingMode encodationMode() const;
94 
95  /**
96  * Returns the number of characters stored in this segment
97  * @return The number of characters stored in this segment
98  */
99  inline unsigned int characters() const;
100 
101  /**
102  * Returns the encoded bits stored in this segment
103  * @return The encoded bits
104  */
105  inline const BitBuffer& bitBuffer() const;
106 
107  /**
108  * Encode a sequence of digits (0-9) and store it in a segment
109  * @param data The data that will be encoded, must be a sequence of digits, i.e., `isNumeric(data) == true`
110  * @param segments The segment storing the encoded data will be appended to this vector, memory will be initialized internally
111  * @return True if the data was successfully encoded, otherwise false
112  */
113  static StatusCode generateSegmentNumeric(const std::string& data, Segments& segments);
114 
115  /**
116  * Encode a sequence of alphanumeric characters (cf. ISO/IEC 18004:2015, Table 5) and store it in a segment
117  * @param data The data that will be encoded, must be a sequence of alphanumeric characters, i.e., `isAlphanumeric(data) == true`
118  * @param segments The segment storing the encoded data will be appended to this vector, memory will be initialized internally
119  * @return True if the data was successfully encoded, otherwise false
120  */
121  static StatusCode generateSegmentAlphanumeric(const std::string& data, Segments& segments);
122 
123  /**
124  * Encode a sequence of bytes and store it in a segment
125  * @param data The data that will be encoded
126  * @param segments The segment storing the encoded data will be appended to this vector, memory will be initialized internally
127  * @return True if the data was successfully encoded, otherwise false
128  */
129  static StatusCode generateSegmentsBytes(const std::vector<uint8_t>& data, Segments& segments);
130 
131  /**
132  * Helper function to append a certain number of bits of a number to a bit buffer
133  * @param value The value of which the first `N` bits will be appended to the bit buffer
134  * @param bits The number of bits to append, range: [0, sizeof(unsigned int) * 8]
135  * @param bitBuffer The bit buffer to which the `N` bits will be appended
136  */
137  static inline void bitBufferAppend(const unsigned int value, size_t bits, BitBuffer& bitBuffer);
138 
139  /**
140  * Test to check if data is numeric (consisting of only digits)
141  * @param data The data to be tested
142  * @return True if the data consists of only digits, otherwise false
143  */
144  static inline bool isNumericData(const std::string& data);
145 
146  /**
147  * Test to check if data contains only alphanumeric characters
148  * @param data The data to be tested
149  * @return True if the data consists of alphanumeric characters, otherwise false
150  * @sa getAlphanumericCharset()
151  */
152  static inline bool isAlphanumericData(const std::string& data);
153 
154  /**
155  * Returns the character set for the alphanumeric data mode
156  * The character set for the alphanumeric data mode, cf. ISO/IEC 18004:2015, Table 5. The index
157  * of each character in the string corresponds to the value assigned to them in the alphanumeric
158  * encoding/decoding table.
159  * @return The character set for the alphanumeric data mode.
160  */
161  static inline const std::string& getAlphanumericCharset();
162 
163  protected:
164 
165  /// The mode used to encode the data of this segment
167 
168  /// The number of characters stored in this segment
169  unsigned int characters_;
170 
171  /// The actual encoded data (sequence of bits)
173  };
174 
175  /**
176  * Definition of the ReedSolomon class
177  */
179  {
180  public:
181 
182  /// 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}.
183  typedef std::vector<uint8_t> Coefficients;
184 
185  public:
186 
187  /**
188  * Generates the Reed-Solomon coefficients for a divisor polynomial of degree `N`.
189  * @param degree The degree of the divisor polynomial, range: [1, infinity)
190  * @return The coeffiencts of the divisor polynomial (will have `N = degree` elements)
191  */
192  static Coefficients generateCoefficients(const unsigned int degree);
193 
194  /**
195  * Computes the Reed-Solomon error correction codewords for a sequence of data codewords
196  * @param codewords The input codewords for which the error correction will be computed
197  * @param coefficients The coefficients of the Reed-Solomon divisor polynomial of degree `N`
198  * @return The error correction codewords
199  */
200  static Codewords computeRemainders(const Codewords& codewords, const Coefficients& coefficients);
201 
202  protected:
203 
204  /**
205  * Return the product of two fields modulo GF(2^8/0x11D)
206  */
207  static uint8_t multiply(const uint8_t a, const uint8_t b);
208  };
209 
210  /**
211  * Computes the remainder of a polynomial long division for (n, k) BCH codes
212  * Notation:
213  * n : block length in bits
214  * k : number of information/data bits
215  * @param data The data used as the nominator in this division
216  * @return The remainder of the polynomial division
217  * @tparam tBlockLength The length, n, of the BCH code in bits, range: (tDataLength, infinity)
218  * @tparam tDataLength The number the information/data bits, k, range: [1, tBlockLength)
219  * @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)
220  */
221  template <uint32_t tBlockLength, uint32_t tDataLength, uint32_t tGeneratorPolynomial>
222  static inline uint32_t computePolynomialDivisonRemainderBCH(const uint32_t data);
223 
224  /**
225  * Determines the number of 1-bits in an integer value (Hamming weight)
226  * @param value The value of which the 1-bits will be counted
227  * @return The number of 1-bits in the input value
228  */
229  static inline uint32_t computeHammingWeight(uint32_t value);
230 
231  /**
232  * Translates the given status code into a human readable string
233  */
234  static inline std::string translateStatusCode(const StatusCode statusCode);
235 };
236 
237 inline QRCodeEncoderBase::Segment::Segment(const QRCode::EncodingMode mode, const unsigned int characters, BitBuffer& bitBuffer) :
238  encodationMode_(mode),
239  characters_(characters),
240  bitBuffer_(bitBuffer)
241 {
242  // Nothing else to do.
243 }
244 
246 {
247  return encodationMode_;
248 }
249 
250 inline unsigned int QRCodeEncoderBase::Segment::characters() const
251 {
252  return characters_;
253 }
254 
256 {
257  return bitBuffer_;
258 }
259 
260 inline bool QRCodeEncoderBase::Segment::isNumericData(const std::string& data)
261 {
262  for (size_t i = 0; i < data.size(); ++i)
263  {
264  if (std::isdigit((unsigned char)data[i]) == false)
265  {
266  return false;
267  }
268  }
269 
270  return data.size() != 0;
271 }
272 
273 inline bool QRCodeEncoderBase::Segment::isAlphanumericData(const std::string& data)
274 {
275  if (data.empty())
276  {
277  return false;
278  }
279 
280  const std::string& alphanumericCharset = getAlphanumericCharset();
281 
282  for (const char character : data)
283  {
284  if (alphanumericCharset.find(character) == std::string::npos)
285  {
286  return false;
287  }
288  }
289 
290  return true;
291 }
292 
294 {
295  const static std::string alphanumericCharset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
296  ocean_assert(alphanumericCharset.size() == 45);
297 
298  return alphanumericCharset;
299 }
300 
301 inline void QRCodeEncoderBase::Segment::bitBufferAppend(const unsigned int value, size_t bits, BitBuffer& bitBuffer)
302 {
303  ocean_assert(bits <= sizeof(unsigned int) * 8);
304 
305  if (bits == 0u)
306  {
307  return;
308  }
309 
310  for (size_t i = bits - 1; i < bits; --i)
311  {
312  bitBuffer.push_back((value >> i) & 1);
313  }
314 }
315 
316 template <uint32_t tBlockLength, uint32_t tDataLength, uint32_t tGeneratorPolynomial>
318 {
319  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.");
320  static_assert(tGeneratorPolynomial != 0u && tGeneratorPolynomial >> tBlockLength == 0u, "The generator polynomial cannot be zero and must fit into the block length");
321  ocean_assert(data >> tBlockLength == 0u);
322 
323  // Example for a (15, 5) BCH code:
324  //
325  // * block length: 15
326  // * data length: 5
327  // * generator polynomial = 10100110111 ~ G(x) = x^10 + x^8 + x^5 + x^4 + x^2 + x + 1
328  // * data = 000111101011001, i.e. 00011|1101011001
329  // data | error correction
330  //
331  // remainder0 = 000111101011001
332  // ^ 10100110111 skip step - left-most bit of the remainder is zero
333  // -----------------
334  // remainder1 = 000111101011001
335  // ^ 10100110111 skip step - left-most bit of the remainder is zero
336  // -----------------
337  // remainder2 = 000111101011001
338  // ^ 10100110111 skip step - left-most bit of the remainder is zero
339  // -----------------
340  // remainder3 = 000111101011001
341  // ^ 10100110111
342  // -----------------
343  // remainder4 = 000010100110111
344  // ^ 10100110111
345  // -----------------
346  // remainder = 000000000000000
347 
348  constexpr uint32_t errorCorrectionBits = tBlockLength - tDataLength;
349 
350  uint32_t remainder = data;
351 
352  for (uint32_t i = 0u; i < tDataLength; ++i)
353  {
354  ocean_assert(tDataLength >= i + 1u);
355  const uint32_t shift = tDataLength - i - 1u;
356 
357  if (remainder & (1u << (shift + errorCorrectionBits)))
358  {
359  remainder ^= tGeneratorPolynomial << shift;
360  }
361  }
362 
363  ocean_assert(tGeneratorPolynomial > remainder);
364  return remainder;
365 }
366 
367 inline uint32_t QRCodeEncoderBase::computeHammingWeight(uint32_t value)
368 {
369  uint32_t weight = 0u;
370 
371  while (value > 0u)
372  {
373  value = value & (value - 1u);
374  weight += 1u;
375  }
376 
377  return weight;
378 }
379 
381 {
382  switch (statusCode)
383  {
384  case SC_SUCCESS:
385  return "SUCCESS";
387  return "CODE_CAPACITY_EXCEEDED";
388  case SC_INVALID_DATA:
389  return "INVALID_DATA";
391  return "INVALID_ERROR_CORRECTION_CAPACITY";
392  case SC_UNKNOWN_ERROR:
393  return "UNKNOWN_ERROR";
394  }
395 
396  ocean_assert(false && "Never be here!");
397  return "UNKOWN";
398 }
399 
400 } // namespace QRCodes
401 
402 } // namespace Detector
403 
404 } // namespace CV
405 
406 } // namespace Ocean
EncodingMode
Definition of encoding modes.
Definition: QRCodeBase.h:72
Definition of the ReedSolomon class.
Definition: QRCodeEncoderBase.h:179
std::vector< uint8_t > Coefficients
Coefficients of the divisor polynomial, stored from highest to lower power (excluding the leading ter...
Definition: QRCodeEncoderBase.h:183
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:79
static bool isAlphanumericData(const std::string &data)
Test to check if data contains only alphanumeric characters.
Definition: QRCodeEncoderBase.h:273
static StatusCode generateSegmentNumeric(const std::string &data, Segments &segments)
Encode a sequence of digits (0-9) and store it in a segment.
static StatusCode generateSegmentsBytes(const std::vector< uint8_t > &data, Segments &segments)
Encode a sequence of bytes and store it in a segment.
QRCode::EncodingMode encodationMode() const
Returns the encodation mode set for this segment.
Definition: QRCodeEncoderBase.h:245
static bool isNumericData(const std::string &data)
Test to check if data is numeric (consisting of only digits)
Definition: QRCodeEncoderBase.h:260
unsigned int characters_
The number of characters stored in this segment.
Definition: QRCodeEncoderBase.h:169
QRCode::EncodingMode encodationMode_
The mode used to encode the data of this segment.
Definition: QRCodeEncoderBase.h:166
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:301
BitBuffer bitBuffer_
The actual encoded data (sequence of bits)
Definition: QRCodeEncoderBase.h:172
unsigned int characters() const
Returns the number of characters stored in this segment.
Definition: QRCodeEncoderBase.h:250
Segment(const QRCode::EncodingMode mode, const unsigned int characters, BitBuffer &bitBuffer)
Constructor for segments.
Definition: QRCodeEncoderBase.h:237
const BitBuffer & bitBuffer() const
Returns the encoded bits stored in this segment.
Definition: QRCodeEncoderBase.h:255
static const std::string & getAlphanumericCharset()
Returns the character set for the alphanumeric data mode The character set for the alphanumeric data ...
Definition: QRCodeEncoderBase.h:293
static StatusCode generateSegmentAlphanumeric(const std::string &data, Segments &segments)
Encode a sequence of alphanumeric characters (cf.
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:317
std::vector< uint8_t > BitBuffer
Typedef for a bit buffer.
Definition: QRCodeEncoderBase.h:45
static std::string translateStatusCode(const StatusCode statusCode)
Translates the given status code into a human readable string.
Definition: QRCodeEncoderBase.h:380
static uint32_t computeHammingWeight(uint32_t value)
Determines the number of 1-bits in an integer value (Hamming weight)
Definition: QRCodeEncoderBase.h:367
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
StatusCode
Enum for the error codes returned by the encoding functions.
Definition: QRCodeEncoderBase.h:57
@ SC_INVALID_ERROR_CORRECTION_CAPACITY
Definition: QRCodeEncoderBase.h:68
@ SC_CODE_CAPACITY_EXCEEDED
The data given exceeds the maximum capacity of the QR code.
Definition: QRCodeEncoderBase.h:62
@ SC_UNKNOWN_ERROR
The encoding failed due to an unknown reason.
Definition: QRCodeEncoderBase.h:71
@ SC_INVALID_DATA
The data given is not valid.
Definition: QRCodeEncoderBase.h:65
@ SC_SUCCESS
The encoding was successful.
Definition: QRCodeEncoderBase.h:59
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