From 08978e2c6cf3befb54320c9ca8ab33f7775e6ec4 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Fri, 4 Jun 2021 00:54:33 +0200 Subject: [PATCH 001/185] IWYU: another (failed) attempt to get usable output from iwyu As of version 0.16 it is still virtually unusable with our code base. I manually went through all the noise and picked what seemed to make sense. This fixes #232 but with future compiler upgrades this kind of error will resurface. --- core/src/BarcodeFormat.cpp | 1 - core/src/BarcodeFormat.h | 1 - core/src/BitArray.h | 1 + core/src/CharacterSetECI.cpp | 1 - core/src/DecodeHints.h | 3 ++- core/src/GenericGF.h | 2 -- core/src/GlobalHistogramBinarizer.cpp | 1 - core/src/HybridBinarizer.cpp | 3 --- core/src/PerspectiveTransform.h | 2 -- core/src/ReadBarcode.cpp | 3 --- core/src/TextUtfEncoding.cpp | 1 - core/src/aztec/AZReader.cpp | 1 - core/src/datamatrix/DMReader.cpp | 2 -- core/src/oned/ODCodabarReader.cpp | 5 +---- core/src/oned/ODCode128Reader.cpp | 2 -- core/src/oned/ODCode39Reader.cpp | 2 -- core/src/oned/ODCode93Reader.cpp | 1 - core/src/oned/ODITFReader.cpp | 1 - core/src/oned/ODMultiUPCEANReader.cpp | 3 --- core/src/oned/ODReader.cpp | 1 - core/src/oned/ODRowReader.h | 2 +- core/src/oned/rss/ODRSSFieldParser.cpp | 2 ++ core/src/pdf417/PDFCodewordDecoder.cpp | 2 +- core/src/pdf417/PDFEncoder.cpp | 1 - core/src/pdf417/PDFModulusGF.h | 1 + core/src/pdf417/PDFReader.cpp | 11 +++++++---- core/src/pdf417/PDFScanningDecoder.cpp | 1 - core/src/qrcode/QRBitMatrixParser.cpp | 2 ++ core/src/qrcode/QRDataBlock.cpp | 1 - core/src/qrcode/QRDecoder.cpp | 2 +- core/src/qrcode/QRDetector.cpp | 7 ++++--- core/src/qrcode/QRMaskUtil.cpp | 1 + core/src/qrcode/QRMatrixUtil.cpp | 1 - core/src/qrcode/QRReader.cpp | 3 --- example/ZXingReader.cpp | 2 +- example/ZXingWriter.cpp | 3 --- test/blackbox/ImageLoader.cpp | 3 --- test/blackbox/TestReaderMain.cpp | 1 - test/blackbox/TestWriterMain.cpp | 1 - 39 files changed, 25 insertions(+), 59 deletions(-) diff --git a/core/src/BarcodeFormat.cpp b/core/src/BarcodeFormat.cpp index fd9ab7cad5..fddb6eface 100644 --- a/core/src/BarcodeFormat.cpp +++ b/core/src/BarcodeFormat.cpp @@ -17,7 +17,6 @@ #include "BarcodeFormat.h" -#include "BitHacks.h" #include "ZXContainerAlgorithms.h" #include diff --git a/core/src/BarcodeFormat.h b/core/src/BarcodeFormat.h index cc540c44dc..509e65282e 100644 --- a/core/src/BarcodeFormat.h +++ b/core/src/BarcodeFormat.h @@ -19,7 +19,6 @@ #include "Flags.h" #include -#include namespace ZXing { diff --git a/core/src/BitArray.h b/core/src/BitArray.h index 24a399c453..4de8fe14b4 100644 --- a/core/src/BitArray.h +++ b/core/src/BitArray.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include diff --git a/core/src/CharacterSetECI.cpp b/core/src/CharacterSetECI.cpp index be2b065372..6d6dbb5300 100644 --- a/core/src/CharacterSetECI.cpp +++ b/core/src/CharacterSetECI.cpp @@ -21,7 +21,6 @@ #include #include #include -#include namespace ZXing::CharacterSetECI { diff --git a/core/src/DecodeHints.h b/core/src/DecodeHints.h index 017c166769..7f0a2cd9e8 100644 --- a/core/src/DecodeHints.h +++ b/core/src/DecodeHints.h @@ -18,8 +18,9 @@ #include "BarcodeFormat.h" -#include #include +#include +#include namespace ZXing { diff --git a/core/src/GenericGF.h b/core/src/GenericGF.h index 489826c3b5..320f6234ab 100644 --- a/core/src/GenericGF.h +++ b/core/src/GenericGF.h @@ -19,8 +19,6 @@ #include "GenericGFPoly.h" #include "ZXConfig.h" -#include -#include #include #include diff --git a/core/src/GlobalHistogramBinarizer.cpp b/core/src/GlobalHistogramBinarizer.cpp index 1e3fd111b7..dbb4f766f0 100644 --- a/core/src/GlobalHistogramBinarizer.cpp +++ b/core/src/GlobalHistogramBinarizer.cpp @@ -17,7 +17,6 @@ #include "GlobalHistogramBinarizer.h" -#include "BitArray.h" #include "BitMatrix.h" #include "ByteArray.h" #include "LuminanceSource.h" diff --git a/core/src/HybridBinarizer.cpp b/core/src/HybridBinarizer.cpp index fa0162f99b..864ac44ea2 100644 --- a/core/src/HybridBinarizer.cpp +++ b/core/src/HybridBinarizer.cpp @@ -24,13 +24,10 @@ #include "Matrix.h" #include "ZXContainerAlgorithms.h" -#include #include -#include #include #include #include -#include namespace ZXing { diff --git a/core/src/PerspectiveTransform.h b/core/src/PerspectiveTransform.h index 75894ed3ce..b02ce66fe1 100644 --- a/core/src/PerspectiveTransform.h +++ b/core/src/PerspectiveTransform.h @@ -20,8 +20,6 @@ #include "Point.h" #include "Quadrilateral.h" -#include - namespace ZXing { /** diff --git a/core/src/ReadBarcode.cpp b/core/src/ReadBarcode.cpp index c96115a63c..81a0236e4d 100644 --- a/core/src/ReadBarcode.cpp +++ b/core/src/ReadBarcode.cpp @@ -16,9 +16,6 @@ #include "ReadBarcode.h" -#include "BinaryBitmap.h" -#include "BitArray.h" -#include "BitMatrix.h" #include "DecodeHints.h" #include "GenericLuminanceSource.h" #include "GlobalHistogramBinarizer.h" diff --git a/core/src/TextUtfEncoding.cpp b/core/src/TextUtfEncoding.cpp index 57674383f6..b2e3e1e4e7 100644 --- a/core/src/TextUtfEncoding.cpp +++ b/core/src/TextUtfEncoding.cpp @@ -19,7 +19,6 @@ #include #include #include -#include namespace ZXing::TextUtfEncoding { diff --git a/core/src/aztec/AZReader.cpp b/core/src/aztec/AZReader.cpp index 42b4d18cbf..287e26d134 100644 --- a/core/src/aztec/AZReader.cpp +++ b/core/src/aztec/AZReader.cpp @@ -27,7 +27,6 @@ #include #include -#include namespace ZXing::Aztec { diff --git a/core/src/datamatrix/DMReader.cpp b/core/src/datamatrix/DMReader.cpp index 4939f5f337..ee02ec408d 100644 --- a/core/src/datamatrix/DMReader.cpp +++ b/core/src/datamatrix/DMReader.cpp @@ -18,7 +18,6 @@ #include "DMReader.h" #include "BinaryBitmap.h" -#include "BitMatrix.h" #include "DMDecoder.h" #include "DMDetector.h" #include "DecodeHints.h" @@ -27,7 +26,6 @@ #include "Result.h" #include -#include namespace ZXing::DataMatrix { diff --git a/core/src/oned/ODCodabarReader.cpp b/core/src/oned/ODCodabarReader.cpp index f4ea0cd92f..0d6ebc33e6 100644 --- a/core/src/oned/ODCodabarReader.cpp +++ b/core/src/oned/ODCodabarReader.cpp @@ -18,15 +18,12 @@ #include "ODCodabarReader.h" -#include "BitArray.h" #include "DecodeHints.h" #include "Result.h" #include "ZXContainerAlgorithms.h" -#include -#include #include -#include +#include namespace ZXing::OneD { diff --git a/core/src/oned/ODCode128Reader.cpp b/core/src/oned/ODCode128Reader.cpp index a0276f92e4..0629a01857 100644 --- a/core/src/oned/ODCode128Reader.cpp +++ b/core/src/oned/ODCode128Reader.cpp @@ -17,14 +17,12 @@ #include "ODCode128Reader.h" -#include "BitArray.h" #include "DecodeHints.h" #include "ODCode128Patterns.h" #include "Result.h" #include "ZXContainerAlgorithms.h" #include -#include #include #include #include diff --git a/core/src/oned/ODCode39Reader.cpp b/core/src/oned/ODCode39Reader.cpp index 25ab91f043..926473278f 100644 --- a/core/src/oned/ODCode39Reader.cpp +++ b/core/src/oned/ODCode39Reader.cpp @@ -17,13 +17,11 @@ #include "ODCode39Reader.h" -#include "BitArray.h" #include "DecodeHints.h" #include "Result.h" #include "ZXContainerAlgorithms.h" #include -#include namespace ZXing::OneD { diff --git a/core/src/oned/ODCode93Reader.cpp b/core/src/oned/ODCode93Reader.cpp index b769f767ec..5926dfba18 100644 --- a/core/src/oned/ODCode93Reader.cpp +++ b/core/src/oned/ODCode93Reader.cpp @@ -18,7 +18,6 @@ #include "ODCode93Reader.h" -#include "BitArray.h" #include "Result.h" #include "ZXContainerAlgorithms.h" diff --git a/core/src/oned/ODITFReader.cpp b/core/src/oned/ODITFReader.cpp index f6fb1359af..42ddd592d9 100644 --- a/core/src/oned/ODITFReader.cpp +++ b/core/src/oned/ODITFReader.cpp @@ -17,7 +17,6 @@ #include "ODITFReader.h" -#include "BitArray.h" #include "DecodeHints.h" #include "Result.h" #include "ZXContainerAlgorithms.h" diff --git a/core/src/oned/ODMultiUPCEANReader.cpp b/core/src/oned/ODMultiUPCEANReader.cpp index eda63dd552..30ac3d5ea0 100644 --- a/core/src/oned/ODMultiUPCEANReader.cpp +++ b/core/src/oned/ODMultiUPCEANReader.cpp @@ -24,9 +24,6 @@ #include "GTIN.h" #include "ODUPCEANCommon.h" #include "Result.h" -#include "TextDecoder.h" - -#include namespace ZXing::OneD { diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index fd96babce4..3bdd923361 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -19,7 +19,6 @@ #include "ODReader.h" #include "BinaryBitmap.h" -#include "BitArray.h" #include "DecodeHints.h" #include "ODCodabarReader.h" #include "ODCode128Reader.h" diff --git a/core/src/oned/ODRowReader.h b/core/src/oned/ODRowReader.h index 57ad645fd4..ca12e55270 100644 --- a/core/src/oned/ODRowReader.h +++ b/core/src/oned/ODRowReader.h @@ -25,8 +25,8 @@ #include #include #include +#include #include -#include /* Code39 : 1:2/3, 5+4+1 (0x3|2x1 wide) -> 12-15 mods, v1-? | ToNarrowWide(OMG 1) == * diff --git a/core/src/oned/rss/ODRSSFieldParser.cpp b/core/src/oned/rss/ODRSSFieldParser.cpp index 9f0d2d4486..b15d6edc88 100644 --- a/core/src/oned/rss/ODRSSFieldParser.cpp +++ b/core/src/oned/rss/ODRSSFieldParser.cpp @@ -22,6 +22,8 @@ #include #include +#include +#include namespace ZXing::OneD::DataBar { diff --git a/core/src/pdf417/PDFCodewordDecoder.cpp b/core/src/pdf417/PDFCodewordDecoder.cpp index 9570422b39..ec84e962ac 100644 --- a/core/src/pdf417/PDFCodewordDecoder.cpp +++ b/core/src/pdf417/PDFCodewordDecoder.cpp @@ -19,9 +19,9 @@ #include "BitArray.h" #include "ZXContainerAlgorithms.h" +#include #include #include -#include #include namespace ZXing { diff --git a/core/src/pdf417/PDFEncoder.cpp b/core/src/pdf417/PDFEncoder.cpp index 662ff4f70f..ac4e1e4510 100644 --- a/core/src/pdf417/PDFEncoder.cpp +++ b/core/src/pdf417/PDFEncoder.cpp @@ -19,7 +19,6 @@ #include "PDFEncoder.h" #include "PDFHighLevelEncoder.h" #include -#include #include #include diff --git a/core/src/pdf417/PDFModulusGF.h b/core/src/pdf417/PDFModulusGF.h index 01630c8d21..12095c9491 100644 --- a/core/src/pdf417/PDFModulusGF.h +++ b/core/src/pdf417/PDFModulusGF.h @@ -20,6 +20,7 @@ #include "ZXConfig.h" #include +#include namespace ZXing { namespace Pdf417 { diff --git a/core/src/pdf417/PDFReader.cpp b/core/src/pdf417/PDFReader.cpp index 992c1a03a5..bec478ebf9 100644 --- a/core/src/pdf417/PDFReader.cpp +++ b/core/src/pdf417/PDFReader.cpp @@ -20,7 +20,6 @@ #include "PDFDetector.h" #include "PDFScanningDecoder.h" #include "PDFCodewordDecoder.h" -#include "PDFDecoderResultExtra.h" #include "DecodeHints.h" #include "DecoderResult.h" #include "Result.h" @@ -30,9 +29,6 @@ #include "BitArray.h" #include "DecodeStatus.h" #include "Pattern.h" -#include "PDFDecodedBitStreamParser.h" -#include "BitMatrixIO.h" -#include #include #include @@ -40,6 +36,13 @@ #include #include +#ifdef PRINT_DEBUG +#include "PDFDecoderResultExtra.h" +#include "PDFDecodedBitStreamParser.h" +#include "BitMatrixIO.h" +#include +#endif + namespace ZXing { namespace Pdf417 { diff --git a/core/src/pdf417/PDFScanningDecoder.cpp b/core/src/pdf417/PDFScanningDecoder.cpp index a56fe7d298..978b090829 100644 --- a/core/src/pdf417/PDFScanningDecoder.cpp +++ b/core/src/pdf417/PDFScanningDecoder.cpp @@ -32,7 +32,6 @@ #include "ZXTestSupport.h" #include -#include #include #include diff --git a/core/src/qrcode/QRBitMatrixParser.cpp b/core/src/qrcode/QRBitMatrixParser.cpp index 23c92f62df..ab599a7b67 100644 --- a/core/src/qrcode/QRBitMatrixParser.cpp +++ b/core/src/qrcode/QRBitMatrixParser.cpp @@ -24,6 +24,8 @@ #include "QRFormatInformation.h" #include "QRVersion.h" +#include + namespace ZXing::QRCode { static bool getBit(const BitMatrix& bitMatrix, int x, int y, bool mirrored) diff --git a/core/src/qrcode/QRDataBlock.cpp b/core/src/qrcode/QRDataBlock.cpp index 59b88b6b9e..7c04f5e760 100644 --- a/core/src/qrcode/QRDataBlock.cpp +++ b/core/src/qrcode/QRDataBlock.cpp @@ -17,7 +17,6 @@ #include "QRDataBlock.h" -#include "DecodeStatus.h" #include "QRErrorCorrectionLevel.h" #include "QRVersion.h" #include "ZXContainerAlgorithms.h" diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index a31b6f023c..0b03a4dec6 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -27,10 +27,10 @@ #include "QRBitMatrixParser.h" #include "QRCodecMode.h" #include "QRDataBlock.h" -#include "QRDataMask.h" #include "QRDecoderMetadata.h" #include "QRFormatInformation.h" #include "ReedSolomonDecoder.h" +#include "StructuredAppend.h" #include "TextDecoder.h" #include "ZXContainerAlgorithms.h" #include "ZXTestSupport.h" diff --git a/core/src/qrcode/QRDetector.cpp b/core/src/qrcode/QRDetector.cpp index f0953b87c6..34581dc323 100644 --- a/core/src/qrcode/QRDetector.cpp +++ b/core/src/qrcode/QRDetector.cpp @@ -24,8 +24,8 @@ #include "DetectorResult.h" #include "GridSampler.h" #include "LogMatrix.h" -#include "PerspectiveTransform.h" -#include "QRVersion.h" +#include "Pattern.h" +#include "Quadrilateral.h" #include "RegressionLine.h" #include "BitMatrixIO.h" @@ -33,9 +33,10 @@ #include #include #include -#include +#include #include #include +#include namespace ZXing::QRCode { diff --git a/core/src/qrcode/QRMaskUtil.cpp b/core/src/qrcode/QRMaskUtil.cpp index 12d1a8a2f0..2f327e4f2a 100644 --- a/core/src/qrcode/QRMaskUtil.cpp +++ b/core/src/qrcode/QRMaskUtil.cpp @@ -21,6 +21,7 @@ #include #include #include +#include namespace ZXing::QRCode::MaskUtil { diff --git a/core/src/qrcode/QRMatrixUtil.cpp b/core/src/qrcode/QRMatrixUtil.cpp index e248a8dc3f..f937a08f5b 100644 --- a/core/src/qrcode/QRMatrixUtil.cpp +++ b/core/src/qrcode/QRMatrixUtil.cpp @@ -23,7 +23,6 @@ #include "QRErrorCorrectionLevel.h" #include "QRVersion.h" -#include #include #include diff --git a/core/src/qrcode/QRReader.cpp b/core/src/qrcode/QRReader.cpp index 7ce9180b1b..208e1222b5 100644 --- a/core/src/qrcode/QRReader.cpp +++ b/core/src/qrcode/QRReader.cpp @@ -18,15 +18,12 @@ #include "QRReader.h" #include "BinaryBitmap.h" -#include "BitMatrix.h" #include "DecodeHints.h" #include "DecoderResult.h" #include "DetectorResult.h" #include "QRDecoder.h" -#include "QRDecoderMetadata.h" #include "QRDetector.h" #include "Result.h" -#include "ResultPoint.h" #include diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index a16068155a..7e2c5066c7 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -18,12 +18,12 @@ #include "TextUtfEncoding.h" #include "GTIN.h" -#include #include #include #include #include #include +#include #include #include diff --git a/example/ZXingWriter.cpp b/example/ZXingWriter.cpp index b038365ee5..060b342712 100644 --- a/example/ZXingWriter.cpp +++ b/example/ZXingWriter.cpp @@ -16,16 +16,13 @@ #include "BarcodeFormat.h" #include "BitMatrix.h" -#include "ByteMatrix.h" #include "MultiFormatWriter.h" #include "TextUtfEncoding.h" #include #include -#include #include #include -#include #include #define STB_IMAGE_WRITE_IMPLEMENTATION diff --git a/test/blackbox/ImageLoader.cpp b/test/blackbox/ImageLoader.cpp index 2375c978d2..b02f469f2b 100644 --- a/test/blackbox/ImageLoader.cpp +++ b/test/blackbox/ImageLoader.cpp @@ -18,10 +18,7 @@ #include "ImageLoader.h" #include "BinaryBitmap.h" -#include "GenericLuminanceSource.h" -#include "HybridBinarizer.h" #include "ReadBarcode.h" -#include "ThresholdBinarizer.h" #include #include diff --git a/test/blackbox/TestReaderMain.cpp b/test/blackbox/TestReaderMain.cpp index 4efc7b566e..cabc70267f 100644 --- a/test/blackbox/TestReaderMain.cpp +++ b/test/blackbox/TestReaderMain.cpp @@ -16,7 +16,6 @@ */ #include "BlackboxTestRunner.h" -#include "ByteArray.h" #include "ImageLoader.h" #include "TextUtfEncoding.h" #include "ZXContainerAlgorithms.h" diff --git a/test/blackbox/TestWriterMain.cpp b/test/blackbox/TestWriterMain.cpp index 041f7c2645..d3e84ae9a9 100644 --- a/test/blackbox/TestWriterMain.cpp +++ b/test/blackbox/TestWriterMain.cpp @@ -16,7 +16,6 @@ */ #include "BitMatrix.h" -#include "ByteMatrix.h" #include "MultiFormatWriter.h" #include From cad4aa216303d0244326993b63793de0e97c95b1 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Sat, 10 Jul 2021 15:57:27 +0200 Subject: [PATCH 002/185] example: add missing '-encoding' command line parameter in ZXingWriter This fixes #231. --- example/ZXingWriter.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/example/ZXingWriter.cpp b/example/ZXingWriter.cpp index 060b342712..dccfd6b2bc 100644 --- a/example/ZXingWriter.cpp +++ b/example/ZXingWriter.cpp @@ -18,6 +18,7 @@ #include "BitMatrix.h" #include "MultiFormatWriter.h" #include "TextUtfEncoding.h" +#include "CharacterSetECI.h" #include #include @@ -57,7 +58,8 @@ static bool ParseSize(std::string str, int* width, int* height) return false; } -static bool ParseOptions(int argc, char* argv[], int* width, int* height, int* margin, int* eccLevel, BarcodeFormat* format, std::string* text, std::string* filePath) +static bool ParseOptions(int argc, char* argv[], int* width, int* height, int* margin, CharacterSet* encoding, + int* eccLevel, BarcodeFormat* format, std::string* text, std::string* filePath) { int nonOptArgCount = 0; for (int i = 1; i < argc; ++i) { @@ -79,6 +81,11 @@ static bool ParseOptions(int argc, char* argv[], int* width, int* height, int* m return false; *eccLevel = std::stoi(argv[i]); } + else if (strcmp(argv[i], "-encoding") == 0) { + if (++i == argc) + return false; + *encoding = CharacterSetECI::CharsetFromName(argv[i]); + } else if (nonOptArgCount == 0) { *format = BarcodeFormatFromString(argv[i]); if (*format == BarcodeFormat::None) { @@ -118,16 +125,17 @@ int main(int argc, char* argv[]) int width = 100, height = 100; int margin = 10; int eccLevel = -1; + CharacterSet encoding = CharacterSet::Unknown; std::string text, filePath; BarcodeFormat format; - if (!ParseOptions(argc, argv, &width, &height, &margin, &eccLevel, &format, &text, &filePath)) { + if (!ParseOptions(argc, argv, &width, &height, &margin, &encoding, &eccLevel, &format, &text, &filePath)) { PrintUsage(argv[0]); return -1; } try { - auto writer = MultiFormatWriter(format).setMargin(margin).setEccLevel(eccLevel); + auto writer = MultiFormatWriter(format).setMargin(margin).setEncoding(encoding).setEccLevel(eccLevel); auto bitmap = ToMatrix(writer.encode(TextUtfEncoding::FromUtf8(text), width, height)); auto ext = GetExtension(filePath); From cc980d08dbaa57c38ca23bb388dca8ea268756ba Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Wed, 14 Jul 2021 21:42:53 +0200 Subject: [PATCH 003/185] Fix typos: "quite zone" -> "quiet zone" --- core/src/BitArray.h | 8 ++++---- core/src/BitMatrix.h | 4 ++-- core/src/BitMatrixIO.cpp | 4 ++-- core/src/BitMatrixIO.h | 2 +- core/src/MultiFormatWriter.h | 2 +- core/src/Pattern.h | 22 +++++++++++----------- core/src/datamatrix/DMDetector.cpp | 2 +- core/src/datamatrix/DMWriter.cpp | 4 ++-- core/src/datamatrix/DMWriter.h | 4 ++-- core/src/oned/ODCodabarReader.cpp | 8 ++++---- core/src/oned/ODCode128Reader.cpp | 8 ++++---- core/src/oned/ODCode39Reader.cpp | 8 ++++---- core/src/oned/ODCode93Reader.cpp | 10 +++++----- core/src/oned/ODITFReader.cpp | 6 +++--- core/src/oned/ODMultiUPCEANReader.cpp | 6 +++--- 15 files changed, 49 insertions(+), 49 deletions(-) diff --git a/core/src/BitArray.h b/core/src/BitArray.h index 4de8fe14b4..966fca547a 100644 --- a/core/src/BitArray.h +++ b/core/src/BitArray.h @@ -275,9 +275,9 @@ class BitArray #endif // Little helper method to make common isRange use case more readable. - // Pass positive zone size to look for quite zone after i and negative for zone in front of i. + // Pass positive zone size to look for quiet zone after i and negative for zone in front of i. // Set allowClippedZone to false if clipping the zone at the image border is not acceptable. - bool hasQuiteZone(Iterator i, int signedZoneSize, bool allowClippedZone = true) const { + bool hasQuietZone(Iterator i, int signedZoneSize, bool allowClippedZone = true) const { int index = static_cast(i - begin()); if (signedZoneSize > 0) { if (!allowClippedZone && index + signedZoneSize >= size()) @@ -290,8 +290,8 @@ class BitArray } } - bool hasQuiteZone(ReverseIterator i, int signedZoneSize, bool allowClippedZone = true) const { - return hasQuiteZone(i.base(), -signedZoneSize, allowClippedZone); + bool hasQuietZone(ReverseIterator i, int signedZoneSize, bool allowClippedZone = true) const { + return hasQuietZone(i.base(), -signedZoneSize, allowClippedZone); } /** diff --git a/core/src/BitMatrix.h b/core/src/BitMatrix.h index 780ed82af8..b8ce4edf0d 100644 --- a/core/src/BitMatrix.h +++ b/core/src/BitMatrix.h @@ -284,11 +284,11 @@ class BitMatrix }; /** - * @brief Inflate scales a BitMatrix up and adds a quite Zone plus padding + * @brief Inflate scales a BitMatrix up and adds a quiet Zone plus padding * @param matrix input to be expanded * @param width new width in bits (pixel) * @param height new height in bits (pixel) - * @param quietZone size of quite zone to add in modules + * @param quietZone size of quiet zone to add in modules * @return expanded BitMatrix, maybe move(input) if size did not change */ BitMatrix Inflate(BitMatrix&& input, int width, int height, int quietZone); diff --git a/core/src/BitMatrixIO.cpp b/core/src/BitMatrixIO.cpp index c796833932..be1732326c 100644 --- a/core/src/BitMatrixIO.cpp +++ b/core/src/BitMatrixIO.cpp @@ -64,9 +64,9 @@ BitMatrix ParseBitMatrix(const std::string& str, char one, bool expectSpace) return mat; } -void SaveAsPBM(const BitMatrix& matrix, const std::string filename, int quiteZone) +void SaveAsPBM(const BitMatrix& matrix, const std::string filename, int quietZone) { - auto out = Inflate(matrix.copy(), 0, 0, quiteZone); + auto out = Inflate(matrix.copy(), 0, 0, quietZone); std::ofstream file(filename); file << "P1\n" << out.width() << ' ' << out.height() << '\n'; file << ToString(out, '1', '0', true); diff --git a/core/src/BitMatrixIO.h b/core/src/BitMatrixIO.h index fde09e0252..6946b33905 100644 --- a/core/src/BitMatrixIO.h +++ b/core/src/BitMatrixIO.h @@ -25,6 +25,6 @@ namespace ZXing { std::string ToString(const BitMatrix& matrix, char one = 'X', char zero = ' ', bool addSpace = true, bool printAsCString = false); BitMatrix ParseBitMatrix(const std::string& str, char one = 'X', bool expectSpace = true); -void SaveAsPBM(const BitMatrix& matrix, const std::string filename, int quiteZone = 0); +void SaveAsPBM(const BitMatrix& matrix, const std::string filename, int quietZone = 0); } // ZXing diff --git a/core/src/MultiFormatWriter.h b/core/src/MultiFormatWriter.h index 7816b5023c..1ec252aa0f 100644 --- a/core/src/MultiFormatWriter.h +++ b/core/src/MultiFormatWriter.h @@ -52,7 +52,7 @@ class MultiFormatWriter } /** - * Used for all formats, sets the minimum number of quite zone pixels. + * Used for all formats, sets the minimum number of quiet zone pixels. */ MultiFormatWriter& setMargin(int margin) { _margin = margin; diff --git a/core/src/Pattern.h b/core/src/Pattern.h index c4a3f7c512..ce1cfda8c8 100644 --- a/core/src/Pattern.h +++ b/core/src/Pattern.h @@ -78,13 +78,13 @@ class PatternView bool isValid() const { return isValid(size()); } template - bool hasQuiteZoneBefore(float scale) const + bool hasQuietZoneBefore(float scale) const { return (acceptIfAtFirstBar && isAtFirstBar()) || _data[-1] >= sum() * scale; } template - bool hasQuiteZoneAfter(float scale) const + bool hasQuietZoneAfter(float scale) const { return (acceptIfAtLastBar && isAtLastBar()) || _data[_size] >= sum() * scale; } @@ -168,7 +168,7 @@ using FixedSparcePattern = FixedPattern; template float IsPattern(const PatternView& view, const FixedPattern& pattern, int spaceInPixel = 0, - float minQuiteZone = 0, float moduleSizeRef = 0.f) + float minQuietZone = 0, float moduleSizeRef = 0) { int width = view.sum(N); if (SUM > N && width < SUM) @@ -176,7 +176,7 @@ float IsPattern(const PatternView& view, const FixedPattern& patt const float moduleSize = (float)width / SUM; - if (minQuiteZone && spaceInPixel < minQuiteZone * moduleSize - 1) + if (minQuietZone && spaceInPixel < minQuietZone * moduleSize - 1) return 0; if (!moduleSizeRef) @@ -195,7 +195,7 @@ float IsPattern(const PatternView& view, const FixedPattern& patt template float IsPattern(const PatternView& view, const FixedPattern& pattern, int spaceInPixel = 0, - float minQuiteZone = 0, float moduleSizeRef = 0.f) + float minQuietZone = 0, float moduleSizeRef = 0) { // pattern contains the indices with the bars/spaces that need to be equally wide int width = 0; @@ -204,7 +204,7 @@ float IsPattern(const PatternView& view, const FixedPattern& patte const float moduleSize = (float)width / SUM; - if (minQuiteZone && spaceInPixel < minQuiteZone * moduleSize - 1) + if (minQuietZone && spaceInPixel < minQuietZone * moduleSize - 1) return 0; if (!moduleSizeRef) @@ -222,11 +222,11 @@ float IsPattern(const PatternView& view, const FixedPattern& patte } template -bool IsRightGuard(const PatternView& view, const FixedPattern& pattern, float minQuiteZone, +bool IsRightGuard(const PatternView& view, const FixedPattern& pattern, float minQuietZone, float moduleSizeRef = 0.f) { int spaceInPixel = view.isAtLastBar() ? std::numeric_limits::max() : *view.end(); - return IsPattern(view, pattern, spaceInPixel, minQuiteZone, moduleSizeRef) != 0; + return IsPattern(view, pattern, spaceInPixel, minQuietZone, moduleSizeRef) != 0; } template @@ -247,11 +247,11 @@ PatternView FindLeftGuard(const PatternView& view, int minSize, Pred isGuard) template PatternView FindLeftGuard(const PatternView& view, int minSize, const FixedPattern& pattern, - float minQuiteZone) + float minQuietZone) { return FindLeftGuard(view, std::max(minSize, LEN), - [&pattern, minQuiteZone](const PatternView& window, int spaceInPixel) { - return IsPattern(window, pattern, spaceInPixel, minQuiteZone); + [&pattern, minQuietZone](const PatternView& window, int spaceInPixel) { + return IsPattern(window, pattern, spaceInPixel, minQuietZone); }); } diff --git a/core/src/datamatrix/DMDetector.cpp b/core/src/datamatrix/DMDetector.cpp index 92eefc3c07..675edbd988 100644 --- a/core/src/datamatrix/DMDetector.cpp +++ b/core/src/datamatrix/DMDetector.cpp @@ -409,7 +409,7 @@ static DetectorResult DetectOld(const BitMatrix& image) * It is performing something like a (back) trace search along edges through the bit matrix, first looking for * the 'L'-pattern, then tracing the black/white borders at the top/right. Advantages over the old code are: * * works with lower resolution scans (around 2 pixel per module), due to sub-pixel precision grid placement -* * works with real-world codes that have just one module wide quite-zone (which is perfectly in spec) +* * works with real-world codes that have just one module wide quiet-zone (which is perfectly in spec) */ class DMRegressionLine : public RegressionLine diff --git a/core/src/datamatrix/DMWriter.cpp b/core/src/datamatrix/DMWriter.cpp index 1f792820ea..fe79e603cb 100644 --- a/core/src/datamatrix/DMWriter.cpp +++ b/core/src/datamatrix/DMWriter.cpp @@ -117,8 +117,8 @@ Writer::encode(const std::wstring& contents, int width, int height) const //4. step: low-level encoding BitMatrix result = EncodeLowLevel(symbolData, *symbolInfo); - //5. step: scale-up to requested size, minimum required quite zone is 1 - return Inflate(std::move(result), width, height, _quiteZone); + //5. step: scale-up to requested size, minimum required quiet zone is 1 + return Inflate(std::move(result), width, height, _quietZone); } } // namespace ZXing::DataMatrix diff --git a/core/src/datamatrix/DMWriter.h b/core/src/datamatrix/DMWriter.h index ef9c5513f4..e90e8bfda7 100644 --- a/core/src/datamatrix/DMWriter.h +++ b/core/src/datamatrix/DMWriter.h @@ -32,7 +32,7 @@ class Writer Writer(); Writer& setMargin(int margin) { - _quiteZone = margin; + _quietZone = margin; return *this; } @@ -57,7 +57,7 @@ class Writer private: SymbolShape _shapeHint; - int _quiteZone = 1, _minWidth = -1, _minHeight = -1, _maxWidth = -1, _maxHeight = -1; + int _quietZone = 1, _minWidth = -1, _minHeight = -1, _maxWidth = -1, _maxHeight = -1; }; } // DataMatrix diff --git a/core/src/oned/ODCodabarReader.cpp b/core/src/oned/ODCodabarReader.cpp index 0d6ebc33e6..b81978de06 100644 --- a/core/src/oned/ODCodabarReader.cpp +++ b/core/src/oned/ODCodabarReader.cpp @@ -48,8 +48,8 @@ CodabarReader::CodabarReader(const DecodeHints& hints) // each character has 4 bars and 3 spaces constexpr int CHAR_LEN = 7; -// quite zone is half the width of a character symbol -constexpr float QUITE_ZONE_SCALE = 0.5f; +// quiet zone is half the width of a character symbol +constexpr float QUIET_ZONE_SCALE = 0.5f; // official start and stop symbols are "ABCD" // some codabar generator allow the codabar string to be closed by every @@ -57,7 +57,7 @@ constexpr float QUITE_ZONE_SCALE = 0.5f; bool IsLeftGuard(const PatternView& view, int spaceInPixel) { - return spaceInPixel > view.sum() * QUITE_ZONE_SCALE && + return spaceInPixel > view.sum() * QUIET_ZONE_SCALE && Contains({0x1A, 0x29, 0x0B, 0x0E}, RowReader::NarrowWideBitPattern(view)); } @@ -96,7 +96,7 @@ CodabarReader::decodePattern(int rowNumber, const PatternView& row, std::unique_ // next now points to the last decoded symbol // check txt length and whitespace after the last char. See also FindStartPattern. - if (Size(txt) < minCharCount || !next.hasQuiteZoneAfter(QUITE_ZONE_SCALE)) + if (Size(txt) < minCharCount || !next.hasQuietZoneAfter(QUIET_ZONE_SCALE)) return Result(DecodeStatus::NotFound); // remove stop/start characters diff --git a/core/src/oned/ODCode128Reader.cpp b/core/src/oned/ODCode128Reader.cpp index 0629a01857..5af91d0455 100644 --- a/core/src/oned/ODCode128Reader.cpp +++ b/core/src/oned/ODCode128Reader.cpp @@ -183,7 +183,7 @@ Code128Reader::Code128Reader(const DecodeHints& hints) : // all 3 start patterns share the same 2-1-1 prefix constexpr auto START_PATTERN_PREFIX = FixedPattern<3, 4>{2, 1, 1}; constexpr int CHAR_LEN = 6; -constexpr float QUITE_ZONE = 8; // quite zone spec is 10 modules +constexpr float QUIET_ZONE = 8; // quiet zone spec is 10 modules //#define USE_FAST_1_TO_4_BIT_PATTERN_DECODING #ifdef USE_FAST_1_TO_4_BIT_PATTERN_DECODING @@ -229,7 +229,7 @@ Result Code128Reader::decodePattern(int rowNumber, const PatternView& row, std:: #endif }; - auto next = FindLeftGuard(row, minCharCount * CHAR_LEN, START_PATTERN_PREFIX, QUITE_ZONE); + auto next = FindLeftGuard(row, minCharCount * CHAR_LEN, START_PATTERN_PREFIX, QUIET_ZONE); if (!next.isValid()) return Result(DecodeStatus::NotFound); @@ -266,10 +266,10 @@ Result Code128Reader::decodePattern(int rowNumber, const PatternView& row, std:: if (Size(rawCodes) < minCharCount - 1) // stop code is missing in rawCodes return Result(DecodeStatus::NotFound); - // check termination bar (is present and not wider than about 2 modules) and quite zone (next is now 13 modules + // check termination bar (is present and not wider than about 2 modules) and quiet zone (next is now 13 modules // wide, require at least 8) next = next.subView(0, CHAR_LEN + 1); - if (!next.isValid() || next[CHAR_LEN] > next.sum(CHAR_LEN) / 4 || !next.hasQuiteZoneAfter(QUITE_ZONE/13)) + if (!next.isValid() || next[CHAR_LEN] > next.sum(CHAR_LEN) / 4 || !next.hasQuietZoneAfter(QUIET_ZONE/13)) return Result(DecodeStatus::NotFound); int checksum = rawCodes.front(); diff --git a/core/src/oned/ODCode39Reader.cpp b/core/src/oned/ODCode39Reader.cpp index 926473278f..9cea7bdbf3 100644 --- a/core/src/oned/ODCode39Reader.cpp +++ b/core/src/oned/ODCode39Reader.cpp @@ -100,10 +100,10 @@ Result Code39Reader::decodePattern(int rowNumber, const PatternView& row, std::u // provide the indices with the narrow bars/spaces which have to be equally wide constexpr auto START_PATTERN = FixedSparcePattern{0, 2, 3, 5, 7, 8}; - // quite zone is half the width of a character symbol - constexpr float QUITE_ZONE_SCALE = 0.5f; + // quiet zone is half the width of a character symbol + constexpr float QUIET_ZONE_SCALE = 0.5f; - auto next = FindLeftGuard(row, minCharCount * CHAR_LEN, START_PATTERN, QUITE_ZONE_SCALE * 12); + auto next = FindLeftGuard(row, minCharCount * CHAR_LEN, START_PATTERN, QUIET_ZONE_SCALE * 12); if (!next.isValid()) return Result(DecodeStatus::NotFound); @@ -129,7 +129,7 @@ Result Code39Reader::decodePattern(int rowNumber, const PatternView& row, std::u txt.pop_back(); // remove asterisk // check txt length and whitespace after the last char. See also FindStartPattern. - if (Size(txt) < minCharCount - 2 || !next.hasQuiteZoneAfter(QUITE_ZONE_SCALE)) + if (Size(txt) < minCharCount - 2 || !next.hasQuietZoneAfter(QUIET_ZONE_SCALE)) return Result(DecodeStatus::NotFound); if (_usingCheckDigit) { diff --git a/core/src/oned/ODCode93Reader.cpp b/core/src/oned/ODCode93Reader.cpp index 5926dfba18..8a36775944 100644 --- a/core/src/oned/ODCode93Reader.cpp +++ b/core/src/oned/ODCode93Reader.cpp @@ -77,8 +77,8 @@ bool DecodeExtendedCode39AndCode93(std::string& encoded, const char ctrl[4]); constexpr int CHAR_LEN = 6; constexpr int CHAR_SUM = 9; -// quite zone is half the width of a character symbol -constexpr float QUITE_ZONE_SCALE = 0.5f; +// quiet zone is half the width of a character symbol +constexpr float QUIET_ZONE_SCALE = 0.5f; static bool IsStartGuard(const PatternView& window, int spaceInPixel) { @@ -86,7 +86,7 @@ static bool IsStartGuard(const PatternView& window, int spaceInPixel) // Use only the first 4 elements which results in more than a 2x speedup. This is counter-intuitive since we save at // most 1/3rd of the loop iterations in FindPattern. The reason might be a successful vectorization with the limited // pattern size that is missed otherwise. We check for the remaining 2 slots for plausibility of the 4:1 ratio. - return IsPattern(window, FixedPattern<4, 4>{1, 1, 1, 1}, spaceInPixel, QUITE_ZONE_SCALE * 12) && + return IsPattern(window, FixedPattern<4, 4>{1, 1, 1, 1}, spaceInPixel, QUIET_ZONE_SCALE * 12) && window[4] > 3 * window[5] - 2 && RowReader::OneToFourBitPattern(window) == ASTERISK_ENCODING; } @@ -120,9 +120,9 @@ Result Code93Reader::decodePattern(int rowNumber, const PatternView& row, std::u if (Size(txt) < minCharCount - 2) return Result(DecodeStatus::NotFound); - // check termination bar (is present and not wider than about 2 modules) and quite zone + // check termination bar (is present and not wider than about 2 modules) and quiet zone next = next.subView(0, CHAR_LEN + 1); - if (!next.isValid() || next[CHAR_LEN] > next.sum(CHAR_LEN) / 4 || !next.hasQuiteZoneAfter(QUITE_ZONE_SCALE)) + if (!next.isValid() || next[CHAR_LEN] > next.sum(CHAR_LEN) / 4 || !next.hasQuietZoneAfter(QUIET_ZONE_SCALE)) return Result(DecodeStatus::NotFound); if (!CheckChecksums(txt)) diff --git a/core/src/oned/ODITFReader.cpp b/core/src/oned/ODITFReader.cpp index 42ddd592d9..5b1224952d 100644 --- a/core/src/oned/ODITFReader.cpp +++ b/core/src/oned/ODITFReader.cpp @@ -43,9 +43,9 @@ constexpr auto STOP_PATTERN_2 = FixedPattern<3, 5>{3, 1, 1}; Result ITFReader::decodePattern(int rowNumber, const PatternView& row, std::unique_ptr&) const { const int minCharCount = 6; - const int minQuiteZone = 10; + const int minQuietZone = 10; - auto next = FindLeftGuard(row, 4 + minCharCount/2 + 3, START_PATTERN_, minQuiteZone); + auto next = FindLeftGuard(row, 4 + minCharCount/2 + 3, START_PATTERN_, minQuietZone); if (!next.isValid()) return Result(DecodeStatus::NotFound); @@ -83,7 +83,7 @@ Result ITFReader::decodePattern(int rowNumber, const PatternView& row, std::uniq if (Size(txt) < minCharCount) return Result(DecodeStatus::NotFound); - if (!IsRightGuard(next, STOP_PATTERN_1, minQuiteZone) && !IsRightGuard(next, STOP_PATTERN_2, minQuiteZone)) + if (!IsRightGuard(next, STOP_PATTERN_1, minQuietZone) && !IsRightGuard(next, STOP_PATTERN_2, minQuietZone)) return Result(DecodeStatus::NotFound); int xStop = next.pixelsTillEnd(); diff --git a/core/src/oned/ODMultiUPCEANReader.cpp b/core/src/oned/ODMultiUPCEANReader.cpp index 30ac3d5ea0..136a724c92 100644 --- a/core/src/oned/ODMultiUPCEANReader.cpp +++ b/core/src/oned/ODMultiUPCEANReader.cpp @@ -48,7 +48,7 @@ static const int FIRST_DIGIT_ENCODINGS[] = { 0x00, 0x0B, 0x0D, 0x0E, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A }; -// The GS1 specification has the following to say about quite zones +// The GS1 specification has the following to say about quiet zones // Type: EAN-13 | EAN-8 | UPC-A | UPC-E | EAN Add-on | UPC Add-on // QZ L: 11 | 7 | 9 | 9 | 7-12 | 9-12 // QZ R: 7 | 7 | 9 | 7 | 5 | 5 @@ -57,7 +57,7 @@ constexpr float QUIET_ZONE_LEFT = 6; constexpr float QUIET_ZONE_RIGHT = 6; // There is a single sample (ean13-1/12.png) that fails to decode with these (new) settings because -// it has a right-side quite zone of only about 4.5 modules, which is clearly out of spec. +// it has a right-side quiet zone of only about 4.5 modules, which is clearly out of spec. static bool DecodeDigit(const PatternView& view, std::string& txt, int* lgPattern = nullptr) { @@ -265,7 +265,7 @@ static bool AddOn(PartialResult& res, PatternView begin, int digitCount) } } - //TODO: check right quite zone + //TODO: check right quiet zone if (digitCount == 2) { CHECK(std::stoi(res.txt) % 4 == lgPattern); From 7067c1c8d5d9b9a8ba1511ad61e71b7726a17e4b Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Wed, 14 Jul 2021 21:51:23 +0200 Subject: [PATCH 004/185] [[deprecated]]: remove the (trivially removable) deprecated APIs If this breaks your code, you have been warned ;). --- core/src/BarcodeFormat.h | 1 - core/src/BinaryBitmap.h | 6 -- core/src/BitMatrix.cpp | 10 --- core/src/BitMatrix.h | 3 - core/src/DecodeHints.h | 22 ------ core/src/ReadBarcode.h | 9 --- core/src/Result.cpp | 8 -- core/src/Result.h | 30 -------- core/src/ResultMetadata.cpp | 140 ----------------------------------- core/src/ResultMetadata.h | 142 ------------------------------------ 10 files changed, 371 deletions(-) delete mode 100644 core/src/ResultMetadata.cpp delete mode 100644 core/src/ResultMetadata.h diff --git a/core/src/BarcodeFormat.h b/core/src/BarcodeFormat.h index 509e65282e..2858ed57e6 100644 --- a/core/src/BarcodeFormat.h +++ b/core/src/BarcodeFormat.h @@ -70,7 +70,6 @@ enum class BarcodeFormat UPC_A [[deprecated]] = UPCA, UPC_E [[deprecated]] = UPCE, - FORMAT_COUNT [[deprecated]] = None, ///> DEPRECATED: will be removed _max = UPCE, ///> implementation detail, don't use }; diff --git a/core/src/BinaryBitmap.h b/core/src/BinaryBitmap.h index 357a4d1a12..14b9d01890 100644 --- a/core/src/BinaryBitmap.h +++ b/core/src/BinaryBitmap.h @@ -39,12 +39,6 @@ class BinaryBitmap public: virtual ~BinaryBitmap() = default; - /** - * Image is a pure monochrome image of a barcode. - */ - [[deprecated]] - virtual bool isPureBarcode() const { return false; } - /** * @return The width of the bitmap. */ diff --git a/core/src/BitMatrix.cpp b/core/src/BitMatrix.cpp index 5868b5fa23..2f484f7d1c 100644 --- a/core/src/BitMatrix.cpp +++ b/core/src/BitMatrix.cpp @@ -18,7 +18,6 @@ #include "BitMatrix.h" #include "BitArray.h" -#include "ByteMatrix.h" #include "Pattern.h" #ifndef ZX_FAST_BIT_STORAGE @@ -46,15 +45,6 @@ BitMatrix::getRow(int y, BitArray& row) const #endif } -ByteMatrix BitMatrix::toByteMatrix(int black, int white) const -{ - ByteMatrix res(width(), height()); - for (int y = 0; y < height(); ++y) - for (int x = 0; x < width(); ++x) - res.set(x, y, get(x, y) ? black : white); - return res; -} - void BitMatrix::setRegion(int left, int top, int width, int height) { diff --git a/core/src/BitMatrix.h b/core/src/BitMatrix.h index b8ce4edf0d..1569e885a5 100644 --- a/core/src/BitMatrix.h +++ b/core/src/BitMatrix.h @@ -100,9 +100,6 @@ class BitMatrix return *this; } - [[deprecated]] - ByteMatrix toByteMatrix(int black = 0, int white = 255) const; - #ifdef ZX_FAST_BIT_STORAGE // experimental iterator based access template diff --git a/core/src/DecodeHints.h b/core/src/DecodeHints.h index 7f0a2cd9e8..4b18dca106 100644 --- a/core/src/DecodeHints.h +++ b/core/src/DecodeHints.h @@ -121,28 +121,6 @@ class DecodeHints bool hasFormat(BarcodeFormats f) const noexcept { return _formats.testFlags(f); } bool hasNoFormat() const noexcept { return _formats.empty(); } - - [[deprecated]] DecodeHints& setPossibleFormats(const std::vector& formats) - { - _formats.clear(); - for (auto f : formats) - _formats |= f; - return *this; - } - - [[deprecated]] bool requireEanAddOnSymbol() const { return _eanAddOnSymbol == EanAddOnSymbol::Require; } - [[deprecated]] DecodeHints& setRequireEanAddOnSymbol(bool v) - { - return setEanAddOnSymbol(v ? EanAddOnSymbol::Require : EanAddOnSymbol::Ignore); - } - [[deprecated]] std::vector allowedEanExtensions() const - { - return _eanAddOnSymbol == EanAddOnSymbol::Require ? std::vector{2, 5} : std::vector{}; - } - [[deprecated]] DecodeHints& setAllowedEanExtensions(const std::vector& v) - { - return setEanAddOnSymbol(v.empty() ? EanAddOnSymbol::Ignore : EanAddOnSymbol::Require); - } }; } // ZXing diff --git a/core/src/ReadBarcode.h b/core/src/ReadBarcode.h index 4204ec3764..901c78d37e 100644 --- a/core/src/ReadBarcode.h +++ b/core/src/ReadBarcode.h @@ -99,14 +99,5 @@ class ImageView */ Result ReadBarcode(const ImageView& buffer, const DecodeHints& hints = {}); - -[[deprecated]] -Result ReadBarcode(int width, int height, const uint8_t* data, int rowStride, - BarcodeFormats formats = {}, bool tryRotate = true, bool tryHarder = true); - -[[deprecated]] -Result ReadBarcode(int width, int height, const uint8_t* data, int rowStride, int pixelStride, int rIndex, int gIndex, int bIndex, - BarcodeFormats formats = {}, bool tryRotate = true, bool tryHarder = true); - } // ZXing diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 4b37224b58..abafb2fd49 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -43,14 +43,6 @@ Result::Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat _position(std::move(position)), _rawBytes(std::move(decodeResult).rawBytes()), _numBits(decodeResult.numBits()), _ecLevel(decodeResult.ecLevel()), _sai(decodeResult.structuredAppend()), _readerInit(decodeResult.readerInit()) { - // TODO: keep that for one release so people get the deprecation warning with a still intact functionality - if (isPartOfSequence()) { - _metadata.put(ResultMetadata::STRUCTURED_APPEND_SEQUENCE, sequenceIndex()); - _metadata.put(ResultMetadata::STRUCTURED_APPEND_CODE_COUNT, sequenceSize()); - if (_format == BarcodeFormat::QRCode) - _metadata.put(ResultMetadata::STRUCTURED_APPEND_PARITY, std::stoi(sequenceId())); - } - // TODO: add type opaque and code specific 'extra data'? (see DecoderResult::extra()) } diff --git a/core/src/Result.h b/core/src/Result.h index 0d3363a85d..0ae693ea8a 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -21,13 +21,9 @@ #include "ByteArray.h" #include "DecodeStatus.h" #include "Quadrilateral.h" -#include "ResultMetadata.h" -#include "ResultPoint.h" #include "StructuredAppend.h" #include -#include -#include namespace ZXing { @@ -66,20 +62,10 @@ class Result return _format; } - [[deprecated]] - void setFormat(BarcodeFormat format) { - _format = format; - } - const std::wstring& text() const { return _text; } - [[deprecated]] - void setText(std::wstring&& text) { - _text = std::move(text); - } - const Position& position() const { return _position; } @@ -101,21 +87,6 @@ class Result return _ecLevel; } - [[deprecated]] - std::vector resultPoints() const { - return {position().begin(), position().end()}; - } - - [[deprecated]] - const ResultMetadata& metadata() const { - return _metadata; - } - - [[deprecated]] - ResultMetadata& metadata() { - return _metadata; - } - /** * @brief sequenceSize number of symbols in a structured append sequence. * @@ -157,7 +128,6 @@ class Result ByteArray _rawBytes; int _numBits = 0; std::wstring _ecLevel; - ResultMetadata _metadata; StructuredAppendInfo _sai; bool _readerInit = false; }; diff --git a/core/src/ResultMetadata.cpp b/core/src/ResultMetadata.cpp deleted file mode 100644 index ec228e266a..0000000000 --- a/core/src/ResultMetadata.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* -* Copyright 2016 Nu-book Inc. -* Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#include "ResultMetadata.h" - -#include "ByteArray.h" - -#include - -namespace ZXing { - -struct ResultMetadata::Value -{ - virtual ~Value() = default; - virtual int toInteger(int fallback) const { - return fallback; - } - virtual std::wstring toString() const { - return std::wstring(); - } - virtual std::list toByteArrayList() const { - return std::list(); - } - virtual std::shared_ptr toCustomData() const { - return nullptr; - } -}; - -struct ResultMetadata::IntegerValue : public Value -{ - int value; - explicit IntegerValue(int v) : value(v) {} - int toInteger(int) const override { - return value; - } - std::wstring toString() const override { - return std::to_wstring(value); - } -}; - -struct ResultMetadata::StringValue : public Value -{ - std::wstring value; - explicit StringValue(std::wstring v) : value(std::move(v)) {} - std::wstring toString() const override { - return value; - } -}; - -struct ResultMetadata::ByteArrayListValue : public Value -{ - std::list value; - explicit ByteArrayListValue(std::list v) : value(std::move(v)) {} - std::list toByteArrayList() const override { - return value; - } -}; - -struct ResultMetadata::CustomDataValue : public Value -{ - std::shared_ptr value; - explicit CustomDataValue(std::shared_ptr v) : value(std::move(v)) {} - std::shared_ptr toCustomData() const override { - return value; - } -}; - -int -ResultMetadata::getInt(Key key, int fallbackValue) const -{ - auto it = _contents.find(key); - return it != _contents.end() ? it->second->toInteger(fallbackValue) : fallbackValue; -} - -std::wstring -ResultMetadata::getString(Key key) const -{ - auto it = _contents.find(key); - return it != _contents.end() ? it->second->toString() : std::wstring(); -} - -std::list -ResultMetadata::getByteArrayList(Key key) const -{ - auto it = _contents.find(key); - return it != _contents.end() ? it->second->toByteArrayList() : std::list(); -} - -std::shared_ptr -ResultMetadata::getCustomData(Key key) const -{ - auto it = _contents.find(key); - return it != _contents.end() ? it->second->toCustomData() : nullptr; -} - -void -ResultMetadata::put(Key key, int value) -{ - _contents[key] = std::make_shared(value); -} - -void -ResultMetadata::put(Key key, const std::wstring& value) -{ - _contents[key] = std::make_shared(value); -} - -void -ResultMetadata::put(Key key, const std::list& value) -{ - _contents[key] = std::make_shared(value); -} - -void -ResultMetadata::put(Key key, const std::shared_ptr& value) -{ - _contents[key] = std::make_shared(value); -} - -void -ResultMetadata::putAll(const ResultMetadata& other) -{ - _contents.insert(other._contents.begin(), other._contents.end()); -} - -} // ZXing diff --git a/core/src/ResultMetadata.h b/core/src/ResultMetadata.h deleted file mode 100644 index 314a8a1ace..0000000000 --- a/core/src/ResultMetadata.h +++ /dev/null @@ -1,142 +0,0 @@ -#pragma once -/* -* Copyright 2016 Nu-book Inc. -* Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#include -#include -#include -#include - -namespace ZXing { - -class ByteArray; -class CustomData; - -/** -* @author Sean Owen -*/ -class ResultMetadata -{ -public: - - /** - * Represents some type of metadata about the result of the decoding that the decoder - * wishes to communicate back to the caller. - */ - enum Key { - - /** - * Unspecified, application-specific metadata. Maps to an unspecified {@link CustomData}. - */ - OTHER [[deprecated]], - - /** - * Denotes the likely approximate orientation of the barcode in the image. This value - * is given as degrees rotated clockwise from the normal, upright orientation. - * For example a 1D barcode which was found by reading top-to-bottom would be - * said to have orientation "90". This key maps to an {@link Integer} whose - * value is in the range [0,360). - */ - ORIENTATION [[deprecated]], - - /** - *

2D barcode formats typically encode text, but allow for a sort of 'byte mode' - * which is sometimes used to encode binary data. While {@link Result} makes available - * the complete raw bytes in the barcode for these formats, it does not offer the bytes - * from the byte segments alone.

- * - *

This maps to a {@link java.util.List} of byte arrays corresponding to the - * raw bytes in the byte segments in the barcode, in order.

- */ - BYTE_SEGMENTS [[deprecated]], - - /** - * Error correction level used, if applicable. The value type depends on the - * format, but is typically a String. - */ - ERROR_CORRECTION_LEVEL [[deprecated]], - - /** - * For some periodicals, indicates the issue number as an {@link Integer}. - */ - ISSUE_NUMBER [[deprecated]], - - /** - * For some products, indicates the suggested retail price in the barcode as a - * formatted {@link String}. - */ - SUGGESTED_PRICE [[deprecated]], - - /** - * For some products, the possible country of manufacture as a {@link String} denoting the - * ISO country code. Some map to multiple possible countries, like "US/CA". - */ - POSSIBLE_COUNTRY [[deprecated]], - - /** - * For some products, the extension text - */ - UPC_EAN_EXTENSION [[deprecated]], - - /** - * PDF417-specific metadata - */ - PDF417_EXTRA_METADATA [[deprecated]], - - /** - * If the code format supports structured append and the current scanned code is part of one then the - * sequence number is given with it. - */ - STRUCTURED_APPEND_SEQUENCE [[deprecated]], - - /** - * If the code format supports structured append and the current scanned code is part of one then the - * total code count is given with it. - */ - STRUCTURED_APPEND_CODE_COUNT [[deprecated]], - - /** - * If the code format supports structured append and the current scanned code is part of one then the - * parity is given with it. - */ - STRUCTURED_APPEND_PARITY [[deprecated]], - - }; - - int getInt(Key key, int fallbackValue = 0) const; - std::wstring getString(Key key) const; - std::list getByteArrayList(Key key) const; - std::shared_ptr getCustomData(Key key) const; - - void put(Key key, int value); - void put(Key key, const std::wstring& value); - void put(Key key, const std::list& value); - void put(Key key, const std::shared_ptr& value); - - void putAll(const ResultMetadata& other); - -private: - struct Value; - struct IntegerValue; - struct StringValue; - struct ByteArrayListValue; - struct CustomDataValue; - - std::map> _contents; -}; - -} // ZXing From e76b4723b28c4cb6d15c325516b98f1be66932b7 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Wed, 14 Jul 2021 21:57:42 +0200 Subject: [PATCH 005/185] [[deprecated]]: fix build regression (overlooked hunk in CMakeLists.txt) --- core/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 7a2db607cf..e2c2c9421b 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -107,8 +107,6 @@ if (BUILD_READERS) src/ReedSolomonDecoder.cpp src/Result.h src/Result.cpp - src/ResultMetadata.h - src/ResultMetadata.cpp src/ResultPoint.h src/ResultPoint.cpp src/TextDecoder.h From 92e17f3cb1b445f2ef98a725f082587a20848e07 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Thu, 15 Jul 2021 16:29:06 +0200 Subject: [PATCH 006/185] [[deprecated]]: remove another overlooked hunk (amend last commit) --- core/src/ReadBarcode.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/core/src/ReadBarcode.cpp b/core/src/ReadBarcode.cpp index 81a0236e4d..67cfa03b4b 100644 --- a/core/src/ReadBarcode.cpp +++ b/core/src/ReadBarcode.cpp @@ -62,18 +62,4 @@ Result ReadBarcode(const ImageView& iv, const DecodeHints& hints) } } -Result ReadBarcode(int width, int height, const uint8_t* data, int rowStride, BarcodeFormats formats, bool tryRotate, - bool tryHarder) -{ - return ReadBarcode({0, 0, width, height, data, rowStride, 1, 0, 0, 0, nullptr}, - DecodeHints().setTryHarder(tryHarder).setTryRotate(tryRotate).setFormats(formats)); -} - -Result ReadBarcode(int width, int height, const uint8_t* data, int rowStride, int pixelStride, int rIndex, int gIndex, - int bIndex, BarcodeFormats formats, bool tryRotate, bool tryHarder) -{ - return ReadBarcode({0, 0, width, height, data, rowStride, pixelStride, rIndex, gIndex, bIndex, nullptr}, - DecodeHints().setTryHarder(tryHarder).setTryRotate(tryRotate).setFormats(formats)); -} - } // ZXing From 89012b4b002db71f0d6b85fd6630c47c509d9e93 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Mon, 19 Jul 2021 10:46:34 +0200 Subject: [PATCH 007/185] [[deprecated]]: Remove LuminanceSource API * replace LuminanceSource constructors of Binarizers with ImageView * remove all unnecessary memcopies of the image buffer * don't rotate the whole buffer for 1D readers, only the checked lines * move BitMatrix cache into the BinaryBitmap base class * simplify BinaryBitmap interface (drop cropped() and rotated()) * move ImageView class into separate header This does indeed result in a slight performance improvement of the ReaderTest blackbox testing on a modern Core i9 (approx. 4% faster). --- core/CMakeLists.txt | 5 +- core/src/BinaryBitmap.cpp | 17 ++ core/src/BinaryBitmap.h | 75 ++------ core/src/GenericLuminanceSource.cpp | 242 -------------------------- core/src/GenericLuminanceSource.h | 84 --------- core/src/GlobalHistogramBinarizer.cpp | 130 +++----------- core/src/GlobalHistogramBinarizer.h | 23 +-- core/src/HybridBinarizer.cpp | 80 +++------ core/src/HybridBinarizer.h | 9 +- core/src/ImageView.h | 100 +++++++++++ core/src/LuminanceSource.cpp | 146 ---------------- core/src/LuminanceSource.h | 114 ------------ core/src/ReadBarcode.cpp | 74 +++++--- core/src/ReadBarcode.h | 71 +------- core/src/ThresholdBinarizer.h | 76 ++++---- core/src/aztec/AZReader.cpp | 2 +- core/src/datamatrix/DMReader.cpp | 2 +- core/src/maxicode/MCReader.cpp | 2 +- core/src/oned/ODReader.cpp | 23 ++- core/src/pdf417/PDFDetector.cpp | 6 +- core/src/pdf417/PDFReader.cpp | 2 +- core/src/qrcode/QRReader.cpp | 2 +- thirdparty/stb/stb_image.h | 2 +- 23 files changed, 293 insertions(+), 994 deletions(-) delete mode 100644 core/src/GenericLuminanceSource.cpp delete mode 100644 core/src/GenericLuminanceSource.h create mode 100644 core/src/ImageView.h delete mode 100644 core/src/LuminanceSource.cpp delete mode 100644 core/src/LuminanceSource.h diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index e2c2c9421b..7f5bbbef2a 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -86,16 +86,13 @@ if (BUILD_READERS) src/DecodeStatus.cpp src/DecoderResult.h src/DetectorResult.h - src/GenericLuminanceSource.h - src/GenericLuminanceSource.cpp src/GlobalHistogramBinarizer.h src/GlobalHistogramBinarizer.cpp src/GridSampler.h src/GridSampler.cpp src/HybridBinarizer.h src/HybridBinarizer.cpp - src/LuminanceSource.h - src/LuminanceSource.cpp + src/ImageView.h src/MultiFormatReader.h src/MultiFormatReader.cpp src/PerspectiveTransform.h diff --git a/core/src/BinaryBitmap.cpp b/core/src/BinaryBitmap.cpp index 93ad54ccde..e68cccd2bc 100644 --- a/core/src/BinaryBitmap.cpp +++ b/core/src/BinaryBitmap.cpp @@ -16,7 +16,24 @@ #include "BinaryBitmap.h" +#include + namespace ZXing { +struct BinaryBitmap::Cache +{ + std::once_flag once; + std::shared_ptr matrix; +}; + +BinaryBitmap::BinaryBitmap(const ImageView& buffer) : _cache(new Cache), _buffer(buffer) {} + +BinaryBitmap::~BinaryBitmap() = default; + +const BitMatrix* BinaryBitmap::getBitMatrix() const +{ + std::call_once(_cache->once, [&](){_cache->matrix = getBlackMatrix();}); + return _cache->matrix.get(); +} } // ZXing diff --git a/core/src/BinaryBitmap.h b/core/src/BinaryBitmap.h index 14b9d01890..896294db73 100644 --- a/core/src/BinaryBitmap.h +++ b/core/src/BinaryBitmap.h @@ -2,6 +2,7 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors +* Copyright 2021 Axel Waggershauser * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,14 +17,14 @@ * limitations under the License. */ -#include -#include +#include "ReadBarcode.h" + #include +#include #include namespace ZXing { -class BitArray; class BitMatrix; using PatternRow = std::vector; @@ -31,75 +32,35 @@ using PatternRow = std::vector; /** * This class is the core bitmap class used by ZXing to represent 1 bit data. Reader objects * accept a BinaryBitmap and attempt to decode it. -* -* @author dswitkin@google.com (Daniel Switkin) */ class BinaryBitmap { -public: - virtual ~BinaryBitmap() = default; - - /** - * @return The width of the bitmap. - */ - virtual int width() const = 0; - - /** - * @return The height of the bitmap. - */ - virtual int height() const = 0; + struct Cache; + std::unique_ptr _cache; - /** - * Converts one row of luminance data to a vector of ints denoting the widths of the bars and spaces. - */ - virtual bool getPatternRow(int y, PatternRow& res) const = 0; +protected: + const ImageView _buffer; /** - * Converts a 2D array of luminance data to 1 bit. This method is intended for decoding 2D - * barcodes and may or may not apply sharpening. Therefore, a row from this matrix may not be - * identical to one fetched using getBlackRow(), so don't mix and match between them. + * Converts a 2D array of luminance data to 1 bit (true means black). * - * @return The 2D array of bits for the image (true means black). - * @return null if image can't be binarized to make a matrix + * @return The 2D array of bits for the image, nullptr on error. */ virtual std::shared_ptr getBlackMatrix() const = 0; - /** - * @return Whether this bitmap can be cropped. - */ - virtual bool canCrop() const { return false; } +public: + BinaryBitmap(const ImageView& buffer); + virtual ~BinaryBitmap(); - /** - * Returns a new object with cropped image data. Implementations may keep a reference to the - * original data rather than a copy. Only callable if isCropSupported() is true. - * - * @param left The left coordinate, which must be in [0,getWidth()) - * @param top The top coordinate, which must be in [0,getHeight()) - * @param width The width of the rectangle to crop. - * @param height The height of the rectangle to crop. - * @return A cropped version of this object. - */ - virtual std::shared_ptr cropped(int /*left*/, int /*top*/, int /*width*/, int /*height*/) const - { - throw std::runtime_error("This binarizer does not support cropping."); - } + int width() const { return _buffer.width(); } + int height() const { return _buffer.height(); } /** - * @return Whether this bitmap supports counter-clockwise rotation. + * Converts one row of luminance data to a vector of ints denoting the widths of the bars and spaces. */ - virtual bool canRotate() const { return false; } + virtual bool getPatternRow(int row, int rotation, PatternRow& res) const = 0; - /** - * Returns a new object with rotated image data by 90 degrees clockwise. - * Only callable if {@link #isRotateSupported()} is true. - * - * @param degreeCW degree in clockwise direction, possible values are 90, 180 and 270 - * @return A rotated version of this object. - */ - virtual std::shared_ptr rotated(int /*degreeCW*/) const - { - throw std::runtime_error("This binarizer does not support rotation."); - } + const BitMatrix* getBitMatrix() const; }; } // ZXing diff --git a/core/src/GenericLuminanceSource.cpp b/core/src/GenericLuminanceSource.cpp deleted file mode 100644 index 501fd2631e..0000000000 --- a/core/src/GenericLuminanceSource.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/* -* Copyright 2016 Nu-book Inc. -* Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#if defined(_MSC_VER) && (_MSC_VER >= 1915) -#pragma warning(disable : 4996) -#endif - -#include "GenericLuminanceSource.h" - -#include "ByteArray.h" -#include "ZXContainerAlgorithms.h" - -#include -#include -#include -#include -#include - -namespace ZXing { - -static uint8_t RGBToGray(unsigned r, unsigned g, unsigned b) -{ - // This optimization is not necessary as the computation below is cheap enough. - //if (r == g && g == b) { - // // Image is already greyscale, so pick any channel. - // return static_cast(r); - //} - - // .299R + 0.587G + 0.114B (YUV/YIQ for PAL and NTSC), - // (306*R) >> 10 is approximately equal to R*0.299, and so on. - // 0x200 >> 10 is 0.5, it implements rounding. - return static_cast((306 * r + 601 * g + 117 * b + 0x200) >> 10); -} - -static std::shared_ptr MakeCopy(const void* src, int rowBytes, int left, int top, int width, int height) -{ - auto result = std::make_shared(); - result->resize(width * height); - const uint8_t* srcRow = static_cast(src) + top * rowBytes + left; - uint8_t* destRow = result->data(); - for (int y = 0; y < height; ++y, srcRow += rowBytes, destRow += width) { - std::copy_n(srcRow, width, destRow); - } - return result; -} - -static std::shared_ptr MakeCopy(const ByteArray& pixels, int rowBytes, int left, int top, int width, int height) -{ - if (top == 0 && left == 0 && width * height == Size(pixels)) { - return std::make_shared(pixels); - } - return MakeCopy(pixels.data(), rowBytes, left, top, width, height); -} - -GenericLuminanceSource::GenericLuminanceSource(int left, int top, int width, int height, const void* bytes, int rowBytes, int pixelBytes, int redIndex, int greenIndex, int blueIndex, void*) : - _left(0), // since we copy the pixels - _top(0), - _width(width), - _height(height), - _rowBytes(width) -{ - if (left < 0 || top < 0 || width < 0 || height < 0) { - throw std::out_of_range("Requested offset is outside the image"); - } - - if (pixelBytes == 1) - _pixels = MakeCopy(bytes, rowBytes, left, top, width, height); - else { - auto pixels = std::make_shared(width * height); - const uint8_t *rgbSource = static_cast(bytes) + top * rowBytes; - uint8_t *destRow = pixels->data(); - for (int y = 0; y < height; ++y, rgbSource += rowBytes, destRow += width) { - const uint8_t *src = rgbSource + left * pixelBytes; - for (int x = 0; x < width; ++x, src += pixelBytes) { - destRow[x] = RGBToGray(src[redIndex], src[greenIndex], src[blueIndex]); - } - } - _pixels = std::move(pixels); - } -} - -GenericLuminanceSource::GenericLuminanceSource(int left, int top, int width, int height, std::shared_ptr pixels, int rowBytes) : - _pixels(std::move(pixels)), - _left(left), - _top(top), - _width(width), - _height(height), - _rowBytes(rowBytes) -{ - if (left < 0 || top < 0 || width < 0 || height < 0) { - throw std::out_of_range("Requested offset is outside the image"); - } -} - -// Defined here instead of inlining in header to avoid link error since WINDOWS_EXPORT_ALL_SYMBOLS does not cover vtable. -GenericLuminanceSource::GenericLuminanceSource(int width, int height, const void* bytes, int rowBytes, int pixelBytes, int redIndex, int greenIndex, int blueIndex) : - GenericLuminanceSource(0, 0, width, height, bytes, rowBytes, pixelBytes, redIndex, greenIndex, blueIndex, nullptr) -{ -} - -// Defined here instead of inlining in header to avoid link error since WINDOWS_EXPORT_ALL_SYMBOLS does not cover vtable. -GenericLuminanceSource::GenericLuminanceSource(int left, int top, int width, int height, const void* bytes, int rowBytes, int pixelBytes, int redIndex, int greenIndex, int blueIndex) : - GenericLuminanceSource(left, top, width, height, bytes, rowBytes, pixelBytes, redIndex, greenIndex, blueIndex, nullptr) -{ -} - -// Defined here instead of inlining in header to avoid link error since WINDOWS_EXPORT_ALL_SYMBOLS does not cover vtable. -GenericLuminanceSource::GenericLuminanceSource(int width, int height, const void* bytes, int rowBytes) : - GenericLuminanceSource(0, 0, width, height, bytes, rowBytes, 1, 0, 0, 0, nullptr) -{ -} - -// Defined here instead of inlining in header to avoid link error since WINDOWS_EXPORT_ALL_SYMBOLS does not cover vtable. -GenericLuminanceSource::GenericLuminanceSource(int left, int top, int width, int height, const void* bytes, int rowBytes) : - GenericLuminanceSource(left, top, width, height, bytes, rowBytes, 1, 0, 0, 0, nullptr) -{ -} - -int -GenericLuminanceSource::width() const -{ - return _width; -} - -int -GenericLuminanceSource::height() const -{ - return _height; -} - - - -const uint8_t * -GenericLuminanceSource::getRow(int y, ByteArray& buffer, bool forceCopy) const -{ - if (y < 0 || y >= _height) { - throw std::out_of_range("Requested row is outside the image"); - } - - const uint8_t* row = _pixels->data() + (y + _top)*_rowBytes + _left; - if (!forceCopy) { - return row; - } - - buffer.resize(_width); - std::copy_n(row, _width, buffer.begin()); - return buffer.data(); -} - -const uint8_t * -GenericLuminanceSource::getMatrix(ByteArray& buffer, int& outRowBytes, bool forceCopy) const -{ - const uint8_t* row = _pixels->data() + _top*_rowBytes + _left; - if (!forceCopy) { - outRowBytes = _rowBytes; - return row; - } - - outRowBytes = _width; - buffer.resize(_width * _height); - uint8_t* dest = buffer.data(); - for (int y = 0; y < _height; ++y, row += _rowBytes, dest += _width) { - std::copy_n(row, _width, dest); - } - return buffer.data(); -} - -bool -GenericLuminanceSource::canCrop() const -{ - return true; -} - -std::shared_ptr -GenericLuminanceSource::cropped(int left, int top, int width, int height) const -{ - if (left < 0 || top < 0 || width < 0 || height < 0 || left + width > _width || top + height > _height) { - throw std::out_of_range("Crop rectangle does not fit within image data."); - } - return std::make_shared(_left + left, _top + top, width, height, _pixels, _rowBytes); -} - -bool -GenericLuminanceSource::canRotate() const -{ - return true; -} - -std::shared_ptr -GenericLuminanceSource::rotated(int degreeCW) const -{ - degreeCW = (degreeCW + 360) % 360; - if (degreeCW == 90) - { - auto pixels = std::make_shared(_width * _height); - const uint8_t* srcRow = _pixels->data() + _top * _rowBytes + _left; - uint8_t* dest = pixels->data(); - for (int y = 0; y < _height; ++y, srcRow += _rowBytes) { - for (int x = 0; x < _width; ++x) { - dest[x * _height + (_height - y - 1)] = srcRow[x]; - } - } - return std::make_shared(0, 0, _height, _width, pixels, _height); - } - else if (degreeCW == 180) { - // same as a vertical flip followed a horizontal flip - auto pixels = MakeCopy(*_pixels, _rowBytes, _left, _top, _width, _height); - std::reverse(pixels->begin(), pixels->end()); - return std::make_shared(0, 0, _width, _height, pixels, _width); - } - else if (degreeCW == 270) { - auto pixels = std::make_shared(_width * _height); - const uint8_t* srcRow = _pixels->data() + _top * _rowBytes + _left; - uint8_t* dest = pixels->data(); - for (int y = 0; y < _height; ++y, srcRow += _rowBytes) { - for (int x = 0; x < _width; ++x) { - dest[(_width - x - 1) * _height + y] = srcRow[x]; - } - } - return std::make_shared(0, 0, _height, _width, pixels, _height); - } - else if (degreeCW == 0) { - return std::make_shared(0, 0, _width, _height, _pixels, _width); - } - throw std::invalid_argument("Unsupported rotation"); -} - -} // ZXing diff --git a/core/src/GenericLuminanceSource.h b/core/src/GenericLuminanceSource.h deleted file mode 100644 index cfb5ad2a19..0000000000 --- a/core/src/GenericLuminanceSource.h +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once -/* -* Copyright 2016 Nu-book Inc. -* Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#include "LuminanceSource.h" - -#include -#include - -namespace ZXing { - -/** -* This decode images from an address in memory as RGB or ARGB data. -*/ -class GenericLuminanceSource : public LuminanceSource -{ -public: - virtual ~GenericLuminanceSource() = default; - - // Don't use in client code. Used internally for now to prevent lots of deprecation warning noise until the GenericLuminanceSource is completely removed. - GenericLuminanceSource(int left, int top, int width, int height, const void* bytes, int rowBytes, int pixelBytes, int redIndex, int greenIndex, int blueIndex, void* deprecation_tag); - /** - * Init with a RGB source. - */ - [[deprecated]] // please use interface from ReadBarcode.h - GenericLuminanceSource(int width, int height, const void* bytes, int rowBytes, int pixelBytes, int redIndex, int greenIndex, int blueIndex); - - /** - * Init with a RGB source, left, top, width, height specify the subregion area in original image; 'bytes' points to the beginning of image buffer (i.e. pixel (0,0)). - */ - [[deprecated]] // please use interface from ReadBarcode.h - GenericLuminanceSource(int left, int top, int width, int height, const void* bytes, int rowBytes, int pixelBytes, int redIndex, int greenIndex, int blueIndex); - - /** - * Init with a grayscale source. - */ - [[deprecated]] // please use interface from ReadBarcode.h - GenericLuminanceSource(int width, int height, const void* bytes, int rowBytes); - - /** - * Init with a grayscale source, left, top, width, height specify the subregion area in original image; 'bytes' points to the beginning of image buffer (i.e. pixel (0,0)). - */ - [[deprecated]] // please use interface from ReadBarcode.h - GenericLuminanceSource(int left, int top, int width, int height, const void* bytes, int rowBytes); - - /** - * Init with a grayscale source, left, top, width, height specify the subregion area in original image; 'bytes' points the beginning of image buffer (i.e. pixel (0,0)). - */ - [[deprecated]] // please use interface from ReadBarcode.h - GenericLuminanceSource(int left, int top, int width, int height, std::shared_ptr pixels, int rowBytes); - - virtual int width() const override; - virtual int height() const override; - virtual const uint8_t* getRow(int y, ByteArray& buffer, bool forceCopy = false) const override; - virtual const uint8_t* getMatrix(ByteArray& buffer, int& outRowBytes, bool forceCopy = false) const override; - virtual bool canCrop() const override; - virtual std::shared_ptr cropped(int left, int top, int width, int height) const override; - virtual bool canRotate() const override; - virtual std::shared_ptr rotated(int degreeCW) const override; - -private: - std::shared_ptr _pixels; - int _left; - int _top; - int _width; - int _height; - int _rowBytes; -}; - -} // ZXing diff --git a/core/src/GlobalHistogramBinarizer.cpp b/core/src/GlobalHistogramBinarizer.cpp index dbb4f766f0..2b25ac629c 100644 --- a/core/src/GlobalHistogramBinarizer.cpp +++ b/core/src/GlobalHistogramBinarizer.cpp @@ -19,13 +19,11 @@ #include "BitMatrix.h" #include "ByteArray.h" -#include "LuminanceSource.h" #include #include #include #include -#include #include namespace ZXing { @@ -34,34 +32,10 @@ static const int LUMINANCE_BITS = 5; static const int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS; static const int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS; - -struct GlobalHistogramBinarizer::DataCache -{ - std::once_flag once; - std::shared_ptr matrix; -}; - -GlobalHistogramBinarizer::GlobalHistogramBinarizer(std::shared_ptr source) : - _source(std::move(source)), - _cache(new DataCache) -{ -} +GlobalHistogramBinarizer::GlobalHistogramBinarizer(const ImageView& buffer) : BinaryBitmap(buffer) {} GlobalHistogramBinarizer::~GlobalHistogramBinarizer() = default; -int -GlobalHistogramBinarizer::width() const -{ - return _source->width(); -} - -int -GlobalHistogramBinarizer::height() const -{ - return _source->height(); -} - - // Return -1 on error static int EstimateBlackPoint(const std::array& buckets) { @@ -110,20 +84,21 @@ static int EstimateBlackPoint(const std::array& buckets) return bestValley << LUMINANCE_SHIFT; } -bool GlobalHistogramBinarizer::getPatternRow(int y, PatternRow& res) const +bool GlobalHistogramBinarizer::getPatternRow(int row, int rotation, PatternRow& res) const { - int width = _source->width(); - if (width < 3) + auto buffer = _buffer.rotated(rotation); + + if (buffer.width() < 3) return false; // special casing the code below for a width < 3 makes no sense res.clear(); - ByteArray buffer; - const uint8_t* luminances = _source->getRow(y, buffer); + const uint8_t* luminances = buffer.data(0, row); + const int pixStride = buffer.pixStride(); std::array buckets = {}; - for (int x = 0; x < width; x++) { - buckets[luminances[x] >> LUMINANCE_SHIFT]++; - } + for (int x = 0; x < buffer.width(); x++) + buckets[luminances[x * pixStride] >> LUMINANCE_SHIFT]++; + int blackPoint = EstimateBlackPoint(buckets); if (blackPoint <= 0) return false; @@ -135,20 +110,20 @@ bool GlobalHistogramBinarizer::getPatternRow(int y, PatternRow& res) const auto process = [&](bool val, const uint8_t* p) { if (val != lastVal) { - res.push_back(static_cast(p - lastPos)); + res.push_back(static_cast(p - lastPos) / pixStride); lastVal = val; lastPos = p; } }; - for (auto* p = luminances + 1; p < luminances + width - 1; ++p) - process((-*(p - 1) + (int(*p) * 4) - *(p + 1)) / 2 < blackPoint, p); + for (auto *p = luminances + pixStride, *e = luminances + (buffer.width() - 1) * pixStride; p < e; p += pixStride) + process((-*(p - pixStride) + (int(*p) * 4) - *(p + pixStride)) / 2 < blackPoint, p); - auto* backPos = luminances + width - 1; + auto* backPos = buffer.data(buffer.width() - 1, row); bool backVal = *backPos < blackPoint; process(backVal, backPos); - res.push_back(static_cast(backPos - lastPos + 1)); + res.push_back(static_cast((backPos - lastPos) / pixStride + 1)); if (backVal) res.push_back(0); // last value is number of white pixels, here 0 @@ -158,85 +133,36 @@ bool GlobalHistogramBinarizer::getPatternRow(int y, PatternRow& res) const return true; } -static void InitBlackMatrix(const LuminanceSource& source, std::shared_ptr& outMatrix) +// Does not sharpen the data, as this call is intended to only be used by 2D Readers. +std::shared_ptr +GlobalHistogramBinarizer::getBlackMatrix() const { - int width = source.width(); - int height = source.height(); - auto matrix = std::make_shared(width, height); - // Quickly calculates the histogram by sampling four rows from the image. This proved to be // more robust on the blackbox tests than sampling a diagonal as we used to do. std::array localBuckets = {}; { - ByteArray buffer; for (int y = 1; y < 5; y++) { - int row = height * y / 5; - const uint8_t* luminances = source.getRow(row, buffer); - int right = (width * 4) / 5; - for (int x = width / 5; x < right; x++) { + int row = height() * y / 5; + const uint8_t* luminances = _buffer.data(0, row); + int right = (width() * 4) / 5; + for (int x = width() / 5; x < right; x++) localBuckets[luminances[x] >> LUMINANCE_SHIFT]++; - } } } int blackPoint = EstimateBlackPoint(localBuckets); if (blackPoint <= 0) - return; + return {}; // We delay reading the entire image luminance until the black point estimation succeeds. // Although we end up reading four rows twice, it is consistent with our motto of // "fail quickly" which is necessary for continuous scanning. - ByteArray buffer; - int stride; - const uint8_t* luminances = source.getMatrix(buffer, stride); - for (int y = 0; y < height; y++) { - int offset = y * stride; - for (int x = 0; x < width; x++) { - if (luminances[offset + x] < blackPoint) { - matrix->set(x, y); - } - } - } - outMatrix = matrix; -} + auto matrix = std::make_shared(width(), height()); + for(int y = 0; y < height(); ++y) + for(int x = 0; x < width(); ++x) + matrix->set(x, y, *_buffer.data(x, y) < blackPoint); -// Does not sharpen the data, as this call is intended to only be used by 2D Readers. -std::shared_ptr -GlobalHistogramBinarizer::getBlackMatrix() const -{ - std::call_once(_cache->once, &InitBlackMatrix, std::cref(*_source), std::ref(_cache->matrix)); - return _cache->matrix; -} - -bool -GlobalHistogramBinarizer::canCrop() const -{ - return _source->canCrop(); -} - -std::shared_ptr -GlobalHistogramBinarizer::cropped(int left, int top, int width, int height) const -{ - return newInstance(_source->cropped(left, top, width, height)); + return matrix; } -bool -GlobalHistogramBinarizer::canRotate() const -{ - return _source->canRotate(); -} - -std::shared_ptr -GlobalHistogramBinarizer::rotated(int degreeCW) const -{ - return newInstance(_source->rotated(degreeCW)); -} - -std::shared_ptr -GlobalHistogramBinarizer::newInstance(const std::shared_ptr& source) const -{ - return std::make_shared(source); -} - - } // ZXing diff --git a/core/src/GlobalHistogramBinarizer.h b/core/src/GlobalHistogramBinarizer.h index 391969d29d..fbeac5d9df 100644 --- a/core/src/GlobalHistogramBinarizer.h +++ b/core/src/GlobalHistogramBinarizer.h @@ -18,12 +18,8 @@ #include "BinaryBitmap.h" -#include - namespace ZXing { -class LuminanceSource; - /** * This Binarizer implementation uses the old ZXing global histogram approach. It is suitable * for low-end mobile devices which don't have enough CPU or memory to use a local thresholding @@ -37,27 +33,12 @@ class LuminanceSource; */ class GlobalHistogramBinarizer : public BinaryBitmap { -protected: - std::shared_ptr _source; - public: - explicit GlobalHistogramBinarizer(std::shared_ptr source); + explicit GlobalHistogramBinarizer(const ImageView& buffer); ~GlobalHistogramBinarizer() override; - int width() const override; - int height() const override; - bool getPatternRow(int y, PatternRow &res) const override; + bool getPatternRow(int row, int rotation, PatternRow &res) const override; std::shared_ptr getBlackMatrix() const override; - bool canCrop() const override; - std::shared_ptr cropped(int left, int top, int width, int height) const override; - bool canRotate() const override; - std::shared_ptr rotated(int degreeCW) const override; - - virtual std::shared_ptr newInstance(const std::shared_ptr& source) const; - -private: - struct DataCache; - std::unique_ptr _cache; }; } // ZXing diff --git a/core/src/HybridBinarizer.cpp b/core/src/HybridBinarizer.cpp index 864ac44ea2..2a9bb7aea5 100644 --- a/core/src/HybridBinarizer.cpp +++ b/core/src/HybridBinarizer.cpp @@ -20,7 +20,6 @@ #include "BitMatrix.h" #include "BitMatrixIO.h" #include "ByteArray.h" -#include "LuminanceSource.h" #include "Matrix.h" #include "ZXContainerAlgorithms.h" @@ -37,17 +36,7 @@ static const int BLOCK_SIZE = 8; static const int MINIMUM_DIMENSION = BLOCK_SIZE * 5; static const int MIN_DYNAMIC_RANGE = 24; -struct HybridBinarizer::DataCache -{ - std::once_flag once; - std::shared_ptr matrix; -}; - -HybridBinarizer::HybridBinarizer(const std::shared_ptr& source) : - GlobalHistogramBinarizer(source), - _cache(new DataCache) -{ -} +HybridBinarizer::HybridBinarizer(const ImageView& iv) : GlobalHistogramBinarizer(iv) {} HybridBinarizer::~HybridBinarizer() = default; @@ -56,7 +45,7 @@ HybridBinarizer::~HybridBinarizer() = default; * See the following thread for a discussion of this algorithm: * http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0 */ -static Matrix CalculateBlackPoints(const uint8_t* luminances, int subWidth, int subHeight, int width, int height, int stride) +static Matrix CalculateBlackPoints(const uint8_t* luminances, int subWidth, int subHeight, int width, int height, int rowStride) { Matrix blackPoints(subWidth, subHeight); @@ -67,7 +56,7 @@ static Matrix CalculateBlackPoints(const uint8_t* luminances, int subWidth, int sum = 0; uint8_t min = 0xFF; uint8_t max = 0; - for (int yy = 0, offset = yoffset * stride + xoffset; yy < BLOCK_SIZE; yy++, offset += stride) { + for (int yy = 0, offset = yoffset * rowStride + xoffset; yy < BLOCK_SIZE; yy++, offset += rowStride) { for (int xx = 0; xx < BLOCK_SIZE; xx++) { auto pixel = luminances[offset + xx]; sum += pixel; @@ -77,7 +66,7 @@ static Matrix CalculateBlackPoints(const uint8_t* luminances, int subWidth, // short-circuit min/max tests once dynamic range is met if (max - min > MIN_DYNAMIC_RANGE) { // finish the rest of the rows quickly - for (yy++, offset += stride; yy < BLOCK_SIZE; yy++, offset += stride) { + for (yy++, offset += rowStride; yy < BLOCK_SIZE; yy++, offset += rowStride) { for (int xx = 0; xx < BLOCK_SIZE; xx++) { sum += luminances[offset + xx]; } @@ -121,17 +110,17 @@ static Matrix CalculateBlackPoints(const uint8_t* luminances, int subWidth, /** * Applies a single threshold to a block of pixels. */ -static void ThresholdBlock(const uint8_t* luminances, int xoffset, int yoffset, int threshold, int stride, BitMatrix& matrix) +static void ThresholdBlock(const uint8_t* luminances, int xoffset, int yoffset, int threshold, int rowStride, BitMatrix& matrix) { #ifdef ZX_FAST_BIT_STORAGE for (int y = yoffset; y < yoffset + BLOCK_SIZE; ++y) { - auto* src = luminances + y * stride + xoffset; + auto* src = luminances + y * rowStride + xoffset; auto* const dstBegin = matrix.row(y).begin() + xoffset; for (auto* dst = dstBegin; dst < dstBegin + BLOCK_SIZE; ++dst, ++src) *dst = *src <= threshold; } #else - for (int y = 0, offset = yoffset * stride + xoffset; y < BLOCK_SIZE; y++, offset += stride) { + for (int y = 0, offset = yoffset * rowStride + xoffset; y < BLOCK_SIZE; y++, offset += rowStride) { for (int x = 0; x < BLOCK_SIZE; x++) { // Comparison needs to be <= so that black == 0 pixels are black even if the threshold is 0. if (luminances[offset + x] <= threshold) { @@ -147,9 +136,11 @@ static void ThresholdBlock(const uint8_t* luminances, int xoffset, int yoffset, * of the blocks around it. Also handles the corner cases (fractional blocks are computed based * on the last pixels in the row/column which are also used in the previous block). */ -static void CalculateThresholdForBlock(const uint8_t* luminances, int subWidth, int subHeight, int width, int height, - int stride, const Matrix& blackPoints, BitMatrix& matrix) +static std::shared_ptr CalculateMatrix(const uint8_t* luminances, int subWidth, int subHeight, int width, + int height, int rowStride, const Matrix& blackPoints) { + auto matrix = std::make_shared(width, height); + for (int y = 0; y < subHeight; y++) { int yoffset = std::min(y * BLOCK_SIZE, height - BLOCK_SIZE); for (int x = 0; x < subWidth; x++) { @@ -163,52 +154,27 @@ static void CalculateThresholdForBlock(const uint8_t* luminances, int subWidth, } } int average = sum / 25; - ThresholdBlock(luminances, xoffset, yoffset, average, stride, matrix); + ThresholdBlock(luminances, xoffset, yoffset, average, rowStride, *matrix); } } -} - -/** -* Calculates the final BitMatrix once for all requests. This could be called once from the -* constructor instead, but there are some advantages to doing it lazily, such as making -* profiling easier, and not doing heavy lifting when callers don't expect it. -*/ -static void InitBlackMatrix(const LuminanceSource& source, std::shared_ptr& outMatrix) -{ - int width = source.width(); - int height = source.height(); - ByteArray buffer; - int stride; - const uint8_t* luminances = source.getMatrix(buffer, stride); - int subWidth = (width + BLOCK_SIZE - 1) / BLOCK_SIZE; // ceil(width/BS) - int subHeight = (height + BLOCK_SIZE - 1) / BLOCK_SIZE; // ceil(height/BS) - auto blackPoints = CalculateBlackPoints(luminances, subWidth, subHeight, width, height, stride); - - auto matrix = std::make_shared(width, height); - CalculateThresholdForBlock(luminances, subWidth, subHeight, width, height, stride, blackPoints, *matrix); - outMatrix = std::move(matrix); + return matrix; } -std::shared_ptr -HybridBinarizer::getBlackMatrix() const +std::shared_ptr HybridBinarizer::getBlackMatrix() const { - int width = _source->width(); - int height = _source->height(); - if (width >= MINIMUM_DIMENSION && height >= MINIMUM_DIMENSION) { - std::call_once(_cache->once, &InitBlackMatrix, std::cref(*_source), std::ref(_cache->matrix)); - return _cache->matrix; - } - else { + if (width() >= MINIMUM_DIMENSION && height() >= MINIMUM_DIMENSION) { + const uint8_t* luminances = _buffer.data(0, 0); + int subWidth = (width() + BLOCK_SIZE - 1) / BLOCK_SIZE; // ceil(width/BS) + int subHeight = (height() + BLOCK_SIZE - 1) / BLOCK_SIZE; // ceil(height/BS) + auto blackPoints = + CalculateBlackPoints(luminances, subWidth, subHeight, width(), height(), _buffer.rowStride()); + + return CalculateMatrix(luminances, subWidth, subHeight, width(), height(), _buffer.rowStride(), blackPoints); + } else { // If the image is too small, fall back to the global histogram approach. return GlobalHistogramBinarizer::getBlackMatrix(); } } -std::shared_ptr -HybridBinarizer::newInstance(const std::shared_ptr& source) const -{ - return std::make_shared(source); -} - } // ZXing diff --git a/core/src/HybridBinarizer.h b/core/src/HybridBinarizer.h index dcee037428..53fd8851d8 100644 --- a/core/src/HybridBinarizer.h +++ b/core/src/HybridBinarizer.h @@ -18,8 +18,6 @@ #include "GlobalHistogramBinarizer.h" -#include - namespace ZXing { /** @@ -42,15 +40,10 @@ namespace ZXing { class HybridBinarizer : public GlobalHistogramBinarizer { public: - explicit HybridBinarizer(const std::shared_ptr& source); + explicit HybridBinarizer(const ImageView& iv); ~HybridBinarizer() override; std::shared_ptr getBlackMatrix() const override; - std::shared_ptr newInstance(const std::shared_ptr& source) const override; - -private: - struct DataCache; - std::unique_ptr _cache; }; } // ZXing diff --git a/core/src/ImageView.h b/core/src/ImageView.h new file mode 100644 index 0000000000..dd1deb74ab --- /dev/null +++ b/core/src/ImageView.h @@ -0,0 +1,100 @@ +#pragma once +/* +* Copyright 2019 Axel Waggershauser +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include +#include + +namespace ZXing { + +enum class ImageFormat : uint32_t +{ + None = 0, + Lum = 0x01000000, + RGB = 0x03000102, + BGR = 0x03020100, + RGBX = 0x04000102, + XRGB = 0x04010203, + BGRX = 0x04020100, + XBGR = 0x04030201, +}; + +constexpr inline int PixStride(ImageFormat format) { return (static_cast(format) >> 3*8) & 0xFF; } +constexpr inline int RedIndex(ImageFormat format) { return (static_cast(format) >> 2*8) & 0xFF; } +constexpr inline int GreenIndex(ImageFormat format) { return (static_cast(format) >> 1*8) & 0xFF; } +constexpr inline int BlueIndex(ImageFormat format) { return (static_cast(format) >> 0*8) & 0xFF; } + +/** + * Simple class that stores a non-owning const pointer to image data plus layout and format information. + */ +class ImageView +{ +protected: + const uint8_t* _data = nullptr; + ImageFormat _format; + int _width = 0, _height = 0, _pixStride = 0, _rowStride = 0; + +// friend class ThresholdBinarizer; +// friend class GlobalHistogramBinarizer; +// friend class HybridBinarizer; +// friend class BinaryBitmap; + +public: + /** + * ImageView contructor + * + * @param data pointer to image buffer + * @param width image width in pixels + * @param height image height in pixels + * @param format image/pixel format + * @param rowStride optional row stride in bytes, default is width * pixStride + * @param pixStride optional pixel stride in bytes, default is calculated from format + */ + ImageView(const uint8_t* data, int width, int height, ImageFormat format, int rowStride = 0, int pixStride = 0) + : _data(data), _format(format), _width(width), _height(height), + _pixStride(pixStride ? pixStride : PixStride(format)), _rowStride(rowStride ? rowStride : width * _pixStride) + {} + + int width() const { return _width; } + int height() const { return _height; } + int pixStride() const { return _pixStride; } + int rowStride() const { return _rowStride; } + ImageFormat format() const { return _format; } + + const uint8_t* data(int x, int y) const { return _data + y * _rowStride + x * _pixStride; } + + ImageView cropped(int left, int top, int width, int height) const + { + left = std::max(0, left); + top = std::max(0, top); + width = width <= 0 ? (_width - left) : std::min(_width - left, width); + height = height <= 0 ? (_height - top) : std::min(_height - top, height); + return {data(left, top), width, height, _format, _rowStride, _pixStride}; + } + + ImageView rotated(int degree) const + { + switch ((degree + 360) % 360) { + case 90: return {data(0, _height - 1), _height, _width, _format, _pixStride, -_rowStride}; + case 180: return {data(_width - 1, _height - 1), _width, _height, _format, -_rowStride, -_pixStride}; + case 270: return {data(_width - 1, 0), _height, _width, _format, -_pixStride, _rowStride}; + } + return *this; + } +}; + +} // ZXing + diff --git a/core/src/LuminanceSource.cpp b/core/src/LuminanceSource.cpp deleted file mode 100644 index d80d51efcd..0000000000 --- a/core/src/LuminanceSource.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* -* Copyright 2016 Nu-book Inc. -* Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#include "LuminanceSource.h" - -#include "ByteArray.h" - -#include -#include -#include - -namespace ZXing { - -namespace { - -/** -* A wrapper implementation of {@link LuminanceSource} which inverts the luminances it returns -- black becomes -* white and vice versa, and each value becomes (255-value). -* -* @author Sean Owen -*/ -class InvertedLuminanceSource : public LuminanceSource -{ - std::shared_ptr _src; - -public: - explicit InvertedLuminanceSource(std::shared_ptr src) : _src(std::move(src)) {} - - const uint8_t* getRow(int y, ByteArray& outBytes, bool) const override - { - _src->getRow(y, outBytes, true); - std::transform(outBytes.begin(), outBytes.end(), outBytes.begin(), [](uint8_t b) { return 255 - b; }); - return outBytes.data(); - } - - const uint8_t* getMatrix(ByteArray& outBytes, int& outRowBytes, bool) const override - { - _src->getMatrix(outBytes, outRowBytes, true); - std::transform(outBytes.begin(), outBytes.end(), outBytes.begin(), [](uint8_t b) { return 255 - b; }); - return outBytes.data(); - } - - int width() const override - { - return _src->width(); - } - - /** - * @return The height of the bitmap. - */ - int height() const override - { - return _src->height(); - } - - bool canCrop() const override - { - return _src->canCrop(); - } - - std::shared_ptr cropped(int left, int top, int width, int height) const override - { - return CreateInverted(_src->cropped(left, top, width, height)); - } - - bool canRotate() const override - { - return _src->canRotate(); - } - - std::shared_ptr rotated(int degreeCW) const override - { - return CreateInverted(_src->rotated(degreeCW)); - } - -protected: - std::shared_ptr getInverted() const override - { - return _src; - } - -}; // InvertedLuminanceSource - -} // anonymous - - -bool -LuminanceSource::canCrop() const -{ - return false; -} - -std::shared_ptr -LuminanceSource::cropped(int, int, int, int) const -{ - throw std::runtime_error("This luminance source does not support cropping."); -} - -bool -LuminanceSource::canRotate() const -{ - return false; -} - -/** -* Returns a new object with rotated image data by 90 degrees counterclockwise. -* Only callable if {@link #isRotateSupported()} is true. -* -* @return A rotated version of this object. -*/ -std::shared_ptr -LuminanceSource::rotated(int) const -{ - throw std::runtime_error("This luminance source does not support rotation by 90 degrees."); -} - -std::shared_ptr -LuminanceSource::getInverted() const -{ - return nullptr; -} - -std::shared_ptr -LuminanceSource::CreateInverted(const std::shared_ptr& src) -{ - auto result = src->getInverted(); - if (result == nullptr) - result = std::make_shared(src); - return result; -} - -} // ZXing diff --git a/core/src/LuminanceSource.h b/core/src/LuminanceSource.h deleted file mode 100644 index 8f7b2bf769..0000000000 --- a/core/src/LuminanceSource.h +++ /dev/null @@ -1,114 +0,0 @@ -#pragma once -/* -* Copyright 2016 Nu-book Inc. -* Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#include - -namespace ZXing { - -class ByteArray; - -/** -* The purpose of this class hierarchy is to abstract different bitmap implementations across -* platforms into a standard interface for requesting greyscale luminance values. The interface -* only provides immutable methods; therefore crop and rotation create copies. This is to ensure -* that one Reader does not modify the original luminance source and leave it in an unknown state -* for other Readers in the chain. -* -* @author dswitkin@google.com (Daniel Switkin) -*/ -class LuminanceSource -{ -public: - virtual ~LuminanceSource() = default; - - /** - * @return The width of the bitmap. - */ - virtual int width() const = 0; - - /** - * @return The height of the bitmap. - */ - virtual int height() const = 0; - - /** - * Fetches one row of luminance data from the underlying platform's bitmap. Values range from - * 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have - * to bitwise and with 0xff for each value. It is preferable for implementations of this method - * to only fetch this row rather than the whole image, since no 2D Readers may be installed and - * getMatrix() may never be called. - * - * @param y The row to fetch, which must be in [0,getHeight()) - * @param row An optional preallocated array. If null or too small, it will be ignored. - * Always use the returned object, and ignore the .length of the array. - * @return An array containing the luminance data. - */ - virtual const uint8_t* getRow(int y, ByteArray& buffer, bool forceCopy = false) const = 0; - - /** - * Fetches luminance data for the underlying bitmap. Values should be fetched using: - * {@code int luminance = array[y * width + x] & 0xff} - * - * @return A row-major 2D array of luminance values. Do not use result.length as it may be - * larger than width * height bytes on some platforms. Do not modify the contents - * of the result. - */ - virtual const uint8_t* getMatrix(ByteArray& buffer, int& outRowBytes, bool forceCopy = false) const = 0; - - /** - * @return Whether this subclass supports cropping. - */ - virtual bool canCrop() const; - - /** - * Returns a new object with cropped image data. Implementations may keep a reference to the - * original data rather than a copy. Only callable if isCropSupported() is true. - * - * @param left The left coordinate, which must be in [0,getWidth()) - * @param top The top coordinate, which must be in [0,getHeight()) - * @param width The width of the rectangle to crop. - * @param height The height of the rectangle to crop. - * @return A cropped version of this object. - */ - virtual std::shared_ptr cropped(int left, int top, int width, int height) const; - - /** - * @return Whether this subclass supports counter-clockwise rotation. - */ - virtual bool canRotate() const; - - /** - * Returns a new object with rotated image data by 90 degrees counterclockwise. - * Only callable if {@link #isRotateSupported()} is true. - * - * @return A rotated version of this object. - */ - virtual std::shared_ptr rotated(int degreeCW) const; - - /** - * @return a wrapper of this {@code LuminanceSource} which inverts the luminances it returns -- black becomes - * white and vice versa, and each value becomes (255-value). - */ - static std::shared_ptr CreateInverted(const std::shared_ptr& src); - -protected: - virtual std::shared_ptr getInverted() const; - -}; - -} // ZXing diff --git a/core/src/ReadBarcode.cpp b/core/src/ReadBarcode.cpp index 67cfa03b4b..3c25862c1c 100644 --- a/core/src/ReadBarcode.cpp +++ b/core/src/ReadBarcode.cpp @@ -17,7 +17,6 @@ #include "ReadBarcode.h" #include "DecodeHints.h" -#include "GenericLuminanceSource.h" #include "GlobalHistogramBinarizer.h" #include "HybridBinarizer.h" #include "MultiFormatReader.h" @@ -27,38 +26,63 @@ namespace ZXing { -static Result ReadBarcode(GenericLuminanceSource&& source, const DecodeHints& hints) +class LumImage : public ImageView { - MultiFormatReader reader(hints); - auto srcPtr = std::shared_ptr(&source, [](void*) {}); + std::unique_ptr _memory; + LumImage(std::unique_ptr&& data, int w, int h) + : ImageView(data.get(), w, h, ImageFormat::Lum), _memory(std::move(data)) + {} - if (hints.binarizer() == Binarizer::LocalAverage) - return reader.read(HybridBinarizer(srcPtr)); - else - return reader.read(GlobalHistogramBinarizer(srcPtr)); +public: + LumImage() : ImageView(nullptr, 0, 0, ImageFormat::Lum) {} + LumImage(int w, int h) : LumImage(std::make_unique(w * h), w, h) {} + + uint8_t* data() { return _memory.get(); } +}; + +static uint8_t RGBToGray(unsigned r, unsigned g, unsigned b) +{ + // .299R + 0.587G + 0.114B (YUV/YIQ for PAL and NTSC), + // (306*R) >> 10 is approximately equal to R*0.299, and so on. + // 0x200 >> 10 is 0.5, it implements rounding. + return static_cast((306 * r + 601 * g + 117 * b + 0x200) >> 10); } -Result ReadBarcode(const ImageView& iv, const DecodeHints& hints) +template +static LumImage ExtractLum(const ImageView& iv, P projection) { + LumImage res(iv.width(), iv.height()); + + auto* dst = res.data(); + for(int y = 0; y < iv.height(); ++y) + for(int x = 0; x < iv.width(); ++x) + *dst++ = projection(iv.data(x, y)); + + return res; +} + +Result ReadBarcode(const ImageView& _iv, const DecodeHints& hints) +{ + LumImage lum; + ImageView iv = _iv; + + if (hints.binarizer() == Binarizer::GlobalHistogram || hints.binarizer() == Binarizer::LocalAverage) { + if (iv.format() != ImageFormat::Lum) { + lum = ExtractLum(iv, [r = RedIndex(iv.format()), g = GreenIndex(iv.format()), b = BlueIndex(iv.format())]( + const uint8_t* src) { return RGBToGray(src[r], src[g], src[b]); }); + } else if (iv.pixStride() != 1) { + // GlobalHistogram and LocalAverage need dense line memory layout + lum = ExtractLum(iv, [](const uint8_t* src) { return *src; }); + } + if (lum.data()) + iv = lum; + } + switch (hints.binarizer()) { case Binarizer::BoolCast: return MultiFormatReader(hints).read(ThresholdBinarizer(iv, 0)); case Binarizer::FixedThreshold: return MultiFormatReader(hints).read(ThresholdBinarizer(iv, 127)); - default: - return ReadBarcode( - { - 0, - 0, - iv._width, - iv._height, - iv._data, - iv._rowStride, - iv._pixStride, - RedIndex(iv._format), - GreenIndex(iv._format), - BlueIndex(iv._format), - nullptr - }, - hints); + case Binarizer::GlobalHistogram: return MultiFormatReader(hints).read(GlobalHistogramBinarizer(iv)); + case Binarizer::LocalAverage: return MultiFormatReader(hints).read(HybridBinarizer(iv)); } } diff --git a/core/src/ReadBarcode.h b/core/src/ReadBarcode.h index 901c78d37e..93e0ec938c 100644 --- a/core/src/ReadBarcode.h +++ b/core/src/ReadBarcode.h @@ -16,80 +16,11 @@ */ #include "DecodeHints.h" +#include "ImageView.h" #include "Result.h" -#include - namespace ZXing { -enum class ImageFormat : uint32_t -{ - None = 0, - Lum = 0x01000000, - RGB = 0x03000102, - BGR = 0x03020100, - RGBX = 0x04000102, - XRGB = 0x04010203, - BGRX = 0x04020100, - XBGR = 0x04030201, -}; - -constexpr inline int PixStride(ImageFormat format) { return (static_cast(format) >> 3*8) & 0xFF; } -constexpr inline int RedIndex(ImageFormat format) { return (static_cast(format) >> 2*8) & 0xFF; } -constexpr inline int GreenIndex(ImageFormat format) { return (static_cast(format) >> 1*8) & 0xFF; } -constexpr inline int BlueIndex(ImageFormat format) { return (static_cast(format) >> 0*8) & 0xFF; } - -/** - * Simple class that stores a non-owning const pointer to image data plus layout and format information. - */ -class ImageView -{ -protected: - const uint8_t* _data = nullptr; - ImageFormat _format; - int _width = 0, _height = 0, _pixStride = 0, _rowStride = 0; - - friend Result ReadBarcode(const ImageView&, const DecodeHints&); - friend class ThresholdBinarizer; - -public: - /** - * ImageView contructor - * - * @param data pointer to image buffer - * @param width image width in pixels - * @param height image height in pixels - * @param format image/pixel format - * @param rowStride optional row stride in bytes, default is width * pixStride - * @param pixStride optional pixel stride in bytes, default is calculated from format - */ - ImageView(const uint8_t* data, int width, int height, ImageFormat format, int rowStride = 0, int pixStride = 0) - : _data(data), _format(format), _width(width), _height(height), - _pixStride(pixStride ? pixStride : PixStride(format)), _rowStride(rowStride ? rowStride : width * _pixStride) - {} - - const uint8_t* data(int x, int y) const { return _data + y * _rowStride + x * _pixStride; } - - ImageView cropped(int left, int top, int width, int height) const - { - left = std::max(0, left); - top = std::max(0, top); - width = width <= 0 ? (_width - left) : std::min(_width - left, width); - height = height <= 0 ? (_height - top) : std::min(_height - top, height); - return {data(left, top), width, height, _format, _rowStride, _pixStride}; - } - - ImageView rotated(int degree) const - { - switch ((degree + 360) % 360) { - case 90: return {data(0, _height - 1), _height, _width, _format, _pixStride, -_rowStride}; - case 180: return {data(_width - 1, _height - 1), _width, _height, _format, -_rowStride, -_pixStride}; - case 270: return {data(_width - 1, 0), _height, _width, _format, -_pixStride, _rowStride}; - } - return *this; - } -}; - /** * Read barcode from an ImageView * diff --git a/core/src/ThresholdBinarizer.h b/core/src/ThresholdBinarizer.h index 51411082a1..32d27f96b1 100644 --- a/core/src/ThresholdBinarizer.h +++ b/core/src/ThresholdBinarizer.h @@ -17,32 +17,25 @@ #include "BinaryBitmap.h" #include "BitMatrix.h" -#include "ReadBarcode.h" #include -#include namespace ZXing { -class LuminanceSource; - class ThresholdBinarizer : public BinaryBitmap { - const ImageView _buffer; const uint8_t _threshold = 0; - mutable std::shared_ptr _cache; public: - ThresholdBinarizer(const ImageView& buffer, uint8_t threshold = 1) : _buffer(buffer), _threshold(threshold) {} - - int width() const override { return _buffer._width; } - int height() const override { return _buffer._height; } + ThresholdBinarizer(const ImageView& buffer, uint8_t threshold = 1) : BinaryBitmap(buffer), _threshold(threshold) {} - bool getPatternRow(int y, PatternRow& res) const override + bool getPatternRow(int row, int rotation, PatternRow& res) const override { - const int stride = _buffer._pixStride; - const uint8_t* begin = _buffer.data(0, y) + GreenIndex(_buffer._format); - const uint8_t* end = begin + _buffer._width * stride; + auto buffer = _buffer.rotated(rotation); + + const int stride = buffer.pixStride(); + const uint8_t* begin = buffer.data(0, row) + GreenIndex(buffer.format()); + const uint8_t* end = begin + buffer.width() * stride; auto* lastPos = begin; bool lastVal = false; @@ -66,40 +59,37 @@ class ThresholdBinarizer : public BinaryBitmap std::shared_ptr getBlackMatrix() const override { - if (!_cache) { - BitMatrix res(width(), height()); + BitMatrix res(width(), height()); #ifdef ZX_FAST_BIT_STORAGE - if (_buffer._pixStride == 1 && _buffer._rowStride == _buffer._width) { - // Specialize for a packed buffer with pixStride 1 to support auto vectorization (16x speedup on AVX2) - auto dst = res.row(0).begin(); - for (auto src = _buffer.data(0, 0), end = _buffer.data(0, height()); src != end; ++src, ++dst) - *dst = *src <= _threshold; - } else { - auto processLine = [this, &res](int y, const auto* src, const int stride) { - for (auto& dst : res.row(y)) { - dst = *src <= _threshold; - src += stride; - } - }; - for (int y = 0; y < res.height(); ++y) { - auto src = _buffer.data(0, y) + GreenIndex(_buffer._format); - // Specialize the inner loop for strides 1 and 4 to support auto vectorization - switch (_buffer._pixStride) { - case 1: processLine(y, src, 1); break; - case 4: processLine(y, src, 4); break; - default: processLine(y, src, _buffer._pixStride); break; - } + if (_buffer.pixStride() == 1 && _buffer.rowStride() == _buffer.width()) { + // Specialize for a packed buffer with pixStride 1 to support auto vectorization (16x speedup on AVX2) + auto dst = res.row(0).begin(); + for (auto src = _buffer.data(0, 0), end = _buffer.data(0, height()); src != end; ++src, ++dst) + *dst = *src <= _threshold; + } else { + auto processLine = [this, &res](int y, const auto* src, const int stride) { + for (auto& dst : res.row(y)) { + dst = *src <= _threshold; + src += stride; + } + }; + for (int y = 0; y < res.height(); ++y) { + auto src = _buffer.data(0, y) + GreenIndex(_buffer.format()); + // Specialize the inner loop for strides 1 and 4 to support auto vectorization + switch (_buffer.pixStride()) { + case 1: processLine(y, src, 1); break; + case 4: processLine(y, src, 4); break; + default: processLine(y, src, _buffer.pixStride()); break; } } + } #else - const int channel = GreenIndex(_buffer._format); - for (int y = 0; y < res.height(); ++y) - for (int x = 0; x < res.width(); ++x) - res.set(x, y, _buffer.data(x, y)[channel] <= _threshold); + const int channel = GreenIndex(_buffer.format()); + for (int y = 0; y < res.height(); ++y) + for (int x = 0; x < res.width(); ++x) + res.set(x, y, _buffer.data(x, y)[channel] <= _threshold); #endif - _cache = std::make_shared(std::move(res)); - } - return _cache; + return std::make_shared(std::move(res)); } }; diff --git a/core/src/aztec/AZReader.cpp b/core/src/aztec/AZReader.cpp index 287e26d134..4091f30dac 100644 --- a/core/src/aztec/AZReader.cpp +++ b/core/src/aztec/AZReader.cpp @@ -38,7 +38,7 @@ Reader::Reader(const DecodeHints& hints) Result Reader::decode(const BinaryBitmap& image) const { - auto binImg = image.getBlackMatrix(); + auto binImg = image.getBitMatrix(); if (binImg == nullptr) { return Result(DecodeStatus::NotFound); } diff --git a/core/src/datamatrix/DMReader.cpp b/core/src/datamatrix/DMReader.cpp index ee02ec408d..d134f778bb 100644 --- a/core/src/datamatrix/DMReader.cpp +++ b/core/src/datamatrix/DMReader.cpp @@ -46,7 +46,7 @@ Reader::Reader(const DecodeHints& hints) Result Reader::decode(const BinaryBitmap& image) const { - auto binImg = image.getBlackMatrix(); + auto binImg = image.getBitMatrix(); if (binImg == nullptr) { return Result(DecodeStatus::NotFound); } diff --git a/core/src/maxicode/MCReader.cpp b/core/src/maxicode/MCReader.cpp index 6d464010f2..36c5a53607 100644 --- a/core/src/maxicode/MCReader.cpp +++ b/core/src/maxicode/MCReader.cpp @@ -59,7 +59,7 @@ Reader::Reader(const DecodeHints& hints) : _isPure(hints.isPure()), _characterSe Result Reader::decode(const BinaryBitmap& image) const { - auto binImg = image.getBlackMatrix(); + auto binImg = image.getBitMatrix(); if (binImg == nullptr) { return Result(DecodeStatus::NotFound); } diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index 3bdd923361..a278312387 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -73,20 +73,18 @@ Reader::~Reader() = default; * rowStep is bigger as the image is taller, but is always at least 1. We've somewhat arbitrarily * decided that moving up and down by about 1/16 of the image is pretty good; we try more of the * image if "trying harder". -* -* @param image The image to decode -* @param hints Any hints that were requested -* @return The contents of the decoded barcode -* @throws NotFoundException Any spontaneous errors which occur */ -static Result -DoDecode(const std::vector>& readers, const BinaryBitmap& image, bool tryHarder, bool isPure) +static Result DoDecode(const std::vector>& readers, const BinaryBitmap& image, + bool tryHarder, bool rotate, bool isPure) { std::vector> decodingState(readers.size()); int width = image.width(); int height = image.height(); + if (rotate) + std::swap(width, height); + int middle = height / 2; int rowStep = std::max(1, height / (tryHarder ? 256 : 32)); int maxLines = tryHarder ? @@ -107,7 +105,7 @@ DoDecode(const std::vector>& readers, const BinaryBit break; } - if (!image.getPatternRow(rowNumber, bars)) + if (!image.getPatternRow(rowNumber, rotate ? 270 : 0, bars)) continue; // While we have the image data in a PatternRow, it's fairly cheap to reverse it in place to @@ -150,15 +148,14 @@ DoDecode(const std::vector>& readers, const BinaryBit Result Reader::decode(const BinaryBitmap& image) const { - Result result = DoDecode(_readers, image, _tryHarder, _isPure); + Result result = DoDecode(_readers, image, _tryHarder, false, _isPure); - if (!result.isValid() && _tryRotate && image.canRotate()) { - auto rotatedImage = image.rotated(270); - result = DoDecode(_readers, *rotatedImage, _tryHarder, _isPure); + if (!result.isValid() && _tryRotate) { + result = DoDecode(_readers, image, _tryHarder, true, _isPure); if (result.isValid()) { // Update position auto points = result.position(); - int height = rotatedImage->height(); + int height = image.width(); for (auto& p : points) { p = {height - p.y - 1, p.x}; } diff --git a/core/src/pdf417/PDFDetector.cpp b/core/src/pdf417/PDFDetector.cpp index 11f8fa2b2b..4570d075b3 100644 --- a/core/src/pdf417/PDFDetector.cpp +++ b/core/src/pdf417/PDFDetector.cpp @@ -348,8 +348,10 @@ bool HasStartPattern(const BitMatrix& m) DecodeStatus Detector::Detect(const BinaryBitmap& image, bool multiple, Result& result) { - auto binImg = image.getBlackMatrix(); - if (binImg == nullptr) { + // construct a 'dummy' shared pointer, just be able to pass it up the call chain in DecodeStatus + // TODO: reimplement PDF Detector + auto binImg = std::shared_ptr(image.getBitMatrix(), [](const BitMatrix*){}); + if (!binImg) { return DecodeStatus::NotFound; } diff --git a/core/src/pdf417/PDFReader.cpp b/core/src/pdf417/PDFReader.cpp index bec478ebf9..93ed83d9e3 100644 --- a/core/src/pdf417/PDFReader.cpp +++ b/core/src/pdf417/PDFReader.cpp @@ -271,7 +271,7 @@ DecoderResult DecodeCodewords(std::vector& codewords, int ecLevel, const st static Result DecodePure(const BinaryBitmap& image_, const std::string& characterSet) { - auto pimage = image_.getBlackMatrix(); + auto pimage = image_.getBitMatrix(); if (!pimage) return Result(DecodeStatus::NotFound); auto& image = *pimage; diff --git a/core/src/qrcode/QRReader.cpp b/core/src/qrcode/QRReader.cpp index 208e1222b5..7e54648579 100644 --- a/core/src/qrcode/QRReader.cpp +++ b/core/src/qrcode/QRReader.cpp @@ -37,7 +37,7 @@ Reader::Reader(const DecodeHints& hints) Result Reader::decode(const BinaryBitmap& image) const { - auto binImg = image.getBlackMatrix(); + auto binImg = image.getBitMatrix(); if (binImg == nullptr) { return Result(DecodeStatus::NotFound); } diff --git a/thirdparty/stb/stb_image.h b/thirdparty/stb/stb_image.h index bf283b7bf7..ee8f61c95a 100644 --- a/thirdparty/stb/stb_image.h +++ b/thirdparty/stb/stb_image.h @@ -1646,7 +1646,7 @@ static stbi_uc stbi__compute_y(int r, int g, int b) { #if 0 // ori return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); -#else // zxing (see GenericLuminanceSource.cpp:RGBToGray) +#else // zxing (see ReadBarcode.cpp:RGBToGray) return (stbi_uc) ((306 * r + 601 * g + 117 * b + 0x200) >> 10); #endif } From 293d4230f0c4bb0f5dba39dc38d081e3811a7dd4 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Mon, 19 Jul 2021 10:47:25 +0200 Subject: [PATCH 008/185] fuzzer: fix unnoticed build regression from RSS->DataBar renaming --- test/fuzz/fuzzDBEDecoder.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/fuzz/fuzzDBEDecoder.cpp b/test/fuzz/fuzzDBEDecoder.cpp index e9d624c93c..4a14761611 100644 --- a/test/fuzz/fuzzDBEDecoder.cpp +++ b/test/fuzz/fuzzDBEDecoder.cpp @@ -5,7 +5,6 @@ #include "oned/rss/ODRSSExpandedBinaryDecoder.h" using namespace ZXing; -using namespace ZXing::OneD::RSS; extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { @@ -16,7 +15,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) for (size_t i = 0; i < size; ++i) bits.appendBits(data[i], 8); - ExpandedBinaryDecoder::Decode(bits); + OneD::DataBar::DecodeExpandedBits(bits); return 0; } From ca4b3c27296b2f343b3c7db14ce833d6c705228c Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Tue, 20 Jul 2021 14:21:53 +0200 Subject: [PATCH 009/185] [[deprecated]]: fix regression with rotated 1D symbols (uint_16 overflow) --- core/src/GlobalHistogramBinarizer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/GlobalHistogramBinarizer.cpp b/core/src/GlobalHistogramBinarizer.cpp index 2b25ac629c..59ad786b43 100644 --- a/core/src/GlobalHistogramBinarizer.cpp +++ b/core/src/GlobalHistogramBinarizer.cpp @@ -110,7 +110,7 @@ bool GlobalHistogramBinarizer::getPatternRow(int row, int rotation, PatternRow& auto process = [&](bool val, const uint8_t* p) { if (val != lastVal) { - res.push_back(static_cast(p - lastPos) / pixStride); + res.push_back(static_cast((p - lastPos) / pixStride)); lastVal = val; lastPos = p; } From a5895c596bd32045181745e91ab7d1339cefdfb9 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Tue, 20 Jul 2021 14:36:04 +0200 Subject: [PATCH 010/185] OneD: scan the whole line, not only until the first of the left-guard If a line contained a valid symbol after a truncated (invalid) one, it would never detect the valid symbol. This fixes this limitation. This fixes #220 and finds 2 more existing black-box samples. It also prepares #138. --- core/src/Pattern.h | 2 +- core/src/oned/ODCodabarReader.cpp | 4 ++-- core/src/oned/ODCodabarReader.h | 2 +- core/src/oned/ODCode128Reader.cpp | 4 ++-- core/src/oned/ODCode128Reader.h | 2 +- core/src/oned/ODCode39Reader.cpp | 4 ++-- core/src/oned/ODCode39Reader.h | 2 +- core/src/oned/ODCode93Reader.cpp | 4 ++-- core/src/oned/ODCode93Reader.h | 2 +- core/src/oned/ODDataBarExpandedReader.cpp | 13 ++++++----- core/src/oned/ODDataBarExpandedReader.h | 2 +- core/src/oned/ODDataBarReader.cpp | 9 +++++--- core/src/oned/ODDataBarReader.h | 2 +- core/src/oned/ODITFReader.cpp | 6 ++--- core/src/oned/ODITFReader.h | 2 +- core/src/oned/ODMultiUPCEANReader.cpp | 9 +++++--- core/src/oned/ODMultiUPCEANReader.h | 2 +- core/src/oned/ODReader.cpp | 26 +++++++++++++--------- core/src/oned/ODRowReader.cpp | 3 ++- core/src/oned/ODRowReader.h | 2 +- test/blackbox/BlackboxTestRunner.cpp | 14 ++++++------ test/samples/itf-1/#220.png | Bin 0 -> 314 bytes test/samples/itf-1/#220.txt | 1 + test/unit/oned/ODCode128ReaderTest.cpp | 3 ++- 24 files changed, 68 insertions(+), 52 deletions(-) create mode 100644 test/samples/itf-1/#220.png create mode 100644 test/samples/itf-1/#220.txt diff --git a/core/src/Pattern.h b/core/src/Pattern.h index ce1cfda8c8..c317be3e57 100644 --- a/core/src/Pattern.h +++ b/core/src/Pattern.h @@ -124,7 +124,7 @@ class PatternView void extend() { - _size = static_cast(_end - _data); + _size = std::max(0, static_cast(_end - _data)); } }; diff --git a/core/src/oned/ODCodabarReader.cpp b/core/src/oned/ODCodabarReader.cpp index b81978de06..d528d043e4 100644 --- a/core/src/oned/ODCodabarReader.cpp +++ b/core/src/oned/ODCodabarReader.cpp @@ -62,7 +62,7 @@ bool IsLeftGuard(const PatternView& view, int spaceInPixel) } Result -CodabarReader::decodePattern(int rowNumber, const PatternView& row, std::unique_ptr&) const +CodabarReader::decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const { // minimal number of characters that must be present (including start, stop and checksum characters) // absolute minimum would be 2 (meaning 0 'content'). everything below 4 produces too many false @@ -70,7 +70,7 @@ CodabarReader::decodePattern(int rowNumber, const PatternView& row, std::unique_ const int minCharCount = 4; auto isStartOrStopSymbol = [](char c) { return 'A' <= c && c <= 'D'; }; - auto next = FindLeftGuard(row, minCharCount * CHAR_LEN, IsLeftGuard); + next = FindLeftGuard(next, minCharCount * CHAR_LEN, IsLeftGuard); if (!next.isValid()) return Result(DecodeStatus::NotFound); diff --git a/core/src/oned/ODCodabarReader.h b/core/src/oned/ODCodabarReader.h index b004c5064c..69945c3d6d 100644 --- a/core/src/oned/ODCodabarReader.h +++ b/core/src/oned/ODCodabarReader.h @@ -34,7 +34,7 @@ class CodabarReader : public RowReader { public: explicit CodabarReader(const DecodeHints& hints); - Result decodePattern(int rowNumber, const PatternView& row, std::unique_ptr& state) const override; + Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr& state) const override; private: bool _returnStartEnd; diff --git a/core/src/oned/ODCode128Reader.cpp b/core/src/oned/ODCode128Reader.cpp index 5af91d0455..5f8038f4e1 100644 --- a/core/src/oned/ODCode128Reader.cpp +++ b/core/src/oned/ODCode128Reader.cpp @@ -214,7 +214,7 @@ constexpr int CHARACTER_ENCODINGS[] = { }; #endif -Result Code128Reader::decodePattern(int rowNumber, const PatternView& row, std::unique_ptr&) const +Result Code128Reader::decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const { int minCharCount = 4; // start + payload + checksum + stop auto decodePattern = [](const PatternView& view, bool start = false) { @@ -229,7 +229,7 @@ Result Code128Reader::decodePattern(int rowNumber, const PatternView& row, std:: #endif }; - auto next = FindLeftGuard(row, minCharCount * CHAR_LEN, START_PATTERN_PREFIX, QUIET_ZONE); + next = FindLeftGuard(next, minCharCount * CHAR_LEN, START_PATTERN_PREFIX, QUIET_ZONE); if (!next.isValid()) return Result(DecodeStatus::NotFound); diff --git a/core/src/oned/ODCode128Reader.h b/core/src/oned/ODCode128Reader.h index 624784641b..6116f13dfe 100644 --- a/core/src/oned/ODCode128Reader.h +++ b/core/src/oned/ODCode128Reader.h @@ -33,7 +33,7 @@ class Code128Reader : public RowReader { public: explicit Code128Reader(const DecodeHints& hints); - Result decodePattern(int rowNumber, const PatternView& row, std::unique_ptr&) const override; + Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const override; private: bool _convertFNC1; diff --git a/core/src/oned/ODCode39Reader.cpp b/core/src/oned/ODCode39Reader.cpp index 9cea7bdbf3..d4bde47b4f 100644 --- a/core/src/oned/ODCode39Reader.cpp +++ b/core/src/oned/ODCode39Reader.cpp @@ -92,7 +92,7 @@ Code39Reader::Code39Reader(const DecodeHints& hints) : { } -Result Code39Reader::decodePattern(int rowNumber, const PatternView& row, std::unique_ptr&) const +Result Code39Reader::decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const { // minimal number of characters that must be present (including start, stop and checksum characters) int minCharCount = _usingCheckDigit ? 4 : 3; @@ -103,7 +103,7 @@ Result Code39Reader::decodePattern(int rowNumber, const PatternView& row, std::u // quiet zone is half the width of a character symbol constexpr float QUIET_ZONE_SCALE = 0.5f; - auto next = FindLeftGuard(row, minCharCount * CHAR_LEN, START_PATTERN, QUIET_ZONE_SCALE * 12); + next = FindLeftGuard(next, minCharCount * CHAR_LEN, START_PATTERN, QUIET_ZONE_SCALE * 12); if (!next.isValid()) return Result(DecodeStatus::NotFound); diff --git a/core/src/oned/ODCode39Reader.h b/core/src/oned/ODCode39Reader.h index c6a53448d3..0573f756fb 100644 --- a/core/src/oned/ODCode39Reader.h +++ b/core/src/oned/ODCode39Reader.h @@ -45,7 +45,7 @@ class Code39Reader : public RowReader */ explicit Code39Reader(const DecodeHints& hints); - Result decodePattern(int rowNumber, const PatternView& row, std::unique_ptr&) const override; + Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const override; private: bool _extendedMode; diff --git a/core/src/oned/ODCode93Reader.cpp b/core/src/oned/ODCode93Reader.cpp index 8a36775944..22298f8972 100644 --- a/core/src/oned/ODCode93Reader.cpp +++ b/core/src/oned/ODCode93Reader.cpp @@ -91,12 +91,12 @@ static bool IsStartGuard(const PatternView& window, int spaceInPixel) RowReader::OneToFourBitPattern(window) == ASTERISK_ENCODING; } -Result Code93Reader::decodePattern(int rowNumber, const PatternView& row, std::unique_ptr&) const +Result Code93Reader::decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const { // minimal number of characters that must be present (including start, stop, checksum and 1 payload characters) int minCharCount = 5; - auto next = FindLeftGuard(row, minCharCount * CHAR_LEN, IsStartGuard); + next = FindLeftGuard(next, minCharCount * CHAR_LEN, IsStartGuard); if (!next.isValid()) return Result(DecodeStatus::NotFound); diff --git a/core/src/oned/ODCode93Reader.h b/core/src/oned/ODCode93Reader.h index 9455019a90..95a385e0ad 100644 --- a/core/src/oned/ODCode93Reader.h +++ b/core/src/oned/ODCode93Reader.h @@ -30,7 +30,7 @@ namespace OneD { class Code93Reader : public RowReader { public: - Result decodePattern(int rowNumber, const PatternView& row, std::unique_ptr&) const override; + Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const override; }; } // OneD diff --git a/core/src/oned/ODDataBarExpandedReader.cpp b/core/src/oned/ODDataBarExpandedReader.cpp index 3bf1683c09..3db777b30d 100644 --- a/core/src/oned/ODDataBarExpandedReader.cpp +++ b/core/src/oned/ODDataBarExpandedReader.cpp @@ -201,16 +201,15 @@ static Pair ReadPair(const PatternView& view, Direction dir) } template -static Pairs ReadRowOfPairs(const PatternView& view, int rowNumber) +static Pairs ReadRowOfPairs(PatternView& next, int rowNumber) { Pairs pairs; Pair pair; - PatternView next; if constexpr (STACKED) { // a possible first pair is either left2right starting on a space or right2left starting on a bar. // it might be a half-pair - next = view.subView(0, HALF_PAIR_SIZE); + next = next.subView(0, HALF_PAIR_SIZE); while (next.shift(1)) { if (IsL2RPair(next) && (pair = ReadPair(next, Direction::Right)) && (pair.finder != FINDER_A || IsGuard(next[-1], next[11]))) @@ -221,7 +220,7 @@ static Pairs ReadRowOfPairs(const PatternView& view, int rowNumber) } else { // the only possible first pair is a full, left2right FINDER_A pair starting on a space // with a guard bar on the left - next = view.subView(-1, FULL_PAIR_SIZE); + next = next.subView(-1, FULL_PAIR_SIZE); while (next.shift(2)) { if (IsL2RPair(next) && IsGuard(next[-1], next[11]) && (pair = ReadPair(next, Direction::Right)).finder == FINDER_A) @@ -231,8 +230,10 @@ static Pairs ReadRowOfPairs(const PatternView& view, int rowNumber) next = next.subView(0, HALF_PAIR_SIZE); } - if (!pair) + if (!pair) { + next = {}; // if we didn't find a single pair, consume the rest of the row return {}; + } auto flippedDir = [](Pair p) { return p.finder < 0 ? Direction::Right : Direction::Left; }; auto isValidPair = [](Pair p, PatternView v) { return p.right || IsGuard(v[p.finder < 0 ? 9 : 11], v[13]); }; @@ -333,7 +334,7 @@ struct DBERState : public RowReader::DecodingState PairMap allPairs; }; -Result DataBarExpandedReader::decodePattern(int rowNumber, const PatternView& view, +Result DataBarExpandedReader::decodePattern(int rowNumber, PatternView& view, std::unique_ptr& state) const { #if 0 // non-stacked version diff --git a/core/src/oned/ODDataBarExpandedReader.h b/core/src/oned/ODDataBarExpandedReader.h index bbc93411df..a2e9a306d6 100644 --- a/core/src/oned/ODDataBarExpandedReader.h +++ b/core/src/oned/ODDataBarExpandedReader.h @@ -33,7 +33,7 @@ class DataBarExpandedReader : public RowReader explicit DataBarExpandedReader(const DecodeHints& hints); ~DataBarExpandedReader() override; - Result decodePattern(int rowNumber, const PatternView& row, std::unique_ptr& state) const override; + Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr& state) const override; }; } // OneD diff --git a/core/src/oned/ODDataBarReader.cpp b/core/src/oned/ODDataBarReader.cpp index 3be9fe5b38..ab0f276a72 100644 --- a/core/src/oned/ODDataBarReader.cpp +++ b/core/src/oned/ODDataBarReader.cpp @@ -163,11 +163,11 @@ struct State : public RowReader::DecodingState std::unordered_set rightPairs; }; -Result DataBarReader::decodePattern(int rowNumber, const PatternView& view, +Result DataBarReader::decodePattern(int rowNumber, PatternView& next, std::unique_ptr& state) const { #if 0 // non-stacked version - auto next = view.subView(-1, FULL_PAIR_SIZE + 2); + next = next.subView(-1, FULL_PAIR_SIZE + 2); // yes: the first view we test is at index 1 (black bar at 0 would be the guard pattern) while (next.shift(2)) { if (IsLeftPair(next)) { @@ -184,7 +184,7 @@ Result DataBarReader::decodePattern(int rowNumber, const PatternView& view, state.reset(new State); auto* prevState = static_cast(state.get()); - auto next = view.subView(0, FULL_PAIR_SIZE + 2); // +2 reflects the guard pattern on the right + next = next.subView(0, FULL_PAIR_SIZE + 2); // +2 reflects the guard pattern on the right // yes: the first view we test is at index 1 (black bar at 0 would be the guard pattern) while (next.shift(1)) { if (IsLeftPair(next)) { @@ -210,6 +210,9 @@ Result DataBarReader::decodePattern(int rowNumber, const PatternView& view, EstimatePosition(leftPair, rightPair), BarcodeFormat::DataBar}; #endif + // guaratee progress (see loop in ODReader.cpp) + next = {}; + return Result(DecodeStatus::NotFound); } diff --git a/core/src/oned/ODDataBarReader.h b/core/src/oned/ODDataBarReader.h index 1402b6991d..2da0109691 100644 --- a/core/src/oned/ODDataBarReader.h +++ b/core/src/oned/ODDataBarReader.h @@ -33,7 +33,7 @@ class DataBarReader : public RowReader explicit DataBarReader(const DecodeHints& hints); ~DataBarReader() override; - Result decodePattern(int rowNumber, const PatternView& row, std::unique_ptr& state) const override; + Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr& state) const override; }; } // OneD diff --git a/core/src/oned/ODITFReader.cpp b/core/src/oned/ODITFReader.cpp index 5b1224952d..124a009a7d 100644 --- a/core/src/oned/ODITFReader.cpp +++ b/core/src/oned/ODITFReader.cpp @@ -40,12 +40,12 @@ constexpr auto START_PATTERN_ = FixedPattern<4, 4>{1, 1, 1, 1}; constexpr auto STOP_PATTERN_1 = FixedPattern<3, 4>{2, 1, 1}; constexpr auto STOP_PATTERN_2 = FixedPattern<3, 5>{3, 1, 1}; -Result ITFReader::decodePattern(int rowNumber, const PatternView& row, std::unique_ptr&) const +Result ITFReader::decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const { const int minCharCount = 6; const int minQuietZone = 10; - auto next = FindLeftGuard(row, 4 + minCharCount/2 + 3, START_PATTERN_, minQuietZone); + next = FindLeftGuard(next, 4 + minCharCount/2 + 3, START_PATTERN_, minQuietZone); if (!next.isValid()) return Result(DecodeStatus::NotFound); @@ -56,7 +56,7 @@ Result ITFReader::decodePattern(int rowNumber, const PatternView& row, std::uniq int xStart = next.pixelsInFront(); next = next.subView(4, 10); - while (next.index() < row.size() - (10 + 3)) { + while (next.isValid()) { const auto threshold = NarrowWideThreshold(next); if (!threshold.isValid()) break; diff --git a/core/src/oned/ODITFReader.h b/core/src/oned/ODITFReader.h index 45085b9eb6..b7a05be3cc 100644 --- a/core/src/oned/ODITFReader.h +++ b/core/src/oned/ODITFReader.h @@ -46,7 +46,7 @@ class ITFReader : public RowReader { public: explicit ITFReader(const DecodeHints& hints); - Result decodePattern(int rowNumber, const PatternView& row, std::unique_ptr&) const override; + Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const override; private: std::vector _allowedLengths; diff --git a/core/src/oned/ODMultiUPCEANReader.cpp b/core/src/oned/ODMultiUPCEANReader.cpp index 136a724c92..0d8e8d825f 100644 --- a/core/src/oned/ODMultiUPCEANReader.cpp +++ b/core/src/oned/ODMultiUPCEANReader.cpp @@ -277,15 +277,16 @@ static bool AddOn(PartialResult& res, PatternView begin, int digitCount) return true; } -Result MultiUPCEANReader::decodePattern(int rowNumber, const PatternView& row, std::unique_ptr&) const +Result MultiUPCEANReader::decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const { const int minSize = 3 + 6*4 + 6; // UPC-E - auto begin = FindLeftGuard(row, minSize, END_PATTERN, QUIET_ZONE_LEFT); - if (!begin.isValid()) + next = FindLeftGuard(next, minSize, END_PATTERN, QUIET_ZONE_LEFT); + if (!next.isValid()) return Result(DecodeStatus::NotFound); PartialResult res; + auto begin = next; if (!(((_hints.hasFormat(BarcodeFormat::EAN13 | BarcodeFormat::UPCA)) && EAN13(res, begin)) || (_hints.hasFormat(BarcodeFormat::EAN8) && EAN8(res, begin)) || @@ -312,6 +313,8 @@ Result MultiUPCEANReader::decodePattern(int rowNumber, const PatternView& row, s res.txt += " " + addOnRes.txt; } + next = res.end; + if (_hints.eanAddOnSymbol() == EanAddOnSymbol::Require && !addOnRes.isValid()) return Result(DecodeStatus::NotFound); diff --git a/core/src/oned/ODMultiUPCEANReader.h b/core/src/oned/ODMultiUPCEANReader.h index 9fa6281f62..9d3154331e 100644 --- a/core/src/oned/ODMultiUPCEANReader.h +++ b/core/src/oned/ODMultiUPCEANReader.h @@ -36,7 +36,7 @@ class MultiUPCEANReader : public RowReader explicit MultiUPCEANReader(const DecodeHints& hints); ~MultiUPCEANReader() override; - Result decodePattern(int rowNumber, const PatternView& row, std::unique_ptr&) const override; + Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const override; private: bool _canReturnUPCA = false; diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index a278312387..6626e03cc6 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -123,18 +123,24 @@ static Result DoDecode(const std::vector>& readers, c } // Look for a barcode for (size_t r = 0; r < readers.size(); ++r) { - Result result = readers[r]->decodePattern(rowNumber, bars, decodingState[r]); - if (result.isValid()) { - if (upsideDown) { - // update position (flip horizontally). - auto points = result.position(); - for (auto& p : points) { - p = {width - p.x - 1, p.y}; + PatternView next(bars); + do { + Result result = readers[r]->decodePattern(rowNumber, next, decodingState[r]); + if (result.isValid()) { + if (upsideDown) { + // update position (flip horizontally). + auto points = result.position(); + for (auto& p : points) { + p = {width - p.x - 1, p.y}; + } + result.setPosition(std::move(points)); } - result.setPosition(std::move(points)); + return result; } - return result; - } + // make sure we make progress and we start the next try on a bar + next.shift(2 - (next.index() % 2)); + next.extend(); + } while (tryHarder && next.isValid()); } } diff --git a/core/src/oned/ODRowReader.cpp b/core/src/oned/ODRowReader.cpp index c3de5c5532..de0febc20e 100644 --- a/core/src/oned/ODRowReader.cpp +++ b/core/src/oned/ODRowReader.cpp @@ -39,7 +39,8 @@ Result RowReader::decodeSingleRow(int rowNumber, const BitArray& row) const if (*(i-1)) res.push_back(0); - return decodePattern(rowNumber, res, state); + PatternView view(res); + return decodePattern(rowNumber, view, state); } } // namespace ZXing::OneD diff --git a/core/src/oned/ODRowReader.h b/core/src/oned/ODRowReader.h index ca12e55270..45fbcdef40 100644 --- a/core/src/oned/ODRowReader.h +++ b/core/src/oned/ODRowReader.h @@ -70,7 +70,7 @@ class RowReader virtual ~RowReader() {} - virtual Result decodePattern(int rowNumber, const PatternView& row, std::unique_ptr& state) const = 0; + virtual Result decodePattern(int rowNumber, PatternView& next, std::unique_ptr& state) const = 0; /** * Determines how closely a set of observed counts of runs of black/white values matches a given diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 0df58ba4a4..168886adfb 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -384,8 +384,8 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set }); runTests("ean13-2", "EAN-13", 24, { - { 7, 13, 0 }, - { 7, 13, 180 }, + { 7, 14, 0 }, + { 7, 14, 180 }, }); runTests("ean13-3", "EAN-13", 21, { @@ -403,9 +403,9 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set { 3, 5, 180 }, }, DecodeHints().setEanAddOnSymbol(EanAddOnSymbol::Require)); - runTests("itf-1", "ITF", 10, { - { 10, 10, 0 }, - { 10, 10, 180 }, + runTests("itf-1", "ITF", 11, { + { 10, 11, 0 }, + { 10, 11, 180 }, }); runTests("itf-2", "ITF", 6, { @@ -427,8 +427,8 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set }); runTests("upca-2", "UPC-A", 36, { - { 17, 22, 0 }, - { 18, 22, 180 }, + { 17, 23, 0 }, + { 18, 23, 180 }, }); runTests("upca-3", "UPC-A", 21, { diff --git a/test/samples/itf-1/#220.png b/test/samples/itf-1/#220.png new file mode 100644 index 0000000000000000000000000000000000000000..67e8eda6ea980a363dd19923c011ff3c82767ca8 GIT binary patch literal 314 zcmV-A0mc4_P)B~x7Pt$G#T4JYP9)^?2vz state; DecodeHints hints; Code128Reader reader(hints); - return reader.decodePattern(0, row, state); + PatternView next(row); + return reader.decodePattern(0, next, state); } TEST(ODCode128ReaderTest, ReaderInit) From 79a21c037b199268430315933bc3d3993da2b7d1 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Tue, 20 Jul 2021 20:36:33 +0200 Subject: [PATCH 011/185] Multi-Barcode: initial (experimental) API and backend for 1D symbologies This partly implements the feature as discussed in #138. The detection of multiple (stacked) DataBar(Expanded) symbols is not supported. Next steps: * implement backend support for the individual 2D codes * get feedback on the top-level API choice --- core/src/MultiFormatReader.cpp | 13 ++++- core/src/MultiFormatReader.h | 5 ++ core/src/ReadBarcode.cpp | 27 +++++++-- core/src/ReadBarcode.h | 3 + core/src/Reader.h | 9 ++- core/src/Result.h | 7 +++ core/src/oned/ODReader.cpp | 52 ++++++++++------- core/src/oned/ODReader.h | 1 + example/ZXingReader.cpp | 93 ++++++++++++++++--------------- test/samples/multi-1/3xean-8.png | Bin 0 -> 364 bytes 10 files changed, 138 insertions(+), 72 deletions(-) create mode 100644 test/samples/multi-1/3xean-8.png diff --git a/core/src/MultiFormatReader.cpp b/core/src/MultiFormatReader.cpp index 7418803c30..9b1e05efdc 100644 --- a/core/src/MultiFormatReader.cpp +++ b/core/src/MultiFormatReader.cpp @@ -19,7 +19,6 @@ #include "BarcodeFormat.h" #include "DecodeHints.h" -#include "Result.h" #include "aztec/AZReader.h" #include "datamatrix/DMReader.h" #include "maxicode/MCReader.h" @@ -75,4 +74,16 @@ MultiFormatReader::read(const BinaryBitmap& image) const return Result(DecodeStatus::NotFound); } +Results MultiFormatReader::readMultiple(const BinaryBitmap& image) const +{ + std::vector res; + + for (const auto& reader : _readers) { + auto r = reader->decode(image, 10); + res.insert(res.end(), r.begin(), r.end()); + } + + return res; +} + } // ZXing diff --git a/core/src/MultiFormatReader.h b/core/src/MultiFormatReader.h index 0da0a140d1..4532766666 100644 --- a/core/src/MultiFormatReader.h +++ b/core/src/MultiFormatReader.h @@ -16,6 +16,8 @@ * limitations under the License. */ +#include "Result.h" + #include #include @@ -42,6 +44,9 @@ class MultiFormatReader Result read(const BinaryBitmap& image) const; + // WARNING: this API is experimental and may change/disappear + Results readMultiple(const BinaryBitmap& image) const; + private: std::vector> _readers; }; diff --git a/core/src/ReadBarcode.cpp b/core/src/ReadBarcode.cpp index 3c25862c1c..03907e7e11 100644 --- a/core/src/ReadBarcode.cpp +++ b/core/src/ReadBarcode.cpp @@ -61,11 +61,8 @@ static LumImage ExtractLum(const ImageView& iv, P projection) return res; } -Result ReadBarcode(const ImageView& _iv, const DecodeHints& hints) +ImageView SetupLumImageView(const ImageView& iv, LumImage& lum, const DecodeHints& hints) { - LumImage lum; - ImageView iv = _iv; - if (hints.binarizer() == Binarizer::GlobalHistogram || hints.binarizer() == Binarizer::LocalAverage) { if (iv.format() != ImageFormat::Lum) { lum = ExtractLum(iv, [r = RedIndex(iv.format()), g = GreenIndex(iv.format()), b = BlueIndex(iv.format())]( @@ -75,8 +72,15 @@ Result ReadBarcode(const ImageView& _iv, const DecodeHints& hints) lum = ExtractLum(iv, [](const uint8_t* src) { return *src; }); } if (lum.data()) - iv = lum; + return lum; } + return iv; +} + +Result ReadBarcode(const ImageView& _iv, const DecodeHints& hints) +{ + LumImage lum; + ImageView iv = SetupLumImageView(_iv, lum, hints); switch (hints.binarizer()) { case Binarizer::BoolCast: return MultiFormatReader(hints).read(ThresholdBinarizer(iv, 0)); @@ -86,4 +90,17 @@ Result ReadBarcode(const ImageView& _iv, const DecodeHints& hints) } } +Results ReadBarcodes(const ImageView& _iv, const DecodeHints& hints) +{ + LumImage lum; + ImageView iv = SetupLumImageView(_iv, lum, hints); + + switch (hints.binarizer()) { + case Binarizer::BoolCast: return MultiFormatReader(hints).readMultiple(ThresholdBinarizer(iv, 0)); + case Binarizer::FixedThreshold: return MultiFormatReader(hints).readMultiple(ThresholdBinarizer(iv, 127)); + case Binarizer::GlobalHistogram: return MultiFormatReader(hints).readMultiple(GlobalHistogramBinarizer(iv)); + case Binarizer::LocalAverage: return MultiFormatReader(hints).readMultiple(HybridBinarizer(iv)); + } +} + } // ZXing diff --git a/core/src/ReadBarcode.h b/core/src/ReadBarcode.h index 93e0ec938c..3bbc48f7b4 100644 --- a/core/src/ReadBarcode.h +++ b/core/src/ReadBarcode.h @@ -30,5 +30,8 @@ namespace ZXing { */ Result ReadBarcode(const ImageView& buffer, const DecodeHints& hints = {}); +// WARNING: this API is experimental and may change/disappear +Results ReadBarcodes(const ImageView& buffer, const DecodeHints& hints = {}); + } // ZXing diff --git a/core/src/Reader.h b/core/src/Reader.h index 1ae56ca51a..642ac9c35f 100644 --- a/core/src/Reader.h +++ b/core/src/Reader.h @@ -16,10 +16,11 @@ * limitations under the License. */ +#include "Result.h" + namespace ZXing { class BinaryBitmap; -class Result; /** * Implementations of this interface can decode an image of a barcode in some format into @@ -55,6 +56,12 @@ class Reader * @throws FormatException if a potential barcode is found but format is invalid */ virtual Result decode(const BinaryBitmap& image) const = 0; + + // WARNING: this API is experimental and may change/disappear + virtual Results decode(const BinaryBitmap& image, [[maybe_unused]] int maxSymbols) const { + auto res = decode(image); + return res.isValid() ? Results{std::move(res)} : Results{}; + } }; } // ZXing diff --git a/core/src/Result.h b/core/src/Result.h index 0ae693ea8a..e0d5c0b87d 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -24,6 +24,7 @@ #include "StructuredAppend.h" #include +#include namespace ZXing { @@ -120,6 +121,10 @@ class Result return _readerInit; } + bool operator==(const Result& o) const { + return text() == o.text() && format() == o.format(); + } + private: DecodeStatus _status = DecodeStatus::NoError; BarcodeFormat _format = BarcodeFormat::None; @@ -132,4 +137,6 @@ class Result bool _readerInit = false; }; +using Results = std::vector; + } // ZXing diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index 6626e03cc6..9209db4bd2 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -74,9 +74,11 @@ Reader::~Reader() = default; * decided that moving up and down by about 1/16 of the image is pretty good; we try more of the * image if "trying harder". */ -static Result DoDecode(const std::vector>& readers, const BinaryBitmap& image, - bool tryHarder, bool rotate, bool isPure) +static Results DoDecode(const std::vector>& readers, const BinaryBitmap& image, + bool tryHarder, bool rotate, bool isPure, int maxSymbols) { + Results res; + std::vector> decodingState(readers.size()); int width = image.width(); @@ -126,7 +128,7 @@ static Result DoDecode(const std::vector>& readers, c PatternView next(bars); do { Result result = readers[r]->decodePattern(rowNumber, next, decodingState[r]); - if (result.isValid()) { + if (result.isValid() && !Contains(res, result)) { if (upsideDown) { // update position (flip horizontally). auto points = result.position(); @@ -135,7 +137,16 @@ static Result DoDecode(const std::vector>& readers, c } result.setPosition(std::move(points)); } - return result; + if (rotate) { + auto points = result.position(); + for (auto& p : points) { + p = {height - p.y - 1, p.x}; + } + result.setPosition(std::move(points)); + } + res.push_back(std::move(result)); + if (maxSymbols && Size(res) == maxSymbols) + return res; } // make sure we make progress and we start the next try on a bar next.shift(2 - (next.index() % 2)); @@ -148,28 +159,29 @@ static Result DoDecode(const std::vector>& readers, c if (isPure) break; } - return Result(DecodeStatus::NotFound); + + return res; } Result Reader::decode(const BinaryBitmap& image) const { - Result result = DoDecode(_readers, image, _tryHarder, false, _isPure); - - if (!result.isValid() && _tryRotate) { - result = DoDecode(_readers, image, _tryHarder, true, _isPure); - if (result.isValid()) { - // Update position - auto points = result.position(); - int height = image.width(); - for (auto& p : points) { - p = {height - p.y - 1, p.x}; - } - result.setPosition(std::move(points)); - } - } + auto result = DoDecode(_readers, image, _tryHarder, false, _isPure, 1); - return result; + if (result.empty() && _tryRotate) + result = DoDecode(_readers, image, _tryHarder, true, _isPure, 1); + + return result.empty() ? Result(DecodeStatus::NotFound) : result.front(); +} + +Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const +{ + auto resH = DoDecode(_readers, image, _tryHarder, false, _isPure, maxSymbols); + if (Size(resH) < maxSymbols && _tryRotate) { + auto resV = DoDecode(_readers, image, _tryHarder, true, _isPure, maxSymbols); + resH.insert(resH.end(), resV.begin(), resV.end()); + } + return resH; } } // namespace ZXing::OneD diff --git a/core/src/oned/ODReader.h b/core/src/oned/ODReader.h index 3603acb93f..70f6dbf257 100644 --- a/core/src/oned/ODReader.h +++ b/core/src/oned/ODReader.h @@ -40,6 +40,7 @@ class Reader : public ZXing::Reader ~Reader() override; Result decode(const BinaryBitmap& image) const override; + Results decode(const BinaryBitmap& image, int maxSymbols) const override; private: std::vector> _readers; diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 7e2c5066c7..5df8de3e9e 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -122,54 +122,57 @@ int main(int argc, char* argv[]) return -1; } - const auto& result = ReadBarcode({buffer.get(), width, height, ImageFormat::RGBX}, hints); - - ret |= static_cast(result.status()); - - if (oneLine) { - std::cout << filePath << " " << ToString(result.format()); - if (result.isValid()) - std::cout << " \"" << ToUtf8(result.text(), angleEscape) << "\""; - else if (result.format() != BarcodeFormat::None) - std::cout << " " << ToString(result.status()); - std::cout << "\n"; - continue; - } - - if (filePaths.size() > 1) { - static bool firstFile = true; - if (!firstFile) + const auto& results = ReadBarcodes({buffer.get(), width, height, ImageFormat::RGBX}, hints); + for (auto&& result : results) { + ret |= static_cast(result.status()); + + if (oneLine) { + std::cout << filePath << " " << ToString(result.format()); + if (result.isValid()) + std::cout << " \"" << ToUtf8(result.text(), angleEscape) << "\""; + else if (result.format() != BarcodeFormat::None) + std::cout << " " << ToString(result.status()); std::cout << "\n"; - std::cout << "File: " << filePath << "\n"; - firstFile = false; - } - std::cout << "Text: \"" << ToUtf8(result.text(), angleEscape) << "\"\n" - << "Format: " << ToString(result.format()) << "\n" - << "Position: " << result.position() << "\n" - << "Rotation: " << result.orientation() << " deg\n" - << "Error: " << ToString(result.status()) << "\n"; - - auto printOptional = [](const char* key, const std::string& v) { - if (!v.empty()) - std::cout << key << v << "\n"; - }; - - printOptional("EC Level: ", ToUtf8(result.ecLevel())); - - if ((BarcodeFormat::EAN13 | BarcodeFormat::EAN8 | BarcodeFormat::UPCA | BarcodeFormat::UPCE) - .testFlag(result.format())) { - printOptional("Country: ", GTIN::LookupCountryIdentifier(ToUtf8(result.text()))); - printOptional("Add-On: ", GTIN::EanAddOn(result)); - printOptional("Price: ", GTIN::Price(GTIN::EanAddOn(result))); - printOptional("Issue #: ", GTIN::IssueNr(GTIN::EanAddOn(result))); - } + continue; + } - if (result.isPartOfSequence()) - std::cout << "Structured Append: symbol " << result.sequenceIndex() + 1 << " of " << result.sequenceSize() - << " (parity/id: '" << result.sequenceId() << "')\n"; + if (filePaths.size() > 1 || results.size() > 1) { + static bool firstFile = true; + if (!firstFile) + std::cout << "\n"; + if (filePaths.size() > 1) + std::cout << "File: " << filePath << "\n"; + firstFile = false; + } + std::cout << "Text: \"" << ToUtf8(result.text(), angleEscape) << "\"\n" + << "Format: " << ToString(result.format()) << "\n" + << "Position: " << result.position() << "\n" + << "Rotation: " << result.orientation() << " deg\n" + << "Error: " << ToString(result.status()) << "\n"; + + auto printOptional = [](const char* key, const std::string& v) { + if (!v.empty()) + std::cout << key << v << "\n"; + }; + + printOptional("EC Level: ", ToUtf8(result.ecLevel())); + + if ((BarcodeFormat::EAN13 | BarcodeFormat::EAN8 | BarcodeFormat::UPCA | BarcodeFormat::UPCE) + .testFlag(result.format())) { + printOptional("Country: ", GTIN::LookupCountryIdentifier(ToUtf8(result.text()))); + printOptional("Add-On: ", GTIN::EanAddOn(result)); + printOptional("Price: ", GTIN::Price(GTIN::EanAddOn(result))); + printOptional("Issue #: ", GTIN::IssueNr(GTIN::EanAddOn(result))); + } + + if (result.isPartOfSequence()) + std::cout << "Structured Append: symbol " << result.sequenceIndex() + 1 << " of " + << result.sequenceSize() << " (parity/id: '" << result.sequenceId() << "')\n"; + + if (result.readerInit()) + std::cout << "Reader Initialisation/Programming\n"; + } - if (result.readerInit()) - std::cout << "Reader Initialisation/Programming\n"; } return ret; diff --git a/test/samples/multi-1/3xean-8.png b/test/samples/multi-1/3xean-8.png new file mode 100644 index 0000000000000000000000000000000000000000..77d2fd730f5b51a2c42a06de3b7bce47a1d18fd3 GIT binary patch literal 364 zcmeAS@N?(olHy`uVBq!ia0y~yVAcV$#W;Wj1N$3yaRvrPV^0^ykcwMxFYnc3Nt9@N z_*{1Kgn*odj+%aMixymdrC~ZHIMZ~K`-2D&%3U7bN<~@)H-@+ukEyw=iZ)$4{v|l*=q9tiH)wv{QEIK-2^9H z%laL$@y8^)%;oJ#m+wst@dz@k@(o|;bgA(z?@_<1=&0?*@f-id-F~?}@Wq8~*|&Fo zt^Jc_)i2_G{_F{_4E@;K*B3szbFL)qSE1voH@9Bi%1r-l|L9DehVrT{#Rrl#SswUz xuquc(L@*>VLKy0T literal 0 HcmV?d00001 From a0a3a0cc138cb64bc6837470ac8bbd3c77abcbfb Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Tue, 20 Jul 2021 21:26:57 +0200 Subject: [PATCH 012/185] python: add new Multi-Barcode API (`read_barcodes`) This can be considered a fix for #131 as it provides a solution and works for both presented examples. --- wrappers/python/zxing.cpp | 48 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/wrappers/python/zxing.cpp b/wrappers/python/zxing.cpp index c7843c750b..be52eb3edf 100644 --- a/wrappers/python/zxing.cpp +++ b/wrappers/python/zxing.cpp @@ -34,8 +34,9 @@ std::ostream& operator<<(std::ostream& os, const Position& points) { return os; } -Result read_barcode(py::object _image, const BarcodeFormats& formats, bool try_rotate, Binarizer binarizer, - bool is_pure, EanAddOnSymbol ean_add_on_symbol) +template +auto read_barcode_impl(FUNC func, py::object _image, const BarcodeFormats& formats, bool try_rotate, + Binarizer binarizer, bool is_pure, EanAddOnSymbol ean_add_on_symbol) { const auto hints = DecodeHints() .setFormats(formats) @@ -80,7 +81,19 @@ Result read_barcode(py::object _image, const BarcodeFormats& formats, bool try_r } const auto bytes = image.data(); - return ReadBarcode({bytes, width, height, imgfmt, width * channels, channels}, hints); + return func({bytes, width, height, imgfmt, width * channels, channels}, hints); +} + +Result read_barcode(py::object _image, const BarcodeFormats& formats, bool try_rotate, Binarizer binarizer, + bool is_pure, EanAddOnSymbol ean_add_on_symbol) +{ + return read_barcode_impl(ReadBarcode, _image, formats, try_rotate, binarizer, is_pure, ean_add_on_symbol); +} + +Results read_barcodes(py::object _image, const BarcodeFormats& formats, bool try_rotate, Binarizer binarizer, + bool is_pure, EanAddOnSymbol ean_add_on_symbol) +{ + return read_barcode_impl(ReadBarcodes, _image, formats, try_rotate, binarizer, is_pure, ean_add_on_symbol); } Image write_barcode(BarcodeFormat format, std::string text, int width, int height, int quiet_zone, int ec_level) @@ -226,6 +239,35 @@ PYBIND11_MODULE(zxingcpp, m) ":rtype: zxing.Result\n" ":return: a zxing result containing decoded symbol if found." ); + m.def("read_barcodes", &read_barcodes, + py::arg("image"), + py::arg("formats") = BarcodeFormats{}, + py::arg("try_rotate") = true, + py::arg("binarizer") = Binarizer::LocalAverage, + py::arg("is_pure") = false, + py::arg("ean_add_on_symbol") = EanAddOnSymbol::Ignore, + "Read (decode) multiple barcodes from a numpy BGR or grayscale image array or from a PIL image.\n\n" + ":type image: numpy.ndarray|PIL.Image.Image\n" + ":param image: The image object to decode. The image can be either:\n" + " - a numpy array containing image either in grayscale (1 byte per pixel) or BGR mode (3 bytes per pixel)\n" + " - a PIL Image\n" + ":type formats: zxing.BarcodeFormat|zxing.BarcodeFormats\n" + ":param formats: the format(s) to decode. If ``None``, decode all formats.\n" + ":type try_rotate: bool\n" + ":param try_rotate: if ``True`` (the default), decoder searched for barcodes in any direction; \n" + " if ``False``, it will not search for 90° / 270° rotated barcodes.\n" + ":type binarizer: zxing.Binarizer\n" + ":param binarizer: the binarizer used to convert image before decoding barcodes.\n" + " Defaults to :py:attr:`zxing.Binarizer.LocalAverage`." + ":type is_pure: bool\n" + ":param is_pure: Set to True if the input contains nothing but a perfectly aligned barcode (generated image).\n" + " Speeds up detection in that case. Default is False." + ":type ean_add_on_symbol: zxing.EanAddOnSymbol\n" + ":param ean_add_on_symbol: Specify whether to Ignore, Read or Require EAN-2/5 add-on symbols while scanning \n" + " EAN/UPC codes. Default is ``Ignore``.\n" + ":rtype: zxing.Result\n" + ":return: a list of zxing results containing decoded symbols, the list is empty if none is found" + ); m.def("write_barcode", &write_barcode, py::arg("format"), py::arg("text"), From 0c0c42d5f47ec44601571c086ceb8279e36f4c2a Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Wed, 21 Jul 2021 14:02:45 +0200 Subject: [PATCH 013/185] fuzzer: make fuzzODDecoders test the new 'check the whole line' feature --- test/fuzz/fuzzODDecoders.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/test/fuzz/fuzzODDecoders.cpp b/test/fuzz/fuzzODDecoders.cpp index 4426c1317f..df39599041 100644 --- a/test/fuzz/fuzzODDecoders.cpp +++ b/test/fuzz/fuzzODDecoders.cpp @@ -46,8 +46,15 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) } row.back() = 0; - for (size_t r = 0; r < readers.size(); ++r) - readers[r]->decodePattern(0, row, decodingState[r]); + for (size_t r = 0; r < readers.size(); ++r) { + PatternView next(row); + while (next.isValid()) { + readers[r]->decodePattern(0, next, decodingState[r]); + // make sure we make progress and we start the next try on a bar + next.shift(2 - (next.index() % 2)); + next.extend(); + } + } return 0; } From 9716ed3cc51e1e759c4377b069cf0913a35a9b53 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Wed, 21 Jul 2021 14:03:36 +0200 Subject: [PATCH 014/185] fuzzer: fix out-of-bounds access in ODITFReader.cpp --- core/src/oned/ODITFReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/oned/ODITFReader.cpp b/core/src/oned/ODITFReader.cpp index 124a009a7d..ad9881b0fd 100644 --- a/core/src/oned/ODITFReader.cpp +++ b/core/src/oned/ODITFReader.cpp @@ -80,7 +80,7 @@ Result ITFReader::decodePattern(int rowNumber, PatternView& next, std::unique_pt next = next.subView(0, 3); - if (Size(txt) < minCharCount) + if (Size(txt) < minCharCount || !next.isValid()) return Result(DecodeStatus::NotFound); if (!IsRightGuard(next, STOP_PATTERN_1, minQuietZone) && !IsRightGuard(next, STOP_PATTERN_2, minQuietZone)) From 3065bad7fede2cbcff6a571cc7046d5e3b040027 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Wed, 21 Jul 2021 14:46:38 +0200 Subject: [PATCH 015/185] fuzzer: add DMDecoder fuzzer --- test/fuzz/CMakeLists.txt | 2 ++ test/fuzz/fuzzDMDecoder.cpp | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 test/fuzz/fuzzDMDecoder.cpp diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt index 73ab9f71d0..a416ea791e 100644 --- a/test/fuzz/CMakeLists.txt +++ b/test/fuzz/CMakeLists.txt @@ -9,10 +9,12 @@ set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -march=native -fsanitize=address,fuz set (BUILD_WRITERS ON) set (BUILD_READERS ON) +add_definitions (-DZXING_BUILD_FOR_TEST) add_subdirectory (${CMAKE_CURRENT_SOURCE_DIR}/../../core ${CMAKE_BINARY_DIR}/ZXing) set (TESTS DBEDecoder + DMDecoder DMEncoder ODDecoders ) diff --git a/test/fuzz/fuzzDMDecoder.cpp b/test/fuzz/fuzzDMDecoder.cpp new file mode 100644 index 0000000000..455679c70c --- /dev/null +++ b/test/fuzz/fuzzDMDecoder.cpp @@ -0,0 +1,26 @@ +#include +#include + +#include "ByteArray.h" +#include "DecoderResult.h" + +using namespace ZXing; + +namespace ZXing::DataMatrix::DecodedBitStreamParser { +DecoderResult Decode(ByteArray&& bytes, const std::string& characterSet); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + if (size < 2) + return 0; + + ByteArray ba; + ba.insert(ba.begin(), data, data + size); + try { + DataMatrix::DecodedBitStreamParser::Decode(std::move(ba), ""); + } catch (...) { + } + + return 0; +} From 47a94e5e2eba47a63ae145fc4adaf7a75c89e8a6 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Wed, 21 Jul 2021 14:48:01 +0200 Subject: [PATCH 016/185] fuzzer: fix out-of-bounds access in DMDecoder.cpp:DecodeAnsiX12Segment() --- core/src/datamatrix/DMDecoder.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/datamatrix/DMDecoder.cpp b/core/src/datamatrix/DMDecoder.cpp index d12ba7a6e2..8010e5ab1b 100644 --- a/core/src/datamatrix/DMDecoder.cpp +++ b/core/src/datamatrix/DMDecoder.cpp @@ -301,7 +301,9 @@ static bool DecodeAnsiX12Segment(BitSource& bits, std::string& result) for (int cValue : *triple) { // X12 segment terminator , separator *, sub-element separator >, space static const char segChars[4] = {'\r', '*', '>', ' '}; - if (cValue < 4) + if (cValue < 0) + return false; + else if (cValue < 4) result.push_back(segChars[cValue]); else if (cValue < 14) // 0 - 9 result.push_back((char)(cValue + 44)); From 0bda9542492c223742a4c65afb99c257563ccc7b Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Thu, 22 Jul 2021 13:02:27 +0200 Subject: [PATCH 017/185] Multi-Barcode: sort list of detected symbols based on their position --- core/src/MultiFormatReader.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/src/MultiFormatReader.cpp b/core/src/MultiFormatReader.cpp index 9b1e05efdc..767a64c685 100644 --- a/core/src/MultiFormatReader.cpp +++ b/core/src/MultiFormatReader.cpp @@ -83,6 +83,13 @@ Results MultiFormatReader::readMultiple(const BinaryBitmap& image) const res.insert(res.end(), r.begin(), r.end()); } + // sort results based on their position on the image + std::sort(res.begin(), res.end(), [](const Result& l, const Result& r) { + auto lp = l.position().topLeft(); + auto rp = r.position().topLeft(); + return lp.y < rp.y || (lp.y == rp.y && lp.x <= rp.x); + }); + return res; } From 98f9990896902a45625a326fa58b5dbbe6ef4991 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Thu, 22 Jul 2021 13:03:58 +0200 Subject: [PATCH 018/185] Multi-Barcode: increase scanning density in tryHarder use case --- core/src/oned/ODReader.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index 9209db4bd2..5de623cd0e 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -88,7 +88,8 @@ static Results DoDecode(const std::vector>& readers, std::swap(width, height); int middle = height / 2; - int rowStep = std::max(1, height / (tryHarder ? 256 : 32)); + // TODO: find a better heuristic/parameterization if maxSymbols != 1 + int rowStep = std::max(1, height / (tryHarder ? (maxSymbols == 1 ? 256 : 512) : 32)); int maxLines = tryHarder ? height : // Look at the whole image, not just the center 15; // 15 rows spaced 1/32 apart is roughly the middle half of the image From ba88001dd69556e326d36ffd6269397bedfcf56b Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Thu, 22 Jul 2021 13:10:40 +0200 Subject: [PATCH 019/185] Multi-Barcode: differentiate between multiple 1D-symbols of the same code This implements a simple heuristic based on the relation between the distance between a new line and an existing compared to the length of the symbol. This feature adds the benefit that 1D symbols now can have a "height" other than 1 reported back in their position. This detects now all 6 symbols from the 'second example' in #138. --- core/src/oned/ODReader.cpp | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index 5de623cd0e..a8cb2cd90a 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -129,7 +129,7 @@ static Results DoDecode(const std::vector>& readers, PatternView next(bars); do { Result result = readers[r]->decodePattern(rowNumber, next, decodingState[r]); - if (result.isValid() && !Contains(res, result)) { + if (result.isValid()) { if (upsideDown) { // update position (flip horizontally). auto points = result.position(); @@ -145,9 +145,39 @@ static Results DoDecode(const std::vector>& readers, } result.setPosition(std::move(points)); } - res.push_back(std::move(result)); - if (maxSymbols && Size(res) == maxSymbols) - return res; + + // check if we know this code already + for (auto& other : res) { + if (other == result) { + auto dTop = maxAbsComponent(other.position().topLeft() - result.position().topLeft()); + auto dBot = maxAbsComponent(other.position().bottomLeft() - result.position().topLeft()); + auto length = maxAbsComponent(other.position().topLeft() - other.position().bottomRight()); + // if the new line is less than half the length of the existing result away from the + // latter, we consider it to belong to the same symbol + if (std::min(dTop, dBot) < length / 2) { + // if so, merge the position information + auto points = other.position(); + if (dTop < dBot || + (dTop == dBot && rotate ^ (sumAbsComponent(points[0]) > + sumAbsComponent(result.position()[0])))) { + points[0] = result.position()[0]; + points[1] = result.position()[1]; + } else { + points[2] = result.position()[2]; + points[3] = result.position()[3]; + } + other.setPosition(points); + // clear the result below, so we don't insert it again + result = Result(DecodeStatus::NotFound); + } + } + } + + if (result.isValid()) { + res.push_back(std::move(result)); + if (maxSymbols && Size(res) == maxSymbols) + return res; + } } // make sure we make progress and we start the next try on a bar next.shift(2 - (next.index() % 2)); From 347ae97266746574b2ca9d4d5078f000bdb997b0 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Thu, 22 Jul 2021 21:05:12 +0200 Subject: [PATCH 020/185] Multi-Barcode: drop hard coded limit of maxSymbols --- core/src/MultiFormatReader.cpp | 2 +- core/src/oned/ODReader.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/MultiFormatReader.cpp b/core/src/MultiFormatReader.cpp index 767a64c685..ceacf1e315 100644 --- a/core/src/MultiFormatReader.cpp +++ b/core/src/MultiFormatReader.cpp @@ -79,7 +79,7 @@ Results MultiFormatReader::readMultiple(const BinaryBitmap& image) const std::vector res; for (const auto& reader : _readers) { - auto r = reader->decode(image, 10); + auto r = reader->decode(image, 0); res.insert(res.end(), r.begin(), r.end()); } diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index a8cb2cd90a..b923b618ad 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -208,7 +208,7 @@ Reader::decode(const BinaryBitmap& image) const Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const { auto resH = DoDecode(_readers, image, _tryHarder, false, _isPure, maxSymbols); - if (Size(resH) < maxSymbols && _tryRotate) { + if ((!maxSymbols || Size(resH) < maxSymbols) && _tryRotate) { auto resV = DoDecode(_readers, image, _tryHarder, true, _isPure, maxSymbols); resH.insert(resH.end(), resV.begin(), resV.end()); } From 81a148217c3f220df1f8a520da21bdf966ec0c2f Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Thu, 22 Jul 2021 21:09:27 +0200 Subject: [PATCH 021/185] example: add `-pngout` option to `ZXingReader` This is helpful for debugging when one wants to see where a symbol or which symbols were found. The written png copy of the input image has a red outline for each detected barcode. This is experimental and might later be removed again. --- example/ZXingReader.cpp | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 5df8de3e9e..62f8fa15c3 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -29,19 +29,22 @@ #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" using namespace ZXing; using namespace TextUtfEncoding; static void PrintUsage(const char* exePath) { - std::cout << "Usage: " << exePath << " [-fast] [-norotate] [-format ] [-ispure] [-1] ...\n" + std::cout << "Usage: " << exePath << " [-fast] [-norotate] [-format ] [-pngout ] [-ispure] [-1] ...\n" << " -fast Skip some lines/pixels during detection (faster)\n" << " -norotate Don't try rotated image during detection (faster)\n" << " -format Only detect given format(s) (faster)\n" << " -ispure Assume the image contains only a 'pure'/perfect code (faster)\n" << " -1 Print only file name, text and status on one line per file\n" << " -escape Escape non-graphical characters in angle brackets (ignored for -1 option, which always escapes)\n" + << " -pngout Write a copy of the input image with barcodes outlined by a red line\n" << "\n" << "Supported formats are:\n"; for (auto f : BarcodeFormats::all()) { @@ -50,7 +53,8 @@ static void PrintUsage(const char* exePath) std::cout << "Formats can be lowercase, with or without '-', separated by ',' and/or '|'\n"; } -static bool ParseOptions(int argc, char* argv[], DecodeHints& hints, bool& oneLine, bool& angleEscape, std::vector& filePaths) +static bool ParseOptions(int argc, char* argv[], DecodeHints& hints, bool& oneLine, bool& angleEscape, + std::vector& filePaths, std::string& outPath) { for (int i = 1; i < argc; ++i) { if (strcmp(argv[i], "-fast") == 0) { @@ -79,6 +83,11 @@ static bool ParseOptions(int argc, char* argv[], DecodeHints& hints, bool& oneLi else if (strcmp(argv[i], "-escape") == 0) { angleEscape = true; } + else if (strcmp(argv[i], "-pngout") == 0) { + if (++i == argc) + return false; + outPath = argv[i]; + } else { filePaths.push_back(argv[i]); } @@ -93,15 +102,32 @@ std::ostream& operator<<(std::ostream& os, const Position& points) { return os; } +void drawLine(const ImageView& image, PointI a, PointI b) +{ + int steps = maxAbsComponent(b - a); + PointF dir = bresenhamDirection(PointF(b - a)); + for (int i = 0; i < steps; ++i) { + auto p = centered(a + i * dir); + *((uint32_t*)image.data(p.x, p.y)) = 0xff0000ff; + } +} + +void drawRect(const ImageView& image, const Position& pos) +{ + for(int i=0;i<4;++i) + drawLine(image, pos[i], pos[(i+1)%4]); +} + int main(int argc, char* argv[]) { DecodeHints hints; std::vector filePaths; + std::string outPath; bool oneLine = false; bool angleEscape = false; int ret = 0; - if (!ParseOptions(argc, argv, hints, oneLine, angleEscape, filePaths)) { + if (!ParseOptions(argc, argv, hints, oneLine, angleEscape, filePaths, outPath)) { PrintUsage(argv[0]); return -1; } @@ -124,6 +150,10 @@ int main(int argc, char* argv[]) const auto& results = ReadBarcodes({buffer.get(), width, height, ImageFormat::RGBX}, hints); for (auto&& result : results) { + + if (!outPath.empty()) + drawRect(image, result.position()); + ret |= static_cast(result.status()); if (oneLine) { @@ -173,6 +203,9 @@ int main(int argc, char* argv[]) std::cout << "Reader Initialisation/Programming\n"; } + if (!outPath.empty()) + stbi_write_png(outPath.c_str(), image.width(), image.height(), 4, image.data(0, 0), image.rowStride()); + } return ret; From 46c5082ca0dfe882768b6466157b534262850386 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Thu, 22 Jul 2021 21:12:06 +0200 Subject: [PATCH 022/185] Multi-Barcode: implement feature in QRCode backend This fixes #105. --- core/src/qrcode/QRDetector.cpp | 19 +++++++++---------- core/src/qrcode/QRDetector.h | 15 +++++++++++++++ core/src/qrcode/QRReader.cpp | 32 ++++++++++++++++++++++++++++++++ core/src/qrcode/QRReader.h | 2 ++ 4 files changed, 58 insertions(+), 10 deletions(-) diff --git a/core/src/qrcode/QRDetector.cpp b/core/src/qrcode/QRDetector.cpp index 34581dc323..e8a8a381ff 100644 --- a/core/src/qrcode/QRDetector.cpp +++ b/core/src/qrcode/QRDetector.cpp @@ -87,13 +87,6 @@ static auto FindFinderPatterns(const BitMatrix& image, bool tryHarder) return res; } -struct FinderPatternSet -{ - ConcentricPattern bl, tl, tr; -}; - -using FinderPatternSets = std::vector; - /** * @brief GenerateFinderPatternSets * @param patterns list of ConcentricPattern objects, i.e. found finder pattern squares @@ -153,9 +146,10 @@ static FinderPatternSets GenerateFinderPatternSets(std::vectorfirst > d) { + const auto setSizeLimit = 16; + if (sets.size() < setSizeLimit || sets.crbegin()->first > d) { sets.emplace(d, FinderPatternSet{*a, *b, *c}); - if (sets.size() > 16) + if (sets.size() > setSizeLimit) sets.erase(std::prev(sets.end())); } } @@ -244,7 +238,7 @@ static RegressionLine TraceLine(const BitMatrix& image, PointF p, PointF d, int return line; } -static DetectorResult SampleAtFinderPatternSet(const BitMatrix& image, const FinderPatternSet& fp) +DetectorResult SampleAtFinderPatternSet(const BitMatrix& image, const FinderPatternSet& fp) { auto top = EstimateDimension(image, fp.tl, fp.tr); auto left = EstimateDimension(image, fp.tl, fp.bl); @@ -353,6 +347,11 @@ static DetectorResult DetectPure(const BitMatrix& image) {{left, top}, {right, top}, {right, bottom}, {left, bottom}}}; } +FinderPatternSets FindFinderPatternSets(const BitMatrix& image, bool tryHarder) +{ + return GenerateFinderPatternSets(FindFinderPatterns(image, tryHarder)); +} + DetectorResult Detect(const BitMatrix& image, bool tryHarder, bool isPure) { #ifdef PRINT_DEBUG diff --git a/core/src/qrcode/QRDetector.h b/core/src/qrcode/QRDetector.h index 67c9d91110..b11975fffa 100644 --- a/core/src/qrcode/QRDetector.h +++ b/core/src/qrcode/QRDetector.h @@ -2,6 +2,7 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors +* Copyright 2021 Axel Waggershauser * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +17,10 @@ * limitations under the License. */ +#include "ConcentricFinder.h" + +#include + namespace ZXing { class DetectorResult; @@ -23,6 +28,16 @@ class BitMatrix; namespace QRCode { +struct FinderPatternSet +{ + ConcentricPattern bl, tl, tr; +}; + +using FinderPatternSets = std::vector; + +FinderPatternSets FindFinderPatternSets(const BitMatrix& image, bool tryHarder); +DetectorResult SampleAtFinderPatternSet(const BitMatrix& image, const FinderPatternSet& fp); + /** * @brief Detects a QR Code in an image. */ diff --git a/core/src/qrcode/QRReader.cpp b/core/src/qrcode/QRReader.cpp index 7e54648579..4c2a8ecc2b 100644 --- a/core/src/qrcode/QRReader.cpp +++ b/core/src/qrcode/QRReader.cpp @@ -55,4 +55,36 @@ Reader::decode(const BinaryBitmap& image) const return Result(std::move(decoderResult), std::move(position), BarcodeFormat::QRCode); } +Results Reader::decode(const BinaryBitmap& image, int maxSymbols) const +{ + auto binImg = image.getBitMatrix(); + if (binImg == nullptr) + return {}; + + FinderPatternSets allFPSets = FindFinderPatternSets(*binImg, _tryHarder); + std::vector usedFPs; + Results results; + + for(auto& fpSet : allFPSets) { + if (Contains(usedFPs, fpSet.bl) || Contains(usedFPs, fpSet.tl) || Contains(usedFPs, fpSet.tr)) + continue; + + auto detectorResult = SampleAtFinderPatternSet(*binImg, fpSet); + if (detectorResult.isValid()) { + auto decoderResult = Decode(detectorResult.bits(), _charset); + auto position = detectorResult.position(); + if (decoderResult.isValid()) { + usedFPs.push_back(fpSet.bl); + usedFPs.push_back(fpSet.tl); + usedFPs.push_back(fpSet.tr); + results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::QRCode); + if (maxSymbols && Size(results) == maxSymbols) + break; + } + } + } + + return results; +} + } // namespace ZXing::QRCode diff --git a/core/src/qrcode/QRReader.h b/core/src/qrcode/QRReader.h index 78e5f50911..1df4e8f2b1 100644 --- a/core/src/qrcode/QRReader.h +++ b/core/src/qrcode/QRReader.h @@ -37,6 +37,8 @@ class Reader : public ZXing::Reader explicit Reader(const DecodeHints& hints); Result decode(const BinaryBitmap& image) const override; + Results decode(const BinaryBitmap& image, int maxSymbols) const override; + private: bool _tryHarder, _isPure; std::string _charset; From 0932324845a3aca0b8d66d5411f30a122c2fd7f8 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Thu, 22 Jul 2021 21:31:09 +0200 Subject: [PATCH 023/185] example: fix build regression --- example/ZXingReader.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 62f8fa15c3..4ec2942849 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -148,7 +148,9 @@ int main(int argc, char* argv[]) return -1; } - const auto& results = ReadBarcodes({buffer.get(), width, height, ImageFormat::RGBX}, hints); + ImageView image{buffer.get(), width, height, ImageFormat::RGBX}; + const auto& results = ReadBarcodes(image, hints); + for (auto&& result : results) { if (!outPath.empty()) From 31caf34b0fbe5443e28b09c22cd5b8ad22567eac Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Thu, 22 Jul 2021 22:19:03 +0200 Subject: [PATCH 024/185] Multi-Barcode: hook up existing PDF417 backend to `ReadBardcodes` Also use `ReadBarcodes` in structured append black box tests. --- core/src/pdf417/PDFReader.cpp | 7 +++++++ core/src/pdf417/PDFReader.h | 4 +++- test/blackbox/BlackboxTestRunner.cpp | 12 ++++-------- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/core/src/pdf417/PDFReader.cpp b/core/src/pdf417/PDFReader.cpp index 93ed83d9e3..64c7344c97 100644 --- a/core/src/pdf417/PDFReader.cpp +++ b/core/src/pdf417/PDFReader.cpp @@ -338,6 +338,13 @@ Reader::decode(const BinaryBitmap& image) const return Result(status); } +Results Reader::decode(const BinaryBitmap& image, [[maybe_unused]] int maxSymbols) const +{ + std::list results; + DoDecode(image, true, results, _characterSet); + return Results(results.begin(), results.end()); +} + std::list Reader::decodeMultiple(const BinaryBitmap& image) const { diff --git a/core/src/pdf417/PDFReader.h b/core/src/pdf417/PDFReader.h index 0f8777b309..138d31ef1e 100644 --- a/core/src/pdf417/PDFReader.h +++ b/core/src/pdf417/PDFReader.h @@ -41,7 +41,9 @@ class Reader : public ZXing::Reader explicit Reader(const DecodeHints& hints); Result decode(const BinaryBitmap& image) const override; - std::list decodeMultiple(const BinaryBitmap& image) const; + Results decode(const BinaryBitmap& image, int maxSymbols) const override; + + [[deprecated]] std::list decodeMultiple(const BinaryBitmap& image) const; }; } // Pdf417 diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 168886adfb..3d31d4ee9e 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -191,15 +191,12 @@ static void doRunTests( } } -static auto readPDF417s = [](const BinaryBitmap& image) { return Pdf417::Reader({}).decodeMultiple(image); }; -static auto readQRCodes = [](const BinaryBitmap& image) { return std::list{QRCode::Reader({}).decode(image)}; }; - -template -static Result readMultiple(const std::vector& imgPaths, int rotation, READER read) +static Result readMultiple(const std::vector& imgPaths, std::string_view format) { std::list allResults; for (const auto& imgPath : imgPaths) { - auto results = read(ThresholdBinarizer(ImageLoader::load(imgPath), 127)); + auto results = + ReadBarcodes(ImageLoader::load(imgPath), DecodeHints().setFormats(BarcodeFormatFromString(format.data()))); allResults.insert(allResults.end(), results.begin(), results.end()); } @@ -243,8 +240,7 @@ static void doRunStructuredAppendTest( auto startTime = std::chrono::steady_clock::now(); for (const auto& [testPath, testImgPaths] : imageGroups) { - auto result = format == "QRCode" ? readMultiple(testImgPaths, test.rotation, readQRCodes) - : readMultiple(testImgPaths, test.rotation, readPDF417s); + auto result = readMultiple(testImgPaths, format); if (result.isValid()) { auto error = checkResult(testPath, format, result); if (!error.empty()) From 81cadadc9d235120fc182d4e74a83b49f5de6dba Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Tue, 27 Jul 2021 01:26:14 +0200 Subject: [PATCH 025/185] ImageView: remove dead code --- core/src/ImageView.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/core/src/ImageView.h b/core/src/ImageView.h index dd1deb74ab..ed1c2bd2f2 100644 --- a/core/src/ImageView.h +++ b/core/src/ImageView.h @@ -47,11 +47,6 @@ class ImageView ImageFormat _format; int _width = 0, _height = 0, _pixStride = 0, _rowStride = 0; -// friend class ThresholdBinarizer; -// friend class GlobalHistogramBinarizer; -// friend class HybridBinarizer; -// friend class BinaryBitmap; - public: /** * ImageView contructor From 6fb30c3df62a571fb4c7d2df66313722e2636836 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Tue, 27 Jul 2021 01:29:36 +0200 Subject: [PATCH 026/185] test: fix wrong number of expected working upca-extension-1 tests --- test/blackbox/BlackboxTestRunner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 3d31d4ee9e..0bd7ea448d 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -443,7 +443,7 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set }); runTests("upca-extension-1", "UPC-A", 6, { - { 3, 6, 0 }, + { 4, 6, 0 }, { 4, 6, 180 }, }, DecodeHints().setEanAddOnSymbol(EanAddOnSymbol::Require)); From 18102c1cbc052b1ee879e89377a5d26038e7ebdb Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Tue, 27 Jul 2021 01:31:05 +0200 Subject: [PATCH 027/185] EAN-Addon: implement check for right quiet zone --- core/src/oned/ODMultiUPCEANReader.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/oned/ODMultiUPCEANReader.cpp b/core/src/oned/ODMultiUPCEANReader.cpp index 0d8e8d825f..d7f03a6902 100644 --- a/core/src/oned/ODMultiUPCEANReader.cpp +++ b/core/src/oned/ODMultiUPCEANReader.cpp @@ -55,6 +55,7 @@ static const int FIRST_DIGIT_ENCODINGS[] = { constexpr float QUIET_ZONE_LEFT = 6; constexpr float QUIET_ZONE_RIGHT = 6; +constexpr float QUIET_ZONE_ADDON = 3; // There is a single sample (ean13-1/12.png) that fails to decode with these (new) settings because // it has a right-side quiet zone of only about 4.5 modules, which is clearly out of spec. @@ -251,6 +252,8 @@ static bool AddOn(PartialResult& res, PatternView begin, int digitCount) auto moduleSize = IsPattern(ext, EXT_START_PATTERN); CHECK(moduleSize); + CHECK(ext.isAtLastBar() || *ext.end() > QUIET_ZONE_ADDON * moduleSize - 1); + res.end = ext; ext = ext.subView(EXT_START_PATTERN.size(), CHAR_LEN); int lgPattern = 0; @@ -265,8 +268,6 @@ static bool AddOn(PartialResult& res, PatternView begin, int digitCount) } } - //TODO: check right quiet zone - if (digitCount == 2) { CHECK(std::stoi(res.txt) % 4 == lgPattern); } else { From a818f4be2df5ea113b31de1aaadcb4bf416ac6d9 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Tue, 27 Jul 2021 01:33:15 +0200 Subject: [PATCH 028/185] Code128: reduce quiet zone requirement to accommodate real world data --- core/src/oned/ODCode128Reader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/oned/ODCode128Reader.cpp b/core/src/oned/ODCode128Reader.cpp index 5f8038f4e1..3ffc95ba4f 100644 --- a/core/src/oned/ODCode128Reader.cpp +++ b/core/src/oned/ODCode128Reader.cpp @@ -183,7 +183,7 @@ Code128Reader::Code128Reader(const DecodeHints& hints) : // all 3 start patterns share the same 2-1-1 prefix constexpr auto START_PATTERN_PREFIX = FixedPattern<3, 4>{2, 1, 1}; constexpr int CHAR_LEN = 6; -constexpr float QUIET_ZONE = 8; // quiet zone spec is 10 modules +constexpr float QUIET_ZONE = 5; // quiet zone spec is 10 modules, real world examples ignore that, see #138 //#define USE_FAST_1_TO_4_BIT_PATTERN_DECODING #ifdef USE_FAST_1_TO_4_BIT_PATTERN_DECODING From 73564446e9b56181781a5b465c3b7167fb8d3e4e Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Tue, 27 Jul 2021 01:34:26 +0200 Subject: [PATCH 029/185] example: ignore -pngout parameter when processing multiple files --- example/ZXingReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 4ec2942849..d1969784a4 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -205,7 +205,7 @@ int main(int argc, char* argv[]) std::cout << "Reader Initialisation/Programming\n"; } - if (!outPath.empty()) + if (Size(filePaths) == 1 && !outPath.empty()) stbi_write_png(outPath.c_str(), image.width(), image.height(), 4, image.data(0, 0), image.rowStride()); } From 22f23533d1d457b06ed40691d001075d7fa12423 Mon Sep 17 00:00:00 2001 From: Huy Cuong Nguyen Date: Sat, 31 Jul 2021 12:21:37 -0400 Subject: [PATCH 030/185] Add missing argument for ApiKey when publishing Nuget package --- .github/workflows/build-winrt.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-winrt.yml b/.github/workflows/build-winrt.yml index 31183f902b..5be49b7f2c 100644 --- a/.github/workflows/build-winrt.yml +++ b/.github/workflows/build-winrt.yml @@ -76,7 +76,7 @@ jobs: - name: Publish NuGet package shell: cmd - run: nuget push huycn.zxingcpp.winrt.nupkg ${{ secrets.NUGET_API_KEY }} -Source https://api.nuget.org/v3/index.json + run: nuget push huycn.zxingcpp.winrt.nupkg -ApiKey ${{ secrets.NUGET_API_KEY }} -Source https://api.nuget.org/v3/index.json - uses: actions/upload-artifact@v2 with: From d7ce34b658a4683509be2bb5be8602b8e3d631b5 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Sun, 8 Aug 2021 14:00:23 +0200 Subject: [PATCH 031/185] android: update IDE + gradle + dependencies --- wrappers/android/.idea/compiler.xml | 2 +- wrappers/android/.idea/gradle.xml | 3 +-- wrappers/android/.idea/misc.xml | 2 +- wrappers/android/app/build.gradle | 12 ++++++------ wrappers/android/build.gradle | 4 ++-- .../android/gradle/wrapper/gradle-wrapper.properties | 2 +- wrappers/android/zxingcpp/build.gradle | 6 ++---- 7 files changed, 14 insertions(+), 17 deletions(-) diff --git a/wrappers/android/.idea/compiler.xml b/wrappers/android/.idea/compiler.xml index 245a82c827..7d7ec2eaff 100644 --- a/wrappers/android/.idea/compiler.xml +++ b/wrappers/android/.idea/compiler.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/wrappers/android/.idea/gradle.xml b/wrappers/android/.idea/gradle.xml index 1323a8a56b..3498dab2d9 100644 --- a/wrappers/android/.idea/gradle.xml +++ b/wrappers/android/.idea/gradle.xml @@ -4,7 +4,7 @@ diff --git a/wrappers/android/.idea/misc.xml b/wrappers/android/.idea/misc.xml index 2925c9621f..59135fb6a1 100644 --- a/wrappers/android/.idea/misc.xml +++ b/wrappers/android/.idea/misc.xml @@ -1,6 +1,6 @@ - + diff --git a/wrappers/android/app/build.gradle b/wrappers/android/app/build.gradle index ad12bda949..6b6b5e32ea 100644 --- a/wrappers/android/app/build.gradle +++ b/wrappers/android/app/build.gradle @@ -38,17 +38,17 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'androidx.core:core-ktx:1.3.2' - implementation 'androidx.appcompat:appcompat:1.2.0' - implementation 'com.google.android.material:material:1.3.0' - implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + implementation 'androidx.core:core-ktx:1.6.0' + implementation 'androidx.appcompat:appcompat:1.3.1' + implementation 'com.google.android.material:material:1.4.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.0' // CameraX - def camerax_version = "1.0.0-rc04" + def camerax_version = "1.0.1" implementation "androidx.camera:camera-core:${camerax_version}" implementation "androidx.camera:camera-camera2:${camerax_version}" implementation "androidx.camera:camera-lifecycle:${camerax_version}" - implementation 'androidx.camera:camera-view:1.0.0-alpha23' + implementation 'androidx.camera:camera-view:1.0.0-alpha27' // Java 'upstream' version of zxing (to compare performance) implementation 'com.google.zxing:core:3.4.1' diff --git a/wrappers/android/build.gradle b/wrappers/android/build.gradle index ddd04db827..7340b5d07c 100644 --- a/wrappers/android/build.gradle +++ b/wrappers/android/build.gradle @@ -1,12 +1,12 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = "1.4.32" + ext.kotlin_version = "1.5.21" repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.3' + classpath 'com.android.tools.build:gradle:7.0.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong diff --git a/wrappers/android/gradle/wrapper/gradle-wrapper.properties b/wrappers/android/gradle/wrapper/gradle-wrapper.properties index 44de1b9d20..5ad631ef9a 100644 --- a/wrappers/android/gradle/wrapper/gradle-wrapper.properties +++ b/wrappers/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip diff --git a/wrappers/android/zxingcpp/build.gradle b/wrappers/android/zxingcpp/build.gradle index 09d957a5db..3a743bf141 100644 --- a/wrappers/android/zxingcpp/build.gradle +++ b/wrappers/android/zxingcpp/build.gradle @@ -11,8 +11,6 @@ android { defaultConfig { minSdkVersion 26 targetSdkVersion 30 - versionCode 1 - versionName "1.0" ndk { // speed up build: compile only arm versions @@ -51,9 +49,9 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'androidx.core:core-ktx:1.3.2' + implementation 'androidx.core:core-ktx:1.6.0' - def camerax_version = "1.0.0-rc04" + def camerax_version = "1.0.1" implementation "androidx.camera:camera-core:${camerax_version}" } From 04772e59ad78341dd45ec0cb79875365a718ee64 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Sun, 8 Aug 2021 14:01:05 +0200 Subject: [PATCH 032/185] android: build native lib with debug info --- wrappers/android/zxingcpp/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrappers/android/zxingcpp/build.gradle b/wrappers/android/zxingcpp/build.gradle index 3a743bf141..08b624a02b 100644 --- a/wrappers/android/zxingcpp/build.gradle +++ b/wrappers/android/zxingcpp/build.gradle @@ -18,7 +18,7 @@ android { } externalNativeBuild { cmake { - arguments "-DCMAKE_BUILD_TYPE=Release" + arguments "-DCMAKE_BUILD_TYPE=RelWithDebInfo" } } } From 855ed4c9e88b5558be985cad4256d271a5239004 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Sun, 8 Aug 2021 14:14:37 +0200 Subject: [PATCH 033/185] test: update libfmt dependency to 8.0.1 --- test/blackbox/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/blackbox/CMakeLists.txt b/test/blackbox/CMakeLists.txt index cf24f0c96c..3da26dd048 100644 --- a/test/blackbox/CMakeLists.txt +++ b/test/blackbox/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14) include(FetchContent) FetchContent_Declare (fmtlib GIT_REPOSITORY https://github.com/fmtlib/fmt.git - GIT_TAG 7.1.2) + GIT_TAG 8.0.1) FetchContent_MakeAvailable (fmtlib) # Adds fmt::fmt if (BUILD_READERS) From 317e2238ed512539b6e009e44f6f95f1de434db0 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Sun, 8 Aug 2021 14:15:20 +0200 Subject: [PATCH 034/185] python: update pybind dependency to 2.7.1 --- wrappers/python/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrappers/python/CMakeLists.txt b/wrappers/python/CMakeLists.txt index 7df364be98..0dd2190e4c 100644 --- a/wrappers/python/CMakeLists.txt +++ b/wrappers/python/CMakeLists.txt @@ -6,7 +6,7 @@ include(FetchContent) #set(FETCHCONTENT_QUIET Off) FetchContent_Declare (pybind11 GIT_REPOSITORY https://github.com/pybind/pybind11.git - GIT_TAG v2.6.2) + GIT_TAG v2.7.1) FetchContent_MakeAvailable (pybind11) # check if we are called from the top-level ZXing project From 63523d2306563c0e4bab69ea845995ac8e5728d6 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Sun, 8 Aug 2021 14:17:26 +0200 Subject: [PATCH 035/185] ODReader: fix comment about size of EAN-13 symbol --- core/src/oned/ODReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index b923b618ad..104cfcb6c5 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -95,7 +95,7 @@ static Results DoDecode(const std::vector>& readers, 15; // 15 rows spaced 1/32 apart is roughly the middle half of the image PatternRow bars; - bars.reserve(128); // e.g. EAN-13 has 96 bars + bars.reserve(128); // e.g. EAN-13 has 59 bars/spaces for (int i = 0; i < maxLines; i++) { From 3d14886838d5b12946ea73d7802c57622b167422 Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Mon, 9 Aug 2021 17:42:32 +0200 Subject: [PATCH 036/185] android: avoid buffer copy from ImageProxy and fix #238 This basically implements the direct buffer access, suggested in #239 by @kim-ninh (Thanks for the input and the help!) while at the same time fixing 238. --- .../zxingcpp/src/main/cpp/BarcodeReader.cpp | 121 ++++++++++-------- .../com/example/zxingcpp/BarcodeReader.kt | 48 +++++-- 2 files changed, 108 insertions(+), 61 deletions(-) diff --git a/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp b/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp index 4b85af741d..31083202e1 100644 --- a/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp +++ b/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp @@ -58,53 +58,10 @@ static jstring ThrowJavaException(JNIEnv* env, const char* message) return nullptr; } -struct LockedPixels +jstring Read(JNIEnv *env, ImageView image, jstring formats, jboolean tryHarder, jboolean tryRotate, + jobject result) { - JNIEnv* env; - jobject bitmap; - void *pixels = nullptr; - - LockedPixels(JNIEnv* env, jobject bitmap) : env(env), bitmap(bitmap) { - if (AndroidBitmap_lockPixels(env, bitmap, &pixels) != ANDROID_BITMAP_RESUT_SUCCESS) - pixels = nullptr; - } - - operator const uint8_t*() const { return static_cast(pixels); } - - ~LockedPixels() { - if (pixels) - AndroidBitmap_unlockPixels(env, bitmap); - } -}; - -extern "C" JNIEXPORT jstring JNICALL -Java_com_example_zxingcpp_BarcodeReader_read( - JNIEnv* env, jobject thiz, jobject bitmap, - jint left, jint top, jint width, jint height, jint rotation, - jstring formats, jboolean tryHarder, jboolean tryRotate, - jobject result) -{ - using namespace ZXing; - try { - AndroidBitmapInfo bmInfo; - AndroidBitmap_getInfo(env, bitmap, &bmInfo); - - ImageFormat fmt = ImageFormat::None; - switch (bmInfo.format) { - case ANDROID_BITMAP_FORMAT_A_8: fmt = ImageFormat::Lum; break; - case ANDROID_BITMAP_FORMAT_RGBA_8888: fmt = ImageFormat::RGBX; break; - default: return ThrowJavaException(env, "Unsupported format"); - } - - auto pixels = LockedPixels(env, bitmap); - - if (!pixels) - return ThrowJavaException(env, "Failed to lock/read AndroidBitmap data"); - - auto image = ImageView{pixels, (int)bmInfo.width, (int)bmInfo.height, fmt, (int)bmInfo.stride} - .cropped(left, top, width, height) - .rotated(rotation); auto hints = DecodeHints() .setFormats(BarcodeFormatsFromString(J2CString(env, formats))) .setTryHarder(tryHarder) @@ -117,13 +74,13 @@ Java_com_example_zxingcpp_BarcodeReader_read( auto duration = std::chrono::high_resolution_clock::now() - startTime; // LOGD("time: %4d ms\n", (int)std::chrono::duration_cast(duration).count()); - if (res.isValid()) { - jclass clResult = env->GetObjectClass(result); + jclass clResult = env->GetObjectClass(result); - jfieldID fidTime = env->GetFieldID(clResult, "time", "Ljava/lang/String;"); - auto time = std::to_wstring(std::chrono::duration_cast(duration).count()); - env->SetObjectField(result, fidTime, C2JString(env, time)); + jfieldID fidTime = env->GetFieldID(clResult, "time", "Ljava/lang/String;"); + auto time = std::to_wstring(std::chrono::duration_cast(duration).count()); + env->SetObjectField(result, fidTime, C2JString(env, time)); + if (res.isValid()) { jfieldID fidText = env->GetFieldID(clResult, "text", "Ljava/lang/String;"); env->SetObjectField(result, fidText, C2JString(env, res.text())); @@ -136,3 +93,67 @@ Java_com_example_zxingcpp_BarcodeReader_read( return ThrowJavaException(env, "Unknown exception"); } } + +extern "C" JNIEXPORT jstring JNICALL +Java_com_example_zxingcpp_BarcodeReader_readYBuffer( + JNIEnv *env, jobject thiz, jobject yBuffer, jint rowStride, + jint left, jint top, jint width, jint height, jint rotation, + jstring formats, jboolean tryHarder, jboolean tryRotate, + jobject result) +{ + const uint8_t* pixels = static_cast(env->GetDirectBufferAddress(yBuffer)); + + auto image = + ImageView{pixels + top * rowStride + left, width, height, ImageFormat::Lum, rowStride} + .rotated(rotation); + + return Read(env, image, formats, tryHarder, tryRotate, result); +} + +struct LockedPixels +{ + JNIEnv* env; + jobject bitmap; + void *pixels = nullptr; + + LockedPixels(JNIEnv* env, jobject bitmap) : env(env), bitmap(bitmap) { + if (AndroidBitmap_lockPixels(env, bitmap, &pixels) != ANDROID_BITMAP_RESUT_SUCCESS) + pixels = nullptr; + } + + operator const uint8_t*() const { return static_cast(pixels); } + + ~LockedPixels() { + if (pixels) + AndroidBitmap_unlockPixels(env, bitmap); + } +}; + +extern "C" JNIEXPORT jstring JNICALL +Java_com_example_zxingcpp_BarcodeReader_readBitmap( + JNIEnv* env, jobject thiz, jobject bitmap, + jint left, jint top, jint width, jint height, jint rotation, + jstring formats, jboolean tryHarder, jboolean tryRotate, + jobject result) +{ + AndroidBitmapInfo bmInfo; + AndroidBitmap_getInfo(env, bitmap, &bmInfo); + + ImageFormat fmt = ImageFormat::None; + switch (bmInfo.format) { + case ANDROID_BITMAP_FORMAT_A_8: fmt = ImageFormat::Lum; break; + case ANDROID_BITMAP_FORMAT_RGBA_8888: fmt = ImageFormat::RGBX; break; + default: return ThrowJavaException(env, "Unsupported format"); + } + + auto pixels = LockedPixels(env, bitmap); + + if (!pixels) + return ThrowJavaException(env, "Failed to lock/Read AndroidBitmap data"); + + auto image = ImageView{pixels, (int)bmInfo.width, (int)bmInfo.height, fmt, (int)bmInfo.stride} + .cropped(left, top, width, height) + .rotated(rotation); + + return Read(env, image, formats, tryHarder, tryRotate, result); +} diff --git a/wrappers/android/zxingcpp/src/main/java/com/example/zxingcpp/BarcodeReader.kt b/wrappers/android/zxingcpp/src/main/java/com/example/zxingcpp/BarcodeReader.kt index 9ded3dcc3a..6f98544a68 100644 --- a/wrappers/android/zxingcpp/src/main/java/com/example/zxingcpp/BarcodeReader.kt +++ b/wrappers/android/zxingcpp/src/main/java/com/example/zxingcpp/BarcodeReader.kt @@ -21,6 +21,7 @@ import android.graphics.ImageFormat import android.graphics.Rect import androidx.camera.core.ImageProxy import java.lang.RuntimeException +import java.nio.ByteBuffer class BarcodeReader { @@ -43,18 +44,35 @@ class BarcodeReader { val time: String? = null, // for development/debug purposes only ) - private lateinit var bitmapBuffer: Bitmap var options : Options = Options() fun read(image: ImageProxy): Result? { - if (!::bitmapBuffer.isInitialized || bitmapBuffer.width != image.width || bitmapBuffer.height != image.height) { - if (image.format != ImageFormat.YUV_420_888) { - error("invalid image format") - } - bitmapBuffer = Bitmap.createBitmap(image.width, image.height, Bitmap.Config.ALPHA_8) + val supportedYUVFormats = arrayOf(ImageFormat.YUV_420_888, ImageFormat.YUV_422_888, ImageFormat.YUV_444_888) + if (image.format !in supportedYUVFormats) { + error("invalid image format") + } + + var result = Result() + val status = image.use { + readYBuffer( + it.planes[0].buffer, + it.planes[0].rowStride, + it.cropRect.left, + it.cropRect.top, + it.cropRect.width(), + it.cropRect.height(), + it.imageInfo.rotationDegrees, + options.formats.joinToString(), + options.tryHarder, + options.tryRotate, + result + ) + } + return try { + result.copy(format = Format.valueOf(status!!)) + } catch (e: Throwable) { + if (status == "NotFound") null else throw RuntimeException(status!!) } - image.use { bitmapBuffer.copyPixelsFromBuffer(image.planes[0].buffer) } - return read(bitmapBuffer, image.cropRect, image.imageInfo.rotationDegrees) } fun read(bitmap: Bitmap, cropRect: Rect = Rect(), rotation: Int = 0): Result? { @@ -64,8 +82,10 @@ class BarcodeReader { fun read(bitmap: Bitmap, options: Options, cropRect: Rect = Rect(), rotation: Int = 0): Result? { var result = Result() val status = with(options) { - read(bitmap, cropRect.left, cropRect.top, cropRect.width(), cropRect.height(), rotation, - formats.joinToString(), tryHarder, tryRotate, result) + readBitmap( + bitmap, cropRect.left, cropRect.top, cropRect.width(), cropRect.height(), rotation, + formats.joinToString(), tryHarder, tryRotate, result + ) } return try { result.copy(format = Format.valueOf(status!!)) @@ -75,7 +95,13 @@ class BarcodeReader { } // setting the format enum from inside the JNI code is a hassle -> use returned String instead - private external fun read( + private external fun readYBuffer( + yBuffer: ByteBuffer, rowStride: Int, left: Int, top: Int, width: Int, height: Int, rotation: Int, + formats: String, tryHarder: Boolean, tryRotate: Boolean, + result: Result, + ): String? + + private external fun readBitmap( bitmap: Bitmap, left: Int, top: Int, width: Int, height: Int, rotation: Int, formats: String, tryHarder: Boolean, tryRotate: Boolean, result: Result, From bbdd03756641bc537204c065f46bd9c36b4ba58b Mon Sep 17 00:00:00 2001 From: Axel Waggershauser Date: Mon, 9 Aug 2021 17:43:11 +0200 Subject: [PATCH 037/185] android: update some style settings for the c++ editor --- wrappers/android/.idea/codeStyles/Project.xml | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/wrappers/android/.idea/codeStyles/Project.xml b/wrappers/android/.idea/codeStyles/Project.xml index b5f2664b65..c4a490d90b 100644 --- a/wrappers/android/.idea/codeStyles/Project.xml +++ b/wrappers/android/.idea/codeStyles/Project.xml @@ -1,27 +1,18 @@ - - + + \ No newline at end of file diff --git a/wrappers/android/app/build.gradle b/wrappers/android/app/build.gradle index 43b31d6bcd..08b2c89fdf 100644 --- a/wrappers/android/app/build.gradle +++ b/wrappers/android/app/build.gradle @@ -4,14 +4,14 @@ plugins { } android { - compileSdkVersion 30 - buildToolsVersion "30.0.3" + compileSdkVersion 31 + buildToolsVersion "32.0.0" // ndkVersion "21.1.6352462" defaultConfig { applicationId 'com.example.zxingdemo' minSdkVersion 26 - targetSdkVersion 30 + targetSdkVersion 31 versionCode 1 versionName "1.0" } @@ -32,25 +32,25 @@ android { kotlinOptions { jvmTarget = '1.8' } - lintOptions { - disable "UnsafeExperimentalUsageError" + lint { + disable 'UnsafeExperimentalUsageError' } } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'androidx.core:core-ktx:1.6.0' - implementation 'androidx.appcompat:appcompat:1.3.1' - implementation 'com.google.android.material:material:1.4.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.0' + implementation 'androidx.core:core-ktx:1.7.0' + implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'com.google.android.material:material:1.5.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.3' // CameraX - def camerax_version = "1.0.1" + def camerax_version = "1.0.2" implementation "androidx.camera:camera-core:${camerax_version}" implementation "androidx.camera:camera-camera2:${camerax_version}" implementation "androidx.camera:camera-lifecycle:${camerax_version}" - implementation 'androidx.camera:camera-view:1.0.0-alpha27' + implementation 'androidx.camera:camera-view:1.0.0-alpha32' // Java 'upstream' version of zxing (to compare performance) implementation 'com.google.zxing:core:3.4.1' diff --git a/wrappers/android/app/src/main/AndroidManifest.xml b/wrappers/android/app/src/main/AndroidManifest.xml index 986de7e03d..540de48834 100644 --- a/wrappers/android/app/src/main/AndroidManifest.xml +++ b/wrappers/android/app/src/main/AndroidManifest.xml @@ -15,7 +15,9 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> - + diff --git a/wrappers/android/build.gradle b/wrappers/android/build.gradle index 7340b5d07c..2eec1a2072 100644 --- a/wrappers/android/build.gradle +++ b/wrappers/android/build.gradle @@ -1,12 +1,12 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = "1.5.21" + ext.kotlin_version = "1.6.20" repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.0.0' + classpath 'com.android.tools.build:gradle:7.1.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong @@ -17,7 +17,7 @@ buildscript { allprojects { repositories { google() - jcenter() + mavenCentral() } } diff --git a/wrappers/android/gradle.properties b/wrappers/android/gradle.properties index a38cbbbb4c..5b0930c074 100644 --- a/wrappers/android/gradle.properties +++ b/wrappers/android/gradle.properties @@ -18,4 +18,5 @@ android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=true # Kotlin code style for this project: "official" or "obsolete": -kotlin.code.style=official \ No newline at end of file +kotlin.code.style=official +# org.gradle.warning.mode=all \ No newline at end of file diff --git a/wrappers/android/gradle/wrapper/gradle-wrapper.properties b/wrappers/android/gradle/wrapper/gradle-wrapper.properties index 5ad631ef9a..e506aad560 100644 --- a/wrappers/android/gradle/wrapper/gradle-wrapper.properties +++ b/wrappers/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip diff --git a/wrappers/android/zxingcpp/build.gradle b/wrappers/android/zxingcpp/build.gradle index 277b20f6c7..fdb5a83403 100644 --- a/wrappers/android/zxingcpp/build.gradle +++ b/wrappers/android/zxingcpp/build.gradle @@ -4,13 +4,13 @@ plugins { } android { - compileSdkVersion 30 - buildToolsVersion "30.0.3" + compileSdkVersion 31 + buildToolsVersion "32.0.0" // ndkVersion '21.1.6352462' defaultConfig { minSdkVersion 21 - targetSdkVersion 30 + targetSdkVersion 31 ndk { // speed up build: compile only arm versions @@ -35,17 +35,17 @@ android { path file('src/main/cpp/CMakeLists.txt') } } - lintOptions { - disable "UnsafeExperimentalUsageError" + lint { + disable 'UnsafeExperimentalUsageError' } } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'androidx.core:core-ktx:1.6.0' + implementation 'androidx.core:core-ktx:1.7.0' - def camerax_version = "1.0.1" + def camerax_version = "1.0.2" implementation "androidx.camera:camera-core:${camerax_version}" } From 6983b89d893a0eef6d7e430e5b029f83061b7645 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 11 Apr 2022 22:29:28 +0200 Subject: [PATCH 123/185] OneD: fix a bug in the isPure code path for the stacked DataBar codes Stacked DataBar (Extended) symbology is actually a 2D not 1D input, hence, my former isPure shortcut had to fail for those. (thanks to @gitlost for implicitly bringing that to my attention ;)). So for those DataBar symbols, we actually need to process more than one non-empty line. This code now requires that the middle line of the image passes through the pure 1D barcode, which is a reasonable assumption imho. --- core/src/oned/ODReader.cpp | 11 ++++++----- test/blackbox/BlackboxTestRunner.cpp | 5 +++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index 4268e19de1..af900ca079 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -97,7 +97,7 @@ static Results DoDecode(const std::vector>& readers, int middle = height / 2; // TODO: find a better heuristic/parameterization if maxSymbols != 1 - int rowStep = std::max(1, height / (tryHarder ? (maxSymbols == 1 ? 256 : 512) : 32)); + int rowStep = std::max(1, height / ((tryHarder && !isPure) ? (maxSymbols == 1 ? 256 : 512) : 32)); int maxLines = tryHarder ? height : // Look at the whole image, not just the center 15; // 15 rows spaced 1/32 apart is roughly the middle half of the image @@ -134,6 +134,11 @@ static Results DoDecode(const std::vector>& readers, } // Look for a barcode for (size_t r = 0; r < readers.size(); ++r) { + // If this is a pure symbol, then checking a single non-empty line is sufficient for all but the stacked + // DataBar codes. They are the only ones using the decodingState, which we can use a flag here. + if (isPure && i && !decodingState[r]) + continue; + PatternView next(bars); do { Result result = readers[r]->decodePattern(rowNumber, next, decodingState[r]); @@ -197,10 +202,6 @@ static Results DoDecode(const std::vector>& readers, } while (tryHarder && next.isValid()); } } - - // If this is a pure symbol, then checking a single non-empty line is sufficient - if (isPure) - break; } out: diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index cf65d7f35a..703c88dc94 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -438,6 +438,7 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set runTests("ean8-1", "EAN-8", 8, { { 8, 8, 0 }, { 8, 8, 180 }, + { 7, 0, pure }, }); runTests("ean13-1", "EAN-13", 31, { @@ -516,6 +517,7 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set runTests("upce-1", "UPC-E", 3, { { 3, 3, 0 }, { 3, 3, 180 }, + { 3, 0, pure }, }); runTests("upce-2", "UPC-E", 28, { @@ -541,6 +543,7 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set runTests("rssexpanded-1", "DataBarExpanded", 33, { { 33, 33, 0 }, { 33, 33, 180 }, + { 33, 0, pure }, }); runTests("rssexpanded-2", "DataBarExpanded", 15, { @@ -551,11 +554,13 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set runTests("rssexpanded-3", "DataBarExpanded", 118, { { 118, 118, 0 }, { 118, 118, 180 }, + { 118, 0, pure }, }); runTests("rssexpandedstacked-1", "DataBarExpanded", 65, { { 60, 65, 0 }, { 60, 65, 180 }, + { 60, 0, pure }, }); runTests("rssexpandedstacked-2", "DataBarExpanded", 7, { From 40b61a7eead3ef2344570d88a8bd676d31a90611 Mon Sep 17 00:00:00 2001 From: Markus Fisch Date: Tue, 12 Apr 2022 13:15:52 +0200 Subject: [PATCH 124/185] android: draw result points/position on screen To highlight a detected barcode. --- .../com/example/zxingcppdemo/DetectorView.kt | 48 ++++++++++++++++ .../com/example/zxingcppdemo/MainActivity.kt | 56 ++++++++++++++++--- .../main/res/layout-land/activity_camera.xml | 7 ++- .../src/main/res/layout/activity_camera.xml | 7 ++- 4 files changed, 109 insertions(+), 9 deletions(-) create mode 100644 wrappers/android/app/src/main/java/com/example/zxingcppdemo/DetectorView.kt diff --git a/wrappers/android/app/src/main/java/com/example/zxingcppdemo/DetectorView.kt b/wrappers/android/app/src/main/java/com/example/zxingcppdemo/DetectorView.kt new file mode 100644 index 0000000000..72710cd6ad --- /dev/null +++ b/wrappers/android/app/src/main/java/com/example/zxingcppdemo/DetectorView.kt @@ -0,0 +1,48 @@ +package com.example.zxingcppdemo + +import android.content.Context +import android.graphics.* +import android.util.AttributeSet +import android.view.View + +class DetectorView : View { + private val paint = Paint(Paint.ANTI_ALIAS_FLAG) + private val path = Path() + + init { + paint.style = Paint.Style.STROKE + paint.color = 0xffffffff.toInt() + paint.strokeWidth = context.resources.displayMetrics.density + } + + constructor(context: Context, attrs: AttributeSet) : + super(context, attrs) + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : + super(context, attrs, defStyleAttr) + + fun update(points: List? = null) { + path.apply { + rewind() + if (!points.isNullOrEmpty()) { + moveTo(points[0]) + val size = points.size + for (i in 1 until size) { + lineTo(points[i]) + } + close() + } + } + invalidate() + } + + override fun onDraw(canvas: Canvas) { + canvas.drawColor(0, PorterDuff.Mode.CLEAR) + if (!path.isEmpty) { + canvas.drawPath(path, paint) + } + } +} + +private fun Path.lineTo(point: PointF) = lineTo(point.x, point.y) +private fun Path.moveTo(point: PointF) = moveTo(point.x, point.y) diff --git a/wrappers/android/app/src/main/java/com/example/zxingcppdemo/MainActivity.kt b/wrappers/android/app/src/main/java/com/example/zxingcppdemo/MainActivity.kt index dcedd338cf..8497e7d6cd 100644 --- a/wrappers/android/app/src/main/java/com/example/zxingcppdemo/MainActivity.kt +++ b/wrappers/android/app/src/main/java/com/example/zxingcppdemo/MainActivity.kt @@ -19,7 +19,9 @@ package com.example.zxingcppdemo import android.Manifest import android.content.Context import android.content.pm.PackageManager -import android.graphics.* +import android.graphics.Point +import android.graphics.PointF +import android.graphics.Rect import android.hardware.camera2.CaptureRequest import android.media.AudioManager import android.media.ToneGenerator @@ -29,20 +31,25 @@ import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import androidx.camera.camera2.interop.Camera2CameraControl import androidx.camera.camera2.interop.CaptureRequestOptions -import androidx.camera.core.* +import androidx.camera.core.AspectRatio +import androidx.camera.core.CameraSelector +import androidx.camera.core.ImageAnalysis +import androidx.camera.core.Preview import androidx.camera.lifecycle.ProcessCameraProvider import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.core.graphics.toPoint +import androidx.core.graphics.toPointF import androidx.lifecycle.LifecycleOwner -import com.zxingcpp.BarcodeReader -import com.zxingcpp.BarcodeReader.Format +import com.example.zxingcppdemo.databinding.ActivityCameraBinding import com.google.zxing.* import com.google.zxing.common.HybridBinarizer +import com.zxingcpp.BarcodeReader +import com.zxingcpp.BarcodeReader.Format import java.util.concurrent.Executors import kotlin.math.abs +import kotlin.math.roundToInt import kotlin.random.Random -import com.example.zxingcppdemo.databinding.ActivityCameraBinding class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityCameraBinding @@ -122,6 +129,7 @@ class MainActivity : AppCompatActivity() { val startTime = System.currentTimeMillis() var resultText: String + var resultPoints: List? = null if (binding.java.isChecked) { val yBuffer = image.planes[0].buffer // Y @@ -145,6 +153,7 @@ class MainActivity : AppCompatActivity() { ) ) val result = readerJava.decode(bitmap, hints) + resultPoints = mapPoints(result?.resultPoints) result?.let { "${it.barcodeFormat}: ${it.text}" } ?: "" } catch (e: Throwable) { if (e.toString() != "com.google.zxing.NotFoundException") e.toString() else "" @@ -161,6 +170,7 @@ class MainActivity : AppCompatActivity() { resultText = try { val result = readerCpp.read(image) runtime2 += result?.time?.toInt() ?: 0 + resultPoints = mapPoints(result?.position) (result?.let { "${it.format}: ${it.text}" } ?: "") } catch (e: Throwable) { e.message ?: "Error" @@ -182,6 +192,7 @@ class MainActivity : AppCompatActivity() { runtime2 = 0 } + binding.detectorView.update(resultPoints) showResult(resultText, infoText) }) @@ -210,6 +221,33 @@ class MainActivity : AppCompatActivity() { }, ContextCompat.getMainExecutor(this)) } + private fun mapPoints(resultPoints: Array?) = resultPoints?.map { + image2View(Point(it.x.roundToInt(), it.y.roundToInt())).toPointF() + } + + private fun mapPoints(position: BarcodeReader.Position?) = position?.let { + val w: Int + val h: Int + if (imageRotation == 90 || imageRotation == 270) { + w = imageHeight + h = imageWidth + } else { + w = imageWidth + h = imageHeight + } + val vf = binding.viewFinder + val xf = vf.width / w.toFloat() + val yf = vf.height / h.toFloat() + listOf( + it.topLeft, + it.topRight, + it.bottomRight, + it.bottomLeft + ).map { p -> + PointF(p.x * xf, p.y * yf) + } + } + private fun showResult(resultText: String, fpsText: String?) = binding.viewFinder.post { // Update the text and UI binding.result.text = resultText @@ -234,11 +272,15 @@ class MainActivity : AppCompatActivity() { } } - private fun image2View(p: Point) : Point { + private fun image2View(p: Point): Point { val s = kotlin.math.min(binding.viewFinder.width, binding.viewFinder.height).toFloat() / imageHeight val o = (kotlin.math.max(binding.viewFinder.width, binding.viewFinder.height) - (imageWidth * s).toInt()) / 2 val res = PointF(p.x * s + o, p.y * s).toPoint() - return if (imageRotation % 180 == 0) res else Point(binding.viewFinder.width - res.y, res.x) + return when (imageRotation) { + 0 -> res + 180 -> Point(binding.viewFinder.width - res.x, binding.viewFinder.height - res.y) + else -> Point(binding.viewFinder.width - res.y, res.x) + } } override fun onResume() { diff --git a/wrappers/android/app/src/main/res/layout-land/activity_camera.xml b/wrappers/android/app/src/main/res/layout-land/activity_camera.xml index 1b1684ce3d..1c038688f4 100644 --- a/wrappers/android/app/src/main/res/layout-land/activity_camera.xml +++ b/wrappers/android/app/src/main/res/layout-land/activity_camera.xml @@ -13,6 +13,11 @@ android:layout_height="match_parent" app:scaleType="fitCenter" /> + + - \ No newline at end of file + diff --git a/wrappers/android/app/src/main/res/layout/activity_camera.xml b/wrappers/android/app/src/main/res/layout/activity_camera.xml index 62bab293db..971a7f64bb 100644 --- a/wrappers/android/app/src/main/res/layout/activity_camera.xml +++ b/wrappers/android/app/src/main/res/layout/activity_camera.xml @@ -13,6 +13,11 @@ android:layout_height="match_parent" app:scaleType="fitCenter" /> + + - \ No newline at end of file + From 776ef6178463aca92576fd9bd1339e8d39bb6c0d Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 13 Apr 2022 13:29:31 +0200 Subject: [PATCH 125/185] android: fix and simplify preview overlay in demo app cropRect and symbol position are now both drawn on the canvas. This also fixes a scale/offset issue with the recently introduced position outlining. Position info in the java/upstream code path was dropped (not important). --- .../com/example/zxingcppdemo/DetectorView.kt | 48 -------- .../com/example/zxingcppdemo/MainActivity.kt | 112 +++++------------- .../example/zxingcppdemo/PreviewOverlay.kt | 87 ++++++++++++++ .../main/res/layout-land/activity_camera.xml | 14 +-- .../src/main/res/layout/activity_camera.xml | 12 +- 5 files changed, 120 insertions(+), 153 deletions(-) delete mode 100644 wrappers/android/app/src/main/java/com/example/zxingcppdemo/DetectorView.kt create mode 100644 wrappers/android/app/src/main/java/com/example/zxingcppdemo/PreviewOverlay.kt diff --git a/wrappers/android/app/src/main/java/com/example/zxingcppdemo/DetectorView.kt b/wrappers/android/app/src/main/java/com/example/zxingcppdemo/DetectorView.kt deleted file mode 100644 index 72710cd6ad..0000000000 --- a/wrappers/android/app/src/main/java/com/example/zxingcppdemo/DetectorView.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.example.zxingcppdemo - -import android.content.Context -import android.graphics.* -import android.util.AttributeSet -import android.view.View - -class DetectorView : View { - private val paint = Paint(Paint.ANTI_ALIAS_FLAG) - private val path = Path() - - init { - paint.style = Paint.Style.STROKE - paint.color = 0xffffffff.toInt() - paint.strokeWidth = context.resources.displayMetrics.density - } - - constructor(context: Context, attrs: AttributeSet) : - super(context, attrs) - - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : - super(context, attrs, defStyleAttr) - - fun update(points: List? = null) { - path.apply { - rewind() - if (!points.isNullOrEmpty()) { - moveTo(points[0]) - val size = points.size - for (i in 1 until size) { - lineTo(points[i]) - } - close() - } - } - invalidate() - } - - override fun onDraw(canvas: Canvas) { - canvas.drawColor(0, PorterDuff.Mode.CLEAR) - if (!path.isEmpty) { - canvas.drawPath(path, paint) - } - } -} - -private fun Path.lineTo(point: PointF) = lineTo(point.x, point.y) -private fun Path.moveTo(point: PointF) = moveTo(point.x, point.y) diff --git a/wrappers/android/app/src/main/java/com/example/zxingcppdemo/MainActivity.kt b/wrappers/android/app/src/main/java/com/example/zxingcppdemo/MainActivity.kt index 8497e7d6cd..40768ba4b3 100644 --- a/wrappers/android/app/src/main/java/com/example/zxingcppdemo/MainActivity.kt +++ b/wrappers/android/app/src/main/java/com/example/zxingcppdemo/MainActivity.kt @@ -19,22 +19,16 @@ package com.example.zxingcppdemo import android.Manifest import android.content.Context import android.content.pm.PackageManager -import android.graphics.Point -import android.graphics.PointF -import android.graphics.Rect +import android.graphics.* import android.hardware.camera2.CaptureRequest import android.media.AudioManager import android.media.ToneGenerator import android.os.Bundle import android.view.View -import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import androidx.camera.camera2.interop.Camera2CameraControl import androidx.camera.camera2.interop.CaptureRequestOptions -import androidx.camera.core.AspectRatio -import androidx.camera.core.CameraSelector -import androidx.camera.core.ImageAnalysis -import androidx.camera.core.Preview +import androidx.camera.core.* import androidx.camera.lifecycle.ProcessCameraProvider import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat @@ -47,10 +41,9 @@ import com.google.zxing.common.HybridBinarizer import com.zxingcpp.BarcodeReader import com.zxingcpp.BarcodeReader.Format import java.util.concurrent.Executors -import kotlin.math.abs -import kotlin.math.roundToInt import kotlin.random.Random + class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityCameraBinding private val executor = Executors.newSingleThreadExecutor() @@ -59,10 +52,6 @@ class MainActivity : AppCompatActivity() { private val beeper = ToneGenerator(AudioManager.STREAM_NOTIFICATION, 50) private var lastText = String() - private var cropRect = Rect() - private var imageRotation: Int = 0 - private var imageWidth: Int = 0 - private var imageHeight: Int = 0 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -108,10 +97,6 @@ class MainActivity : AppCompatActivity() { val readerCpp = BarcodeReader() imageAnalysis.setAnalyzer(executor, ImageAnalysis.Analyzer { image -> - imageRotation = image.imageInfo.rotationDegrees - imageWidth = image.width - imageHeight = image.height - // Early exit: image analysis is in paused state if (binding.pause.isChecked) { image.close() @@ -119,13 +104,14 @@ class MainActivity : AppCompatActivity() { } val cropSize = image.height / 3 * 2 - cropRect = if (binding.crop.isChecked) + val cropRect = if (binding.crop.isChecked) Rect( (image.width - cropSize) / 2, (image.height - cropSize) / 2, (image.width - cropSize) / 2 + cropSize, (image.height - cropSize) / 2 + cropSize ) else Rect(0, 0, image.width, image.height) + image.setCropRect(cropRect) val startTime = System.currentTimeMillis() var resultText: String @@ -146,14 +132,13 @@ class MainActivity : AppCompatActivity() { val bitmap = BinaryBitmap( HybridBinarizer( PlanarYUVLuminanceSource( - data, imageWidth, imageHeight, + data, image.width, image.height, cropRect.left, cropRect.top, cropRect.width(), cropRect.height(), false ) ) ) val result = readerJava.decode(bitmap, hints) - resultPoints = mapPoints(result?.resultPoints) result?.let { "${it.barcodeFormat}: ${it.text}" } ?: "" } catch (e: Throwable) { if (e.toString() != "com.google.zxing.NotFoundException") e.toString() else "" @@ -165,12 +150,19 @@ class MainActivity : AppCompatActivity() { tryRotate = binding.tryRotate.isChecked ) - image.setCropRect(cropRect) - resultText = try { val result = readerCpp.read(image) runtime2 += result?.time?.toInt() ?: 0 - resultPoints = mapPoints(result?.position) + resultPoints = result?.position?.let { + listOf( + it.topLeft, + it.topRight, + it.bottomRight, + it.bottomLeft + ).map { p -> + p.toPointF() + } + } (result?.let { "${it.format}: ${it.text}" } ?: "") } catch (e: Throwable) { e.message ?: "Error" @@ -192,8 +184,7 @@ class MainActivity : AppCompatActivity() { runtime2 = 0 } - binding.detectorView.update(resultPoints) - showResult(resultText, infoText) + showResult(resultText, infoText, resultPoints, image) }) // Create a new camera selector each time, enforcing lens facing @@ -221,67 +212,22 @@ class MainActivity : AppCompatActivity() { }, ContextCompat.getMainExecutor(this)) } - private fun mapPoints(resultPoints: Array?) = resultPoints?.map { - image2View(Point(it.x.roundToInt(), it.y.roundToInt())).toPointF() - } - - private fun mapPoints(position: BarcodeReader.Position?) = position?.let { - val w: Int - val h: Int - if (imageRotation == 90 || imageRotation == 270) { - w = imageHeight - h = imageWidth - } else { - w = imageWidth - h = imageHeight - } - val vf = binding.viewFinder - val xf = vf.width / w.toFloat() - val yf = vf.height / h.toFloat() - listOf( - it.topLeft, - it.topRight, - it.bottomRight, - it.bottomLeft - ).map { p -> - PointF(p.x * xf, p.y * yf) - } - } - - private fun showResult(resultText: String, fpsText: String?) = binding.viewFinder.post { - // Update the text and UI - binding.result.text = resultText - binding.result.visibility = View.VISIBLE - - val tl = image2View(Point(cropRect.left, cropRect.top)) - val br = image2View(Point(cropRect.right, cropRect.bottom)) - (binding.cropRect.layoutParams as ViewGroup.MarginLayoutParams).apply { - leftMargin = kotlin.math.min(tl.x, br.x) - topMargin = kotlin.math.min(tl.y, br.y) - width = abs(br.x - tl.x) - height = abs(br.y - tl.y) - } - binding.cropRect.visibility = if (binding.crop.isChecked) View.VISIBLE else View.GONE + private fun showResult(resultText: String, fpsText: String?, points: List?, image: ImageProxy) = + binding.viewFinder.post { + // Update the text and UI + binding.result.text = resultText + binding.result.visibility = View.VISIBLE - if (fpsText != null) - binding.fps.text = fpsText + binding.overlay.update(binding.viewFinder, image, points) - if (resultText.isNotEmpty() && lastText != resultText) { - lastText = resultText - beeper.startTone(ToneGenerator.TONE_PROP_BEEP) - } - } + if (fpsText != null) + binding.fps.text = fpsText - private fun image2View(p: Point): Point { - val s = kotlin.math.min(binding.viewFinder.width, binding.viewFinder.height).toFloat() / imageHeight - val o = (kotlin.math.max(binding.viewFinder.width, binding.viewFinder.height) - (imageWidth * s).toInt()) / 2 - val res = PointF(p.x * s + o, p.y * s).toPoint() - return when (imageRotation) { - 0 -> res - 180 -> Point(binding.viewFinder.width - res.x, binding.viewFinder.height - res.y) - else -> Point(binding.viewFinder.width - res.y, res.x) + if (resultText.isNotEmpty() && lastText != resultText) { + lastText = resultText + beeper.startTone(ToneGenerator.TONE_PROP_BEEP) + } } - } override fun onResume() { super.onResume() diff --git a/wrappers/android/app/src/main/java/com/example/zxingcppdemo/PreviewOverlay.kt b/wrappers/android/app/src/main/java/com/example/zxingcppdemo/PreviewOverlay.kt new file mode 100644 index 0000000000..0bc84a5300 --- /dev/null +++ b/wrappers/android/app/src/main/java/com/example/zxingcppdemo/PreviewOverlay.kt @@ -0,0 +1,87 @@ +/* +* Copyright 2022 Axel Waggershauser +* Copyright 2022 Markus Fisch +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.example.zxingcppdemo + +import android.content.Context +import android.graphics.* +import android.util.AttributeSet +import android.view.View +import androidx.camera.core.ImageProxy +import kotlin.math.max +import kotlin.math.min + +class PreviewOverlay constructor(context: Context, attributeSet: AttributeSet?) : + View(context, attributeSet) { + + private val paintPath = Paint(Paint.ANTI_ALIAS_FLAG).apply { + style = Paint.Style.STROKE + color = 0xff00ff00.toInt() + strokeWidth = 2 * context.resources.displayMetrics.density + } + private val paintRect = Paint().apply { + style = Paint.Style.STROKE + color = 0x80ffffff.toInt() + strokeWidth = 3 * context.resources.displayMetrics.density + } + private val path = Path() + private var cropRect = Rect() + private var rotation = 0 + private var s: Float = 0f + private var o: Float = 0f + + fun update(viewFinder: View, image: ImageProxy, points: List?) { + cropRect = image.cropRect + rotation = image.imageInfo.rotationDegrees + s = min(viewFinder.width, viewFinder.height).toFloat() / image.height + o = (max(viewFinder.width, viewFinder.height) - (image.width * s).toInt()).toFloat() / 2 + + path.apply { + rewind() + if (!points.isNullOrEmpty()) { + moveTo(points.last().x, points.last().y) + for (p in points) + lineTo(p.x, p.y) + } + } + invalidate() + } + + override fun onDraw(canvas: Canvas) { + canvas.apply { + drawColor(0, PorterDuff.Mode.CLEAR) + // draw the cropRect, which is relative to the original image orientation + save() + if (rotation == 90) { + translate(width.toFloat(), 0f) + rotate(rotation.toFloat()) + } + translate(o, 0f) + scale(s, s) + drawRect(cropRect, paintRect) + restore() + + // draw the path, which is relative to the (centered) rotated cropRect + when (rotation) { + 0, 180 -> translate(o + cropRect.left * s, cropRect.top * s) + 90 -> translate(cropRect.top * s, o + cropRect.left * s) + } + scale(s, s) + drawPath(path, paintPath) + } + } +} diff --git a/wrappers/android/app/src/main/res/layout-land/activity_camera.xml b/wrappers/android/app/src/main/res/layout-land/activity_camera.xml index 1c038688f4..3f7859de28 100644 --- a/wrappers/android/app/src/main/res/layout-land/activity_camera.xml +++ b/wrappers/android/app/src/main/res/layout-land/activity_camera.xml @@ -13,21 +13,11 @@ android:layout_height="match_parent" app:scaleType="fitCenter" /> - - - - - - - - Date: Thu, 14 Apr 2022 09:21:37 +0200 Subject: [PATCH 126/185] OneD: comment and trivial logic fixes --- core/src/oned/ODReader.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index af900ca079..7be4265a80 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -135,15 +135,15 @@ static Results DoDecode(const std::vector>& readers, // Look for a barcode for (size_t r = 0; r < readers.size(); ++r) { // If this is a pure symbol, then checking a single non-empty line is sufficient for all but the stacked - // DataBar codes. They are the only ones using the decodingState, which we can use a flag here. + // DataBar codes. They are the only ones using the decodingState, which we can use as a flag here. if (isPure && i && !decodingState[r]) continue; PatternView next(bars); do { Result result = readers[r]->decodePattern(rowNumber, next, decodingState[r]); - result.incrementLineCount(); if (result.isValid()) { + result.incrementLineCount(); if (upsideDown) { // update position (flip horizontally). auto points = result.position(); @@ -182,18 +182,21 @@ static Results DoDecode(const std::vector>& readers, } other.setPosition(points); other.incrementLineCount(); - // clear the result below, so we don't insert it again + // clear the result, so we don't insert it again below result = Result(DecodeStatus::NotFound); + break; } } } - if (result.isValid()) { + if (result.isValid()) res.push_back(std::move(result)); - if (maxSymbols && Reduce(res, 0, [&](int s, const Result& r) { - return s + (r.lineCount() >= minLineCount); - }) == maxSymbols) - goto out; + + if (maxSymbols && Reduce(res, 0, [&](int s, const Result& r) { + return s + (r.lineCount() >= minLineCount); + }) == maxSymbols) { + goto out; + } } } // make sure we make progress and we start the next try on a bar From e697808640596c8db29bb3a58321726c97ea276c Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 14 Apr 2022 10:41:36 +0200 Subject: [PATCH 127/185] DataBar: EstimateLineCount for stacked versions to support minLineCount This is a bit of a hack/workaround for the fact that stacked DataBar symbols are actually 2D codes but are handled as 1D codes. Without this some stacked DataBar symbols necessarily failed in non-tryHarder-mode. --- core/src/Result.cpp | 4 ++-- core/src/Result.h | 3 ++- core/src/oned/ODDataBarCommon.cpp | 6 ++++++ core/src/oned/ODDataBarCommon.h | 1 + core/src/oned/ODDataBarExpandedReader.cpp | 3 ++- core/src/oned/ODDataBarReader.cpp | 4 ++-- 6 files changed, 15 insertions(+), 6 deletions(-) diff --git a/core/src/Result.cpp b/core/src/Result.cpp index f1064fec80..60faebe943 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -26,9 +26,9 @@ namespace ZXing { Result::Result(std::wstring&& text, Position&& position, BarcodeFormat format, ByteArray&& rawBytes, - std::string symbologyIdentifier, StructuredAppendInfo sai, const bool readerInit) + std::string symbologyIdentifier, StructuredAppendInfo sai, const bool readerInit, int lineCount) : _format(format), _text(std::move(text)), _position(std::move(position)), _rawBytes(std::move(rawBytes)), - _symbologyIdentifier(symbologyIdentifier), _sai(sai), _readerInit(readerInit) + _symbologyIdentifier(symbologyIdentifier), _sai(sai), _readerInit(readerInit), _lineCount(lineCount) { _numBits = Size(_rawBytes) * 8; } diff --git a/core/src/Result.h b/core/src/Result.h index 50543889dd..789af3d956 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -43,7 +43,8 @@ class Result explicit Result(DecodeStatus status) : _status(status) {} Result(std::wstring&& text, Position&& position, BarcodeFormat format, ByteArray&& rawBytes = {}, - std::string symbologyIdentifier = "", StructuredAppendInfo sai = {}, const bool readerInit = false); + std::string symbologyIdentifier = "", StructuredAppendInfo sai = {}, const bool readerInit = false, + int lineCount = 0); // 1D convenience constructor Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, ByteArray&& rawBytes = {}, diff --git a/core/src/oned/ODDataBarCommon.cpp b/core/src/oned/ODDataBarCommon.cpp index b47d55025d..aba8505311 100644 --- a/core/src/oned/ODDataBarCommon.cpp +++ b/core/src/oned/ODDataBarCommon.cpp @@ -163,4 +163,10 @@ Position EstimatePosition(const Pair& first, const Pair& last) return Position{{first.xStart, first.y}, {first.xStop, first.y}, {last.xStop, last.y}, {last.xStart, last.y}}; } +int EstimateLineCount(const Pair& first, const Pair& last) +{ + // see incrementLineCount() in ODReader.cpp for the -1 here + return std::min(first.count, last.count) * ((first.y == last.y) ? 1 : 2) - 1; +} + } // namespace ZXing::OneD::DataBar diff --git a/core/src/oned/ODDataBarCommon.h b/core/src/oned/ODDataBarCommon.h index ce8e98683e..08669b208e 100644 --- a/core/src/oned/ODDataBarCommon.h +++ b/core/src/oned/ODDataBarCommon.h @@ -138,5 +138,6 @@ bool ReadDataCharacterRaw(const PatternView& view, int numModules, bool reversed int GetValue(const Array4I& widths, int maxWidth, bool noNarrow); Position EstimatePosition(const Pair& first, const Pair& last); +int EstimateLineCount(const Pair& first, const Pair& last); } // namespace ZXing::OneD::DataBar diff --git a/core/src/oned/ODDataBarExpandedReader.cpp b/core/src/oned/ODDataBarExpandedReader.cpp index 740e344359..5a9bfdbe02 100644 --- a/core/src/oned/ODDataBarExpandedReader.cpp +++ b/core/src/oned/ODDataBarExpandedReader.cpp @@ -377,7 +377,8 @@ Result DataBarExpandedReader::decodePattern(int rowNumber, PatternView& view, std::string symbologyIdentifier("]e0"); return {TextDecoder::FromLatin1(txt), EstimatePosition(pairs.front(), pairs.back()), - BarcodeFormat::DataBarExpanded, {}, symbologyIdentifier}; + BarcodeFormat::DataBarExpanded, {}, symbologyIdentifier, {}, false, + EstimateLineCount(pairs.front(), pairs.back())}; } } // namespace ZXing::OneD diff --git a/core/src/oned/ODDataBarReader.cpp b/core/src/oned/ODDataBarReader.cpp index ac9844f3fa..fa5ce47f14 100644 --- a/core/src/oned/ODDataBarReader.cpp +++ b/core/src/oned/ODDataBarReader.cpp @@ -210,8 +210,8 @@ Result DataBarReader::decodePattern(int rowNumber, PatternView& next, for (const auto& rightPair : prevState->rightPairs) if (ChecksumIsValid(leftPair, rightPair)) return {TextDecoder::FromLatin1(ConstructText(leftPair, rightPair)), - EstimatePosition(leftPair, rightPair), BarcodeFormat::DataBar, - {}, std::move(symbologyIdentifier)}; + EstimatePosition(leftPair, rightPair), BarcodeFormat::DataBar, {}, + std::move(symbologyIdentifier), {}, false, EstimateLineCount(leftPair, rightPair)}; #endif // guaratee progress (see loop in ODReader.cpp) From 17fd4fdc3e392f52e58f0b2b591b0e0e8b6ccf53 Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 14 Apr 2022 11:38:44 +0200 Subject: [PATCH 128/185] ODReader: fix build regression (insufficient attention on my side...) --- core/src/oned/ODReader.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index 7be4265a80..2eea069d55 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -197,7 +197,6 @@ static Results DoDecode(const std::vector>& readers, }) == maxSymbols) { goto out; } - } } // make sure we make progress and we start the next try on a bar next.shift(2 - (next.index() % 2)); From e47f28244296c03c5788b082f0b673ef385e80cf Mon Sep 17 00:00:00 2001 From: Markus Fisch Date: Thu, 14 Apr 2022 11:20:43 +0200 Subject: [PATCH 129/185] android: remove clearing overlay canvas It's unnecessary, and clears out the camera frame when the underlying surface is the same, which it is for some devices/graphics drivers. --- .../app/src/main/java/com/example/zxingcppdemo/PreviewOverlay.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/wrappers/android/app/src/main/java/com/example/zxingcppdemo/PreviewOverlay.kt b/wrappers/android/app/src/main/java/com/example/zxingcppdemo/PreviewOverlay.kt index 0bc84a5300..7c2db15c12 100644 --- a/wrappers/android/app/src/main/java/com/example/zxingcppdemo/PreviewOverlay.kt +++ b/wrappers/android/app/src/main/java/com/example/zxingcppdemo/PreviewOverlay.kt @@ -63,7 +63,6 @@ class PreviewOverlay constructor(context: Context, attributeSet: AttributeSet?) override fun onDraw(canvas: Canvas) { canvas.apply { - drawColor(0, PorterDuff.Mode.CLEAR) // draw the cropRect, which is relative to the original image orientation save() if (rotation == 90) { From 59b68f854d7f320f7e7e4690f09952993cec629b Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 14 Apr 2022 14:51:27 +0200 Subject: [PATCH 130/185] ODReader: improve detection with minLineCount > 1 If minLineCount > 1 then we can improve the likelyhood of a detection by checking additional rows very close to the first detected valid row. This is especially true for the non-tryHarder mode. This prepares the switch to a default minLineCount of 2. --- core/src/oned/ODReader.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index 2eea069d55..31b79df8a1 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -102,6 +102,10 @@ static Results DoDecode(const std::vector>& readers, height : // Look at the whole image, not just the center 15; // 15 rows spaced 1/32 apart is roughly the middle half of the image + if (isPure) + minLineCount = 1; + std::vector checkRows; + PatternRow bars; bars.reserve(128); // e.g. EAN-13 has 59 bars/spaces @@ -116,6 +120,15 @@ static Results DoDecode(const std::vector>& readers, break; } + // See if we have additional check rows (see below) to process + if (checkRows.size()) { + --i; + rowNumber = checkRows.back(); + checkRows.pop_back(); + if (rowNumber < 0 || rowNumber >= height) + continue; + } + if (!image.getPatternRow(rowNumber, rotate ? 270 : 0, bars)) continue; @@ -197,6 +210,14 @@ static Results DoDecode(const std::vector>& readers, }) == maxSymbols) { goto out; } + + // if we found a valid code but have a minLineCount > 1, add additional check rows above and + // below the current one + if (checkRows.empty() && minLineCount > 1 && rowStep > 1) { + checkRows = {rowNumber - 1, rowNumber + 1}; + if (rowStep > 2) + checkRows.insert(checkRows.end(), {rowNumber - 2, rowNumber + 2}); + } } // make sure we make progress and we start the next try on a bar next.shift(2 - (next.index() % 2)); From 11fc1d889071562a9eb4fb7ac94e65f220dd86ab Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 14 Apr 2022 14:58:42 +0200 Subject: [PATCH 131/185] ODReader: change default value of DecodingHints::minLineCount to 2 This change prioritizes quality over quantity as the default setting. It results in a few 'lost' samples from the test images set but removes almost all remaining misreads. The user can get back to the old behavior my setting minLineCount to 1. This fixes #255. --- core/src/DecodeHints.h | 4 +-- test/blackbox/BlackboxTestRunner.cpp | 46 ++++++++++++++-------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/core/src/DecodeHints.h b/core/src/DecodeHints.h index 7b308e9ebf..a19f56e17b 100644 --- a/core/src/DecodeHints.h +++ b/core/src/DecodeHints.h @@ -62,7 +62,7 @@ class DecodeHints std::string _characterSet; std::vector _allowedLengths; BarcodeFormats _formats = BarcodeFormat::None; - uint8_t _minLineCount = 1; + uint8_t _minLineCount = 2; public: // bitfields don't get default initialized to 0. @@ -95,7 +95,7 @@ class DecodeHints /// Set to true if the input contains nothing but a perfectly aligned barcode (generated image) ZX_PROPERTY(bool, isPure, setIsPure) - /// The number of scan lines in a 1D barcode that have to be equal to accept the result, default is 1 + /// The number of scan lines in a 1D barcode that have to be equal to accept the result, default is 2 ZX_PROPERTY(uint8_t, minLineCount, setMinLineCount) /// Specifies what character encoding to use when decoding, where applicable. diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 703c88dc94..7e6112eaf1 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -396,8 +396,8 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set }); runTests("codabar-2", "Codabar", 4, { - { 3, 3, 0 }, - { 3, 3, 180 }, + { 2, 3, 0 }, + { 2, 3, 180 }, }); runTests("code39-1", "Code39", 4, { @@ -426,7 +426,7 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set }); runTests("code128-2", "Code128", 21, { - { 19, 21, 0 }, + { 18, 21, 0 }, { 19, 21, 180 }, }); @@ -442,13 +442,13 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set }); runTests("ean13-1", "EAN-13", 31, { - { 26, 29, 0 }, + { 24, 29, 0 }, { 23, 29, 180 }, }); runTests("ean13-2", "EAN-13", 24, { - { 7, 14, 0 }, - { 7, 14, 180 }, + { 7, 13, 0 }, + { 7, 13, 180 }, }); runTests("ean13-3", "EAN-13", 21, { @@ -457,12 +457,12 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set }); runTests("ean13-4", "EAN-13", 22, { - { 7, 14, 0 }, - { 8, 14, 180 }, + { 6, 13, 0 }, + { 8, 13, 180 }, }); runTests("ean13-extension-1", "EAN-13", 5, { - { 4, 5, 0 }, + { 3, 5, 0 }, { 3, 5, 180 }, }, DecodeHints().setEanAddOnSymbol(EanAddOnSymbol::Require)); @@ -485,33 +485,33 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set }); runTests("upca-1", "UPC-A", 12, { - { 9, 12, 0, 1, 0 }, - { 11, 12, 0, 1, 180 }, + { 9, 12, 0 }, + { 11, 12, 180 }, }); runTests("upca-2", "UPC-A", 36, { - { 17, 23, 0 }, - { 18, 23, 180 }, + { 17, 22, 0 }, + { 16, 22, 180 }, }); runTests("upca-3", "UPC-A", 21, { - { 7, 10, 0, 1, 0 }, - { 8, 10, 0, 1, 180 }, + { 7, 10, 0 }, + { 8, 10, 180 }, }); runTests("upca-4", "UPC-A", 19, { - { 9, 11, 0, 1, 0 }, - { 9, 11, 0, 1, 180 }, + { 8, 12, 0 }, + { 9, 12, 180 }, }); runTests("upca-5", "UPC-A", 32, { { 17, 20, 0 }, - { 19, 20, 180 }, + { 18, 20, 180 }, }); runTests("upca-extension-1", "UPC-A", 6, { - { 4, 6, 0 }, - { 4, 6, 180 }, + { 4, 4, 0 }, + { 3, 4, 180 }, }, DecodeHints().setEanAddOnSymbol(EanAddOnSymbol::Require)); runTests("upce-1", "UPC-E", 3, { @@ -521,13 +521,13 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set }); runTests("upce-2", "UPC-E", 28, { - { 19, 22, 0, 1, 0 }, + { 17, 22, 0, 1, 0 }, { 20, 22, 1, 1, 180 }, }); runTests("upce-3", "UPC-E", 11, { - { 6, 8, 0 }, - { 6, 8, 180 }, + { 5, 7, 0 }, + { 6, 7, 180 }, }); runTests("rss14-1", "DataBar", 6, { From e9a3d56fbe94b98dfa3e519389734ae1afb9bec3 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 15 Apr 2022 20:50:22 +0200 Subject: [PATCH 132/185] DataBar: fix numerous issues related to the position estimation Position and Linecount estimation was completely broken. --- core/src/oned/ODDataBarReader.cpp | 28 +++++++++++++++++----------- test/blackbox/BlackboxTestRunner.cpp | 4 ++-- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/core/src/oned/ODDataBarReader.cpp b/core/src/oned/ODDataBarReader.cpp index fa5ce47f14..e2d8af00d1 100644 --- a/core/src/oned/ODDataBarReader.cpp +++ b/core/src/oned/ODDataBarReader.cpp @@ -127,8 +127,8 @@ static Pair ReadPair(const PatternView& view, bool rightPair) if (auto outside = ReadDataCharacter(rightPair ? RightChar(view) : LeftChar(view), true, rightPair)) if (auto inside = ReadDataCharacter(rightPair ? LeftChar(view) : RightChar(view), false, rightPair)) { // include left and right guards - int xStart = view.pixelsInFront() - (rightPair ? 0 : view[-1] + std::min(view[-2], view[-1])); - int xStop = view.pixelsTillEnd() + (rightPair ? view[FULL_PAIR_SIZE] + view[FULL_PAIR_SIZE + 1] : 0); + int xStart = view.pixelsInFront() - view[-1]; + int xStop = view.pixelsTillEnd() + 2 * view[FULL_PAIR_SIZE]; return {outside, inside, pattern, xStart, xStop}; } @@ -167,7 +167,7 @@ Result DataBarReader::decodePattern(int rowNumber, PatternView& next, std::unique_ptr& state) const { #if 0 // non-stacked version - next = next.subView(-1, FULL_PAIR_SIZE + 2); + next = next.subView(-1, FULL_PAIR_SIZE); // yes: the first view we test is at index 1 (black bar at 0 would be the guard pattern) while (next.shift(2)) { if (IsLeftPair(next)) { @@ -184,7 +184,7 @@ Result DataBarReader::decodePattern(int rowNumber, PatternView& next, state.reset(new State); auto* prevState = static_cast(state.get()); - next = next.subView(0, FULL_PAIR_SIZE + 2); // +2 reflects the guard pattern on the right + next = next.subView(0, FULL_PAIR_SIZE); // yes: the first view we test is at index 1 (black bar at 0 would be the guard pattern) while (next.shift(1)) { if (IsLeftPair(next)) { @@ -203,15 +203,21 @@ Result DataBarReader::decodePattern(int rowNumber, PatternView& next, } } - // Symbology identifier ISO/IEC 24724:2011 Section 9 and GS1 General Specifications 5.1.3 Figure 5.1.3-2 - std::string symbologyIdentifier("]e0"); - for (const auto& leftPair : prevState->leftPairs) for (const auto& rightPair : prevState->rightPairs) - if (ChecksumIsValid(leftPair, rightPair)) - return {TextDecoder::FromLatin1(ConstructText(leftPair, rightPair)), - EstimatePosition(leftPair, rightPair), BarcodeFormat::DataBar, {}, - std::move(symbologyIdentifier), {}, false, EstimateLineCount(leftPair, rightPair)}; + if (ChecksumIsValid(leftPair, rightPair)) { + // Symbology identifier ISO/IEC 24724:2011 Section 9 and GS1 General Specifications 5.1.3 Figure 5.1.3-2 + std::string symbologyIdentifier("]e0"); + + Result res{TextDecoder::FromLatin1(ConstructText(leftPair, rightPair)), + EstimatePosition(leftPair, rightPair), BarcodeFormat::DataBar, + {}, std::move(symbologyIdentifier), {}, false, + EstimateLineCount(leftPair, rightPair)}; + + prevState->leftPairs.erase(leftPair); + prevState->rightPairs.erase(rightPair); + return res; + } #endif // guaratee progress (see loop in ODReader.cpp) diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 7e6112eaf1..146309d123 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -536,8 +536,8 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set }); runTests("rss14-2", "DataBar", 16, { - { 8 , 10, 0 }, - { 10, 10, 180 }, + { 8, 10, 0 }, + { 9, 10, 180 }, }); runTests("rssexpanded-1", "DataBarExpanded", 33, { From 5c8898fee887347f13abfe676d22f75a59cf1ed7 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 15 Apr 2022 20:54:24 +0200 Subject: [PATCH 133/185] DataBar: skip view of right pair to make optimal progress (performance fix) --- core/src/oned/ODDataBarReader.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/oned/ODDataBarReader.cpp b/core/src/oned/ODDataBarReader.cpp index e2d8af00d1..1c4f2a38c9 100644 --- a/core/src/oned/ODDataBarReader.cpp +++ b/core/src/oned/ODDataBarReader.cpp @@ -199,6 +199,7 @@ Result DataBarReader::decodePattern(int rowNumber, PatternView& next, if (auto rightPair = ReadPair(next, true)) { rightPair.y = rowNumber; prevState->rightPairs.insert(rightPair); + next.shift(FULL_PAIR_SIZE + 2); } } } From b4abea93b1ba02b4e87ef297ec04666046be013c Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 15 Apr 2022 20:57:32 +0200 Subject: [PATCH 134/185] ODReader: check for next.size() instead of isValid() (performance fix) An empty PatternView after `extend()` is valid but uninteresting to look at again. --- core/src/oned/ODReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index 31b79df8a1..db7ec26593 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -222,7 +222,7 @@ static Results DoDecode(const std::vector>& readers, // make sure we make progress and we start the next try on a bar next.shift(2 - (next.index() % 2)); next.extend(); - } while (tryHarder && next.isValid()); + } while (tryHarder && next.size()); } } } From 922cd41c332c7b8a8fb4e771d0f1713405c284ca Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 15 Apr 2022 21:35:16 +0200 Subject: [PATCH 135/185] DataBarExpanded: fix position estimate (see also earlier DataBar fix) --- core/src/oned/ODDataBarExpandedReader.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/src/oned/ODDataBarExpandedReader.cpp b/core/src/oned/ODDataBarExpandedReader.cpp index 5a9bfdbe02..bccc384467 100644 --- a/core/src/oned/ODDataBarExpandedReader.cpp +++ b/core/src/oned/ODDataBarExpandedReader.cpp @@ -316,6 +316,14 @@ static Pairs FindValidSequence(PairMap& all) return stack; } +static void RemovePairs(PairMap& all, const Pairs& pairs) +{ + for(const auto& p : pairs) + if (auto i = Find(all[p.finder], p); i != all[p.finder].end()) + if (--i->count == 0) + all[p.finder].erase(i); +} + static BitArray BuildBitArray(const Pairs& pairs) { BitArray res; @@ -373,9 +381,13 @@ Result DataBarExpandedReader::decodePattern(int rowNumber, PatternView& view, if (txt.empty()) return Result(DecodeStatus::NotFound); + RemovePairs(allPairs, pairs); + // Symbology identifier ISO/IEC 24724:2011 Section 9 and GS1 General Specifications 5.1.3 Figure 5.1.3-2 std::string symbologyIdentifier("]e0"); + // TODO: EstimatePosition misses part of the symbol in the stacked case where the last row contains less pairs than + // the first return {TextDecoder::FromLatin1(txt), EstimatePosition(pairs.front(), pairs.back()), BarcodeFormat::DataBarExpanded, {}, symbologyIdentifier, {}, false, EstimateLineCount(pairs.front(), pairs.back())}; From 52e0235b1ed06c77e0aa7b52223502d028860f05 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 15 Apr 2022 21:42:57 +0200 Subject: [PATCH 136/185] Result: move intersection check of symbol position into operator==() Two results are now only considered equal if they are believed to represent the same symbol, not only the same data/format. In the 1D case, we use the heuristic from the `ODReader` that incorporates distance and length of the 2 symbols. This prepares the upcoming multi-resolution disambiguation. --- core/src/Quadrilateral.h | 8 ++++++++ core/src/Result.cpp | 17 +++++++++++++++++ core/src/Result.h | 4 +--- core/src/oned/ODReader.cpp | 34 ++++++++++++++-------------------- 4 files changed, 40 insertions(+), 23 deletions(-) diff --git a/core/src/Quadrilateral.h b/core/src/Quadrilateral.h index d11117a4ea..90f5aaae7f 100644 --- a/core/src/Quadrilateral.h +++ b/core/src/Quadrilateral.h @@ -102,6 +102,14 @@ bool IsConvex(const Quadrilateral& poly) return M / m < 4.0; } +template +bool IsIntersecting(const Quadrilateral& a, const Quadrilateral& b) +{ + // TODO: this is only a quick and dirty approximation that works for the trivial standard cases + bool x = b.topRight().x < a.topLeft().x || b.topLeft().x > a.topRight().x; + bool y = b.bottomLeft().y < a.topLeft().y || b.topLeft().y > a.bottomLeft().y; + return !(x || y); +} } // ZXing diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 60faebe943..2d959d4ec5 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -55,4 +55,21 @@ int Result::orientation() const return std::lround(_position.orientation() * 180 / std_numbers_pi_v); } +bool Result::operator==(const Result& o) const +{ + if (format() != o.format() || text() != o.text()) + return false; + + if (BarcodeFormats(BarcodeFormat::TwoDCodes).testFlag(format())) + return IsIntersecting(position(), o.position()); + + // if one line is less than half the length of the other away from the + // latter, we consider it to belong to the same symbol + auto dTop = maxAbsComponent(o.position().topLeft() - position().topLeft()); + auto dBot = maxAbsComponent(o.position().bottomLeft() - position().topLeft()); + auto length = maxAbsComponent(position().topLeft() - position().bottomRight()); + + return std::min(dTop, dBot) < length / 2; +} + } // ZXing diff --git a/core/src/Result.h b/core/src/Result.h index 789af3d956..20116a6884 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -139,9 +139,7 @@ class Result ++_lineCount; } - bool operator==(const Result& o) const { - return text() == o.text() && format() == o.format(); - } + bool operator==(const Result& o) const; private: DecodeStatus _status = DecodeStatus::NoError; diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index db7ec26593..851427e0d4 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -176,29 +176,23 @@ static Results DoDecode(const std::vector>& readers, // check if we know this code already for (auto& other : res) { if (other == result) { + // merge the position information auto dTop = maxAbsComponent(other.position().topLeft() - result.position().topLeft()); auto dBot = maxAbsComponent(other.position().bottomLeft() - result.position().topLeft()); - auto length = maxAbsComponent(other.position().topLeft() - other.position().bottomRight()); - // if the new line is less than half the length of the existing result away from the - // latter, we consider it to belong to the same symbol - if (std::min(dTop, dBot) < length / 2) { - // if so, merge the position information - auto points = other.position(); - if (dTop < dBot || - (dTop == dBot && rotate ^ (sumAbsComponent(points[0]) > - sumAbsComponent(result.position()[0])))) { - points[0] = result.position()[0]; - points[1] = result.position()[1]; - } else { - points[2] = result.position()[2]; - points[3] = result.position()[3]; - } - other.setPosition(points); - other.incrementLineCount(); - // clear the result, so we don't insert it again below - result = Result(DecodeStatus::NotFound); - break; + auto points = other.position(); + if (dTop < dBot || (dTop == dBot && rotate ^ (sumAbsComponent(points[0]) > + sumAbsComponent(result.position()[0])))) { + points[0] = result.position()[0]; + points[1] = result.position()[1]; + } else { + points[2] = result.position()[2]; + points[3] = result.position()[3]; } + other.setPosition(points); + other.incrementLineCount(); + // clear the result, so we don't insert it again below + result = Result(DecodeStatus::NotFound); + break; } } From f17785ee66b981955c43bd5e45ab6e6a69ea62f7 Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 16 Apr 2022 15:46:27 +0200 Subject: [PATCH 137/185] Implement MultiResolution detection option This adds 2 new DecodingHints properties: a) `multiResolutionThreshold` Every input image with a higher resolution as the given threshold will be additionally scanned in downsampled resolutions. This helps with noise in very high res images and specifically in 2D codes where the module size is >8 pixels. See #282 for an example. This feature is currently only enabled in the `ReadBarcodes` function. This is currently an experimental feature to gather some feedback. b) `maxNumberOfSymbols` Specify the maximum number of results to produce. Setting this to 1 makes `ReadBarcodes` conceptually behave like `ReadBardcode` (and will likely do exactly that in the future). --- core/src/DecodeHints.h | 10 +++- core/src/MultiFormatReader.cpp | 9 ++-- core/src/MultiFormatReader.h | 2 +- core/src/Quadrilateral.h | 6 +++ core/src/ReadBarcode.cpp | 97 ++++++++++++++++++++++++++++------ 5 files changed, 104 insertions(+), 20 deletions(-) diff --git a/core/src/DecodeHints.h b/core/src/DecodeHints.h index a19f56e17b..dffbee6f35 100644 --- a/core/src/DecodeHints.h +++ b/core/src/DecodeHints.h @@ -62,7 +62,9 @@ class DecodeHints std::string _characterSet; std::vector _allowedLengths; BarcodeFormats _formats = BarcodeFormat::None; + uint16_t _multiResolutionThreshold = 0xffff; uint8_t _minLineCount = 2; + uint8_t _maxNumberOfSymbols = 0xff; public: // bitfields don't get default initialized to 0. @@ -92,12 +94,18 @@ class DecodeHints /// Binarizer to use internally when using the ReadBarcode function ZX_PROPERTY(Binarizer, binarizer, setBinarizer) - /// Set to true if the input contains nothing but a perfectly aligned barcode (generated image) + /// Set to true if the input contains nothing but a single perfectly aligned barcode (generated image) ZX_PROPERTY(bool, isPure, setIsPure) + /// Image size (width or height) threshold at which to start multi-resolution scanning + ZX_PROPERTY(uint16_t, multiResolutionThreshold, setMultiResolutionThreshold) + /// The number of scan lines in a 1D barcode that have to be equal to accept the result, default is 2 ZX_PROPERTY(uint8_t, minLineCount, setMinLineCount) + /// The maximum number of symbols (barcodes) to detect / look for in the image with ReadBarcodes + ZX_PROPERTY(uint8_t, maxNumberOfSymbols, setMaxNumberOfSymbols) + /// Specifies what character encoding to use when decoding, where applicable. ZX_PROPERTY(std::string, characterSet, setCharacterSet) diff --git a/core/src/MultiFormatReader.cpp b/core/src/MultiFormatReader.cpp index ceacf1e315..7f01e33ee7 100644 --- a/core/src/MultiFormatReader.cpp +++ b/core/src/MultiFormatReader.cpp @@ -74,13 +74,16 @@ MultiFormatReader::read(const BinaryBitmap& image) const return Result(DecodeStatus::NotFound); } -Results MultiFormatReader::readMultiple(const BinaryBitmap& image) const +Results MultiFormatReader::readMultiple(const BinaryBitmap& image, int maxSymbols) const { std::vector res; for (const auto& reader : _readers) { - auto r = reader->decode(image, 0); - res.insert(res.end(), r.begin(), r.end()); + auto r = reader->decode(image, maxSymbols); + maxSymbols -= r.size(); + res.insert(res.end(), std::move_iterator(r.begin()), std::move_iterator(r.end())); + if (maxSymbols <= 0) + break; } // sort results based on their position on the image diff --git a/core/src/MultiFormatReader.h b/core/src/MultiFormatReader.h index 4532766666..defafc4b1f 100644 --- a/core/src/MultiFormatReader.h +++ b/core/src/MultiFormatReader.h @@ -45,7 +45,7 @@ class MultiFormatReader Result read(const BinaryBitmap& image) const; // WARNING: this API is experimental and may change/disappear - Results readMultiple(const BinaryBitmap& image) const; + Results readMultiple(const BinaryBitmap& image, int maxSymbols = 0xFF) const; private: std::vector> _readers; diff --git a/core/src/Quadrilateral.h b/core/src/Quadrilateral.h index 90f5aaae7f..e95a74e172 100644 --- a/core/src/Quadrilateral.h +++ b/core/src/Quadrilateral.h @@ -102,6 +102,12 @@ bool IsConvex(const Quadrilateral& poly) return M / m < 4.0; } +template +Quadrilateral Scale(const Quadrilateral& q, int factor) +{ + return {factor * q[0], factor * q[1], factor * q[2], factor * q[3]}; +} + template bool IsIntersecting(const Quadrilateral& a, const Quadrilateral& b) { diff --git a/core/src/ReadBarcode.cpp b/core/src/ReadBarcode.cpp index 7084ac06c4..be885b5053 100644 --- a/core/src/ReadBarcode.cpp +++ b/core/src/ReadBarcode.cpp @@ -54,7 +54,47 @@ static LumImage ExtractLum(const ImageView& iv, P projection) return res; } -ImageView SetupLumImageView(const ImageView& iv, LumImage& lum, const DecodeHints& hints) +class LumImagePyramid +{ + static constexpr int N = 3; + std::vector buffers; + + void addLayer() + { + auto siv = layers.back(); + buffers.emplace_back(siv.width() / N, siv.height() / N); + layers.push_back(buffers.back()); + auto& div = buffers.back(); + auto* d = div.data(); + + for (int dy = 0; dy < div.height(); ++dy) + for (int dx = 0; dx < div.width(); ++dx) { + int sum = (N * N) / 2; + for (int ty = 0; ty < N; ++ty) + for (int tx = 0; tx < N; ++tx) + sum += *siv.data(dx * N + tx, dy * N + ty); + *d++ = sum / (N * N); + } + } + +public: + std::vector layers; + + LumImagePyramid(const ImageView& iv, int threshold) + { + layers.push_back(iv); + while (threshold > 0 && std::max(layers.back().width(), layers.back().height()) > threshold) + addLayer(); +#if 0 + // Reversing the layers means we'd start with the smallest. that can make sense if we are only looking for a + // single symbol. If we start with the higher resolution, we get better (high res) position information. + // TODO: see if masking out higher res layers based on found symbols in lower res helps overall performance. + std::reverse(layers.begin(), layers.end()); +#endif + } +}; + +ImageView SetupLumImageView(ImageView iv, LumImage& lum, const DecodeHints& hints) { if (iv.format() == ImageFormat::None) throw std::invalid_argument("Invalid image format"); @@ -73,32 +113,59 @@ ImageView SetupLumImageView(const ImageView& iv, LumImage& lum, const DecodeHint return iv; } +std::unique_ptr CreateBitmap(ZXing::Binarizer binarizer, const ImageView& iv) +{ + switch (binarizer) { + case Binarizer::BoolCast: return std::make_unique(iv, 0); + case Binarizer::FixedThreshold: return std::make_unique(iv, 127); + case Binarizer::GlobalHistogram: return std::make_unique(iv); + case Binarizer::LocalAverage: return std::make_unique(iv); + } + return {}; // silence gcc warning +} + Result ReadBarcode(const ImageView& _iv, const DecodeHints& hints) { +#if 0 + auto ress = ReadBarcodes(_iv, DecodeHints(hints).setMaxNumberOfSymbols(1)); + return ress.empty() ? Result(DecodeStatus::NotFound) : ress.front(); +#else LumImage lum; ImageView iv = SetupLumImageView(_iv, lum, hints); - switch (hints.binarizer()) { - case Binarizer::BoolCast: return MultiFormatReader(hints).read(ThresholdBinarizer(iv, 0)); - case Binarizer::FixedThreshold: return MultiFormatReader(hints).read(ThresholdBinarizer(iv, 127)); - case Binarizer::GlobalHistogram: return MultiFormatReader(hints).read(GlobalHistogramBinarizer(iv)); - case Binarizer::LocalAverage: return MultiFormatReader(hints).read(HybridBinarizer(iv)); - } - return Result(DecodeStatus::FormatError); + return MultiFormatReader(hints).read(*CreateBitmap(hints.binarizer(), iv)); +#endif } Results ReadBarcodes(const ImageView& _iv, const DecodeHints& hints) { LumImage lum; ImageView iv = SetupLumImageView(_iv, lum, hints); - - switch (hints.binarizer()) { - case Binarizer::BoolCast: return MultiFormatReader(hints).readMultiple(ThresholdBinarizer(iv, 0)); - case Binarizer::FixedThreshold: return MultiFormatReader(hints).readMultiple(ThresholdBinarizer(iv, 127)); - case Binarizer::GlobalHistogram: return MultiFormatReader(hints).readMultiple(GlobalHistogramBinarizer(iv)); - case Binarizer::LocalAverage: return MultiFormatReader(hints).readMultiple(HybridBinarizer(iv)); + MultiFormatReader reader(hints); + + if (hints.isPure()) + return {reader.read(*CreateBitmap(hints.binarizer(), iv))}; + + LumImagePyramid pyramid(iv, hints.multiResolutionThreshold()); + + Results results; + int maxSymbols = hints.maxNumberOfSymbols(); + for (auto&& iv : pyramid.layers) { + auto bitmap = CreateBitmap(hints.binarizer(), iv); + auto rs = reader.readMultiple(*bitmap, maxSymbols); + for (auto& r : rs) { + if (iv.width() != _iv.width()) + r.setPosition(Scale(r.position(), _iv.width() / iv.width())); + if (!Contains(results, r)) { + results.push_back(std::move(r)); + --maxSymbols; + } + } + if (maxSymbols <= 0) + break; } - return {}; + + return results; } } // ZXing From c54920165db25f6e25889e2958189efed591c728 Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 16 Apr 2022 15:47:24 +0200 Subject: [PATCH 138/185] cleanup: remove stale IsIntercting function that was overlooked before --- core/src/oned/ODReader.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index 851427e0d4..0506d54e1a 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -66,13 +66,6 @@ Reader::Reader(const DecodeHints& hints) : Reader::~Reader() = default; -static bool IsIntersecting(QuadrilateralI a, QuadrilateralI b) -{ - bool x = b.topRight().x < a.topLeft().x || b.topLeft().x > a.topRight().x; - bool y = b.bottomLeft().y < a.topLeft().y || b.topLeft().y > a.bottomLeft().y; - return !(x || y); -} - /** * We're going to examine rows from the middle outward, searching alternately above and below the * middle, and farther out each time. rowStep is the number of rows between each successive From f97c382af2165362667131febfe597b20c46d4b0 Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 16 Apr 2022 20:53:00 +0200 Subject: [PATCH 139/185] example: enable the use of the new multi resolution scanning --- example/ZXingReader.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 0db343bbdc..1da3a84ba7 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -41,6 +41,7 @@ static void PrintUsage(const char* exePath) << " -fast Skip some lines/pixels during detection (faster)\n" << " -norotate Don't try rotated image during detection (faster)\n" << " -format Only detect given format(s) (faster)\n" + << " -multires Image size threshold at which to start multi-resolution scanning, 0 disables it\n" << " -ispure Assume the image contains only a 'pure'/perfect code (faster)\n" << " -1 Print only file name, text and status on one line per file/barcode\n" << " -escape Escape non-graphical characters in angle brackets (ignored for -1 option, which always escapes)\n" @@ -73,6 +74,15 @@ static bool ParseOptions(int argc, char* argv[], DecodeHints& hints, bool& oneLi std::cerr << e.what() << "\n"; return false; } + } else if (strcmp(argv[i], "-multires") == 0) { + if (++i == argc) + return false; + try { + hints.setMultiResolutionThreshold(std::stoi(argv[i])); + } catch (const std::exception& e) { + std::cerr << e.what() << "\n"; + return false; + } } else if (strcmp(argv[i], "-1") == 0) { oneLine = true; } else if (strcmp(argv[i], "-escape") == 0) { @@ -121,6 +131,8 @@ int main(int argc, char* argv[]) bool angleEscape = false; int ret = 0; + hints.setMultiResolutionThreshold(500); // enable the feature by default + if (!ParseOptions(argc, argv, hints, oneLine, angleEscape, filePaths, outPath)) { PrintUsage(argv[0]); return -1; From c74a8553606fbb6f7673f910039d45f557df6fd0 Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 16 Apr 2022 22:39:35 +0200 Subject: [PATCH 140/185] ITFReader: code cosmetic --- core/src/oned/ODITFReader.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/core/src/oned/ODITFReader.cpp b/core/src/oned/ODITFReader.cpp index 6ae6c47c2e..26051f2205 100644 --- a/core/src/oned/ODITFReader.cpp +++ b/core/src/oned/ODITFReader.cpp @@ -27,15 +27,14 @@ namespace ZXing::OneD { /** Valid ITF lengths. Anything longer than the largest value is also allowed. */ -static const std::array DEFAULT_ALLOWED_LENGTHS = { 6, 8, 10, 12, 14 }; +constexpr auto DEFAULT_ALLOWED_LENGTHS = { 6, 8, 10, 12, 14 }; ITFReader::ITFReader(const DecodeHints& hints) : _allowedLengths(hints.allowedLengths()), _usingCheckDigit(hints.assumeITFCheckDigit()) { - if (_allowedLengths.empty()) { + if (_allowedLengths.empty()) _allowedLengths.assign(DEFAULT_ALLOWED_LENGTHS.begin(), DEFAULT_ALLOWED_LENGTHS.end()); - } } constexpr auto START_PATTERN_ = FixedPattern<4, 4>{1, 1, 1, 1}; @@ -88,13 +87,13 @@ Result ITFReader::decodePattern(int rowNumber, PatternView& next, std::unique_pt if (!IsRightGuard(next, STOP_PATTERN_1, minQuietZone) && !IsRightGuard(next, STOP_PATTERN_2, minQuietZone)) return Result(DecodeStatus::NotFound); + if (_usingCheckDigit && !GTIN::IsCheckDigitValid(txt)) + return Result(DecodeStatus::ChecksumError); + // Symbology identifier ISO/IEC 16390:2007 Annex C Table C.1 // See also GS1 General Specifications 5.1.3 Figure 5.1.3-2 std::string symbologyIdentifier("]I0"); // No check character validation - if (_usingCheckDigit && !GTIN::IsCheckDigitValid(txt)) - return Result(DecodeStatus::ChecksumError); - if (_usingCheckDigit || (txt.size() == 14 && GTIN::IsCheckDigitValid(txt))) // If no hint test if valid ITF-14 symbologyIdentifier = "]I1"; // Modulo 10 symbol check character validated and transmitted From fb495c8641276d9e0914d1be7a2dacb0cf0d1ffe Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 16 Apr 2022 22:46:15 +0200 Subject: [PATCH 141/185] DecodingHints: rename `assume...CheckDigit` to `validate...CheckSum` Fixes #298. See there for details. --- core/src/DecodeHints.h | 36 +++++++++++++-------------- core/src/oned/ODCode39Reader.cpp | 8 +++--- core/src/oned/ODCode39Reader.h | 2 +- core/src/oned/ODITFReader.cpp | 6 ++--- core/src/oned/ODITFReader.h | 2 +- test/unit/oned/ODCode39ReaderTest.cpp | 4 +-- 6 files changed, 28 insertions(+), 30 deletions(-) diff --git a/core/src/DecodeHints.h b/core/src/DecodeHints.h index dffbee6f35..b18bcb576a 100644 --- a/core/src/DecodeHints.h +++ b/core/src/DecodeHints.h @@ -2,6 +2,7 @@ /* * Copyright 2016 Nu-book Inc. * Copyright 2016 ZXing authors +* Copyright 2020 Axel Waggershauser * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,9 +53,8 @@ class DecodeHints bool _tryRotate : 1; bool _isPure : 1; bool _tryCode39ExtendedMode : 1; - bool _assumeCode39CheckDigit : 1; - bool _assumeITFCheckDigit : 1; - bool _assumeGS1 : 1; + bool _validateCode39CheckSum : 1; + bool _validateITFCheckSum : 1; bool _returnCodabarStartEnd : 1; Binarizer _binarizer : 2; EanAddOnSymbol _eanAddOnSymbol : 2; @@ -69,8 +69,8 @@ class DecodeHints public: // bitfields don't get default initialized to 0. DecodeHints() - : _tryHarder(1), _tryRotate(1), _isPure(0), _tryCode39ExtendedMode(0), _assumeCode39CheckDigit(0), - _assumeITFCheckDigit(0), _assumeGS1(0), _returnCodabarStartEnd(0), _binarizer(Binarizer::LocalAverage), + : _tryHarder(1), _tryRotate(1), _isPure(0), _tryCode39ExtendedMode(0), _validateCode39CheckSum(0), + _validateITFCheckSum(0), _returnCodabarStartEnd(0), _binarizer(Binarizer::LocalAverage), _eanAddOnSymbol(EanAddOnSymbol::Ignore) {} @@ -78,10 +78,6 @@ class DecodeHints TYPE GETTER() const noexcept { return _##GETTER; } \ DecodeHints& SETTER(TYPE v) { return _##GETTER = std::move(v), *this; } -#define ZX_PROPERTY_DEPRECATED(TYPE, GETTER, SETTER) \ - [[deprecated]] TYPE GETTER() const noexcept { return _##GETTER; } \ - [[deprecated]] DecodeHints& SETTER(TYPE v) { return _##GETTER = std::move(v), *this; } - /// Specify a set of BarcodeFormats that should be searched for, the default is all supported formats. ZX_PROPERTY(BarcodeFormats, formats, setFormats) @@ -115,17 +111,11 @@ class DecodeHints /// If true, the Code-39 reader will try to read extended mode. ZX_PROPERTY(bool, tryCode39ExtendedMode, setTryCode39ExtendedMode) - /// Assume Code-39 codes employ a check digit. - ZX_PROPERTY(bool, assumeCode39CheckDigit, setAssumeCode39CheckDigit) - - /// Assume ITF codes employ a GS1 check digit. - ZX_PROPERTY(bool, assumeITFCheckDigit, setAssumeITFCheckDigit) + /// Assume Code-39 codes employ a check digit and validate it. + ZX_PROPERTY(bool, validateCode39CheckSum, setValidateCode39CheckSum) - /** - * Assume the barcode is being processed as a GS1 barcode, and modify behavior as needed. - * NOTE: used to affect FNC1 handling for Code 128 (aka GS1-128) but behavior now based on position of FNC1. - */ - ZX_PROPERTY_DEPRECATED(bool, assumeGS1, setAssumeGS1) + /// Assume ITF codes employ a GS1 check digit and validate it. + ZX_PROPERTY(bool, validateITFCheckSum, setValidateITFCheckSum) /** * If true, return the start and end digits in a Codabar barcode instead of stripping them. They @@ -140,6 +130,14 @@ class DecodeHints #undef ZX_PROPERTY #undef ZX_PROPERTY_DEPRECATED + /// NOTE: used to affect FNC1 handling for Code 128 (aka GS1-128) but behavior now based on position of FNC1. + [[deprecated]] bool assumeGS1() const noexcept { return true; } + [[deprecated]] DecodeHints& setAssumeGS1(bool v [[maybe_unused]]) { return *this; } + + /// NOTE: use validateCode39CheckSum + [[deprecated]] bool assumeCode39CheckDigit() const noexcept { return validateCode39CheckSum(); } + [[deprecated]] DecodeHints& setAssumeCode39CheckDigit(bool v) { return setValidateCode39CheckSum(v); } + bool hasFormat(BarcodeFormats f) const noexcept { return _formats.testFlags(f); } bool hasNoFormat() const noexcept { return _formats.empty(); } }; diff --git a/core/src/oned/ODCode39Reader.cpp b/core/src/oned/ODCode39Reader.cpp index dd25f1ae4c..cfe376326c 100644 --- a/core/src/oned/ODCode39Reader.cpp +++ b/core/src/oned/ODCode39Reader.cpp @@ -88,14 +88,14 @@ DecodeExtendedCode39AndCode93(std::string& encoded, const char ctrl[4]) Code39Reader::Code39Reader(const DecodeHints& hints) : _extendedMode(hints.tryCode39ExtendedMode()), - _usingCheckDigit(hints.assumeCode39CheckDigit()) + _validateCheckSum(hints.validateCode39CheckSum()) { } Result Code39Reader::decodePattern(int rowNumber, PatternView& next, std::unique_ptr&) const { // minimal number of characters that must be present (including start, stop and checksum characters) - int minCharCount = _usingCheckDigit ? 4 : 3; + int minCharCount = _validateCheckSum ? 4 : 3; auto isStartOrStopSymbol = [](char c) { return c == '*'; }; // provide the indices with the narrow bars/spaces which have to be equally wide @@ -132,7 +132,7 @@ Result Code39Reader::decodePattern(int rowNumber, PatternView& next, std::unique if (Size(txt) < minCharCount - 2 || !next.hasQuietZoneAfter(QUIET_ZONE_SCALE)) return Result(DecodeStatus::NotFound); - if (_usingCheckDigit) { + if (_validateCheckSum) { auto checkDigit = txt.back(); txt.pop_back(); int checksum = TransformReduce(txt, 0, [](char c) { return IndexOf(ALPHABET, c); }); @@ -145,7 +145,7 @@ Result Code39Reader::decodePattern(int rowNumber, PatternView& next, std::unique // Symbology identifier modifiers ISO/IEC 16388:2007 Annex C Table C.1 static const int symbologyModifiers[4] = { 0, 3 /*checksum*/, 4 /*extended*/, 7 /*checksum,extended*/ }; - int symbologyIdModifier = symbologyModifiers[(int)_extendedMode * 2 + (int)_usingCheckDigit]; + int symbologyIdModifier = symbologyModifiers[(int)_extendedMode * 2 + (int)_validateCheckSum]; std::string symbologyIdentifier("]A" + std::to_string(symbologyIdModifier)); diff --git a/core/src/oned/ODCode39Reader.h b/core/src/oned/ODCode39Reader.h index 0573f756fb..5c5daef9f8 100644 --- a/core/src/oned/ODCode39Reader.h +++ b/core/src/oned/ODCode39Reader.h @@ -49,7 +49,7 @@ class Code39Reader : public RowReader private: bool _extendedMode; - bool _usingCheckDigit; + bool _validateCheckSum; }; } // OneD diff --git a/core/src/oned/ODITFReader.cpp b/core/src/oned/ODITFReader.cpp index 26051f2205..7b0b30cac1 100644 --- a/core/src/oned/ODITFReader.cpp +++ b/core/src/oned/ODITFReader.cpp @@ -31,7 +31,7 @@ constexpr auto DEFAULT_ALLOWED_LENGTHS = { 6, 8, 10, 12, 14 }; ITFReader::ITFReader(const DecodeHints& hints) : _allowedLengths(hints.allowedLengths()), - _usingCheckDigit(hints.assumeITFCheckDigit()) + _validateCheckSum(hints.validateITFCheckSum()) { if (_allowedLengths.empty()) _allowedLengths.assign(DEFAULT_ALLOWED_LENGTHS.begin(), DEFAULT_ALLOWED_LENGTHS.end()); @@ -87,14 +87,14 @@ Result ITFReader::decodePattern(int rowNumber, PatternView& next, std::unique_pt if (!IsRightGuard(next, STOP_PATTERN_1, minQuietZone) && !IsRightGuard(next, STOP_PATTERN_2, minQuietZone)) return Result(DecodeStatus::NotFound); - if (_usingCheckDigit && !GTIN::IsCheckDigitValid(txt)) + if (_validateCheckSum && !GTIN::IsCheckDigitValid(txt)) return Result(DecodeStatus::ChecksumError); // Symbology identifier ISO/IEC 16390:2007 Annex C Table C.1 // See also GS1 General Specifications 5.1.3 Figure 5.1.3-2 std::string symbologyIdentifier("]I0"); // No check character validation - if (_usingCheckDigit || (txt.size() == 14 && GTIN::IsCheckDigitValid(txt))) // If no hint test if valid ITF-14 + if (_validateCheckSum || (txt.size() == 14 && GTIN::IsCheckDigitValid(txt))) // If no hint test if valid ITF-14 symbologyIdentifier = "]I1"; // Modulo 10 symbol check character validated and transmitted int xStop = next.pixelsTillEnd(); diff --git a/core/src/oned/ODITFReader.h b/core/src/oned/ODITFReader.h index 960067775c..322ee17d20 100644 --- a/core/src/oned/ODITFReader.h +++ b/core/src/oned/ODITFReader.h @@ -49,7 +49,7 @@ class ITFReader : public RowReader private: std::vector _allowedLengths; - bool _usingCheckDigit; + bool _validateCheckSum; }; } // OneD diff --git a/test/unit/oned/ODCode39ReaderTest.cpp b/test/unit/oned/ODCode39ReaderTest.cpp index cc265c8d45..c92d88202f 100644 --- a/test/unit/oned/ODCode39ReaderTest.cpp +++ b/test/unit/oned/ODCode39ReaderTest.cpp @@ -49,7 +49,7 @@ TEST(ODCode39ReaderTest, SymbologyIdentifier) { // "A" with checksum PatternRow row({ 2, 1, 1, 1, 1, 2, 1, 1, 2, 0, 2, 1, 1, 1, 1, 2, 1, 1, 2 }); - auto result = parse(row, DecodeHints().setAssumeCode39CheckDigit(true)); + auto result = parse(row, DecodeHints().setValidateCode39CheckSum(true)); EXPECT_EQ(result.symbologyIdentifier(), "]A3"); EXPECT_EQ(result.text(), L"A"); @@ -71,7 +71,7 @@ TEST(ODCode39ReaderTest, SymbologyIdentifier) { // Extended "a" with checksum PatternRow row({ 1, 2, 1, 1, 1, 2, 1, 2, 1, 0, 2, 1, 1, 1, 1, 2, 1, 1, 2, 0, 2, 1, 1, 2, 1, 1, 2, 1, 1 }); - auto result = parse(row, DecodeHints().setTryCode39ExtendedMode(true).setAssumeCode39CheckDigit(true)); + auto result = parse(row, DecodeHints().setTryCode39ExtendedMode(true).setValidateCode39CheckSum(true)); EXPECT_EQ(result.symbologyIdentifier(), "]A7"); EXPECT_EQ(result.text(), L"a"); From 3836f4c998bf059a643cb2e59ad35a96f02bd40b Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 16 Apr 2022 23:41:58 +0200 Subject: [PATCH 142/185] Result: add `isMirrored` property This lets the client know whether or not the symbol was mirrored/flipped. This is currently only implemented for QRCode and DataMatrix symbology. The now unnecessary QRDecoderMetadata class has been removed. --- core/src/DecoderResult.h | 2 + core/src/Result.cpp | 3 +- core/src/Result.h | 8 ++++ core/src/datamatrix/DMDecoder.cpp | 5 ++- core/src/qrcode/QRDecoder.cpp | 3 +- core/src/qrcode/QRDecoderMetadata.h | 45 ------------------- core/src/qrcode/QRReader.cpp | 3 -- example/ZXingReader.cpp | 1 + test/blackbox/BlackboxTestRunner.cpp | 2 + .../datamatrix-1/0123456789.result.txt | 1 + .../abcd-36x12-mirrored.result.txt | 1 + 11 files changed, 21 insertions(+), 53 deletions(-) delete mode 100644 core/src/qrcode/QRDecoderMetadata.h create mode 100644 test/samples/datamatrix-1/abcd-36x12-mirrored.result.txt diff --git a/core/src/DecoderResult.h b/core/src/DecoderResult.h index 9d736725bd..30abc8c772 100644 --- a/core/src/DecoderResult.h +++ b/core/src/DecoderResult.h @@ -47,6 +47,7 @@ class DecoderResult int _erasures = -1; std::string _symbologyIdentifier; StructuredAppendInfo _structuredAppend; + bool _isMirrored = false; bool _readerInit = false; std::shared_ptr _extra; @@ -92,6 +93,7 @@ class DecoderResult ZX_PROPERTY(int, erasures, setErasures) ZX_PROPERTY(std::string, symbologyIdentifier, setSymbologyIdentifier) ZX_PROPERTY(StructuredAppendInfo, structuredAppend, setStructuredAppend) + ZX_PROPERTY(bool, isMirrored, setIsMirrored) ZX_PROPERTY(bool, readerInit, setReaderInit) ZX_PROPERTY(std::shared_ptr, extra, setExtra) diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 2d959d4ec5..b5eaa7ab7d 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -43,7 +43,8 @@ Result::Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat : _status(decodeResult.errorCode()), _format(format), _text(std::move(decodeResult).text()), _position(std::move(position)), _rawBytes(std::move(decodeResult).rawBytes()), _numBits(decodeResult.numBits()), _ecLevel(decodeResult.ecLevel()), _symbologyIdentifier(decodeResult.symbologyIdentifier()), - _sai(decodeResult.structuredAppend()), _readerInit(decodeResult.readerInit()) + _sai(decodeResult.structuredAppend()), _isMirrored(decodeResult.isMirrored()), + _readerInit(decodeResult.readerInit()) { // TODO: add type opaque and code specific 'extra data'? (see DecoderResult::extra()) diff --git a/core/src/Result.h b/core/src/Result.h index 20116a6884..4cb8b48369 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -77,6 +77,13 @@ class Result int orientation() const; //< orientation of barcode in degree, see also Position::orientation() + /** + * @brief isMirrored is the symbol mirrored (currently only supported by QRCode and DataMatrix) + */ + bool isMirrored() const { + return _isMirrored; + } + const ByteArray& rawBytes() const { return _rawBytes; } @@ -151,6 +158,7 @@ class Result std::wstring _ecLevel; std::string _symbologyIdentifier; StructuredAppendInfo _sai; + bool _isMirrored = false; bool _readerInit = false; int _lineCount = 0; }; diff --git a/core/src/datamatrix/DMDecoder.cpp b/core/src/datamatrix/DMDecoder.cpp index 3636938cfd..feb469cb38 100644 --- a/core/src/datamatrix/DMDecoder.cpp +++ b/core/src/datamatrix/DMDecoder.cpp @@ -540,11 +540,12 @@ DecoderResult Decode(const BitMatrix& bits, const std::string& characterSet) return res; //TODO: - // * report mirrored state (see also QRReader) // * unify bit mirroring helper code with QRReader? // * rectangular symbols with the a size of 8 x Y are not supported a.t.m. - if (auto mirroredRes = DoDecode(FlippedL(bits), characterSet); mirroredRes.isValid()) + if (auto mirroredRes = DoDecode(FlippedL(bits), characterSet); mirroredRes.isValid()) { + mirroredRes.setIsMirrored(true); return mirroredRes; + } return res; } diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index 342681dd00..cc0899d7c7 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -27,7 +27,6 @@ #include "QRBitMatrixParser.h" #include "QRCodecMode.h" #include "QRDataBlock.h" -#include "QRDecoderMetadata.h" #include "QRFormatInformation.h" #include "ReedSolomonDecoder.h" #include "StructuredAppend.h" @@ -487,7 +486,7 @@ DecoderResult Decode(const BitMatrix& bits, const std::string& hintedCharset) return res; if (auto resMirrored = DoDecode(bits, *version, hintedCharset, true); resMirrored.isValid()) { - resMirrored.setExtra(std::make_shared(true)); + resMirrored.setIsMirrored(true); return resMirrored; } diff --git a/core/src/qrcode/QRDecoderMetadata.h b/core/src/qrcode/QRDecoderMetadata.h deleted file mode 100644 index 6a23e8002f..0000000000 --- a/core/src/qrcode/QRDecoderMetadata.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once -/* -* Copyright 2016 Nu-book Inc. -* Copyright 2016 ZXing authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#include "CustomData.h" - -namespace ZXing { -namespace QRCode { - -/** -* Meta-data container for QR Code decoding. Instances of this class may be used to convey information back to the -* decoding caller. Callers are expected to process this. -*/ - -class DecoderMetadata : public CustomData -{ - bool _mirrored; - -public: - explicit DecoderMetadata(bool mirrored) : _mirrored(mirrored) {} - - /** - * @return true if the QR Code was mirrored. - */ - bool isMirrored() const { - return _mirrored; - } -}; - -} // QRCode -} // ZXing diff --git a/core/src/qrcode/QRReader.cpp b/core/src/qrcode/QRReader.cpp index 5942898075..effd0e44e1 100644 --- a/core/src/qrcode/QRReader.cpp +++ b/core/src/qrcode/QRReader.cpp @@ -57,9 +57,6 @@ Reader::decode(const BinaryBitmap& image) const auto decoderResult = Decode(detectorResult.bits(), _charset); auto position = detectorResult.position(); - // TODO: report the information that the symbol was mirrored back to the caller - //bool isMirrored = decoderResult.extra() && static_cast(decoderResult.extra().get())->isMirrored(); - return Result(std::move(decoderResult), std::move(position), BarcodeFormat::QRCode); } diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 1da3a84ba7..8b9355254e 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -191,6 +191,7 @@ int main(int argc, char* argv[]) << "Identifier: " << result.symbologyIdentifier() << "\n" << "Position: " << result.position() << "\n" << "Rotation: " << result.orientation() << " deg\n" + << "IsMirrored: " << (result.isMirrored() ? "true" : "false") << "\n" << "Error: " << ToString(result.status()) << "\n"; auto printOptional = [](const char* key, const std::string& v) { diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 146309d123..1d166babb3 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -94,6 +94,8 @@ static std::string getResultValue(const Result& result, const std::string& key) return result.isLastInSequence() ? "true" : "false"; if (key == "isPartOfSequence") return result.isPartOfSequence() ? "true" : "false"; + if (key == "isMirrored") + return result.isMirrored() ? "true" : "false"; if (key == "readerInit") return result.readerInit() ? "true" : "false"; diff --git a/test/samples/datamatrix-1/0123456789.result.txt b/test/samples/datamatrix-1/0123456789.result.txt index bbec5f6fa7..771cae78da 100644 --- a/test/samples/datamatrix-1/0123456789.result.txt +++ b/test/samples/datamatrix-1/0123456789.result.txt @@ -1 +1,2 @@ symbologyIdentifier=]d1 +isMirrored=false diff --git a/test/samples/datamatrix-1/abcd-36x12-mirrored.result.txt b/test/samples/datamatrix-1/abcd-36x12-mirrored.result.txt new file mode 100644 index 0000000000..f32b8d9e10 --- /dev/null +++ b/test/samples/datamatrix-1/abcd-36x12-mirrored.result.txt @@ -0,0 +1 @@ +isMirrored=true From 7fa9c403507ad22efc4454fa2d93ae8b3a89856b Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 16 Apr 2022 23:43:31 +0200 Subject: [PATCH 143/185] DecodeHints: add WARNING experimental comment to multiResolutionThreshold --- core/src/DecodeHints.h | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/DecodeHints.h b/core/src/DecodeHints.h index b18bcb576a..a67820b80e 100644 --- a/core/src/DecodeHints.h +++ b/core/src/DecodeHints.h @@ -94,6 +94,7 @@ class DecodeHints ZX_PROPERTY(bool, isPure, setIsPure) /// Image size (width or height) threshold at which to start multi-resolution scanning + // WARNING: this API is experimental and may change/disappear ZX_PROPERTY(uint16_t, multiResolutionThreshold, setMultiResolutionThreshold) /// The number of scan lines in a 1D barcode that have to be equal to accept the result, default is 2 From ca9996f871063d2d14797e5c90c6fc317abfb0c6 Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 16 Apr 2022 23:47:21 +0200 Subject: [PATCH 144/185] cmake: fix build regression (remove QRDecoderMetadata.h from CMakeLists) --- core/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index f6f8561349..0d4bff9f0c 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -339,7 +339,6 @@ if (BUILD_READERS) src/qrcode/QRDataMask.h src/qrcode/QRDecoder.h src/qrcode/QRDecoder.cpp - src/qrcode/QRDecoderMetadata.h src/qrcode/QRDetector.h src/qrcode/QRDetector.cpp src/qrcode/QRECB.h From 47cad821c640d08657a9adeb944b4fd6e19ae839 Mon Sep 17 00:00:00 2001 From: axxel Date: Sun, 17 Apr 2022 19:26:51 +0200 Subject: [PATCH 145/185] Result: micro optimization of construction (more movable parameters) --- core/src/Result.cpp | 15 ++++++++------- core/src/Result.h | 8 ++++---- core/src/oned/ODCodabarReader.cpp | 2 +- core/src/oned/ODCode128Reader.cpp | 6 +++--- core/src/oned/ODCode39Reader.cpp | 2 +- core/src/oned/ODCode93Reader.cpp | 2 +- core/src/oned/ODDataBarExpandedReader.cpp | 2 +- core/src/oned/ODDataBarReader.cpp | 2 +- core/src/oned/ODITFReader.cpp | 2 +- core/src/oned/ODMultiUPCEANReader.cpp | 4 ++-- test/blackbox/BlackboxTestRunner.cpp | 3 ++- 11 files changed, 25 insertions(+), 23 deletions(-) diff --git a/core/src/Result.cpp b/core/src/Result.cpp index b5eaa7ab7d..1e5bc4d442 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -25,18 +25,19 @@ namespace ZXing { -Result::Result(std::wstring&& text, Position&& position, BarcodeFormat format, ByteArray&& rawBytes, - std::string symbologyIdentifier, StructuredAppendInfo sai, const bool readerInit, int lineCount) +Result::Result(std::wstring&& text, Position&& position, BarcodeFormat format, std::string&& symbologyIdentifier, + ByteArray&& rawBytes, StructuredAppendInfo&& sai, const bool readerInit, int lineCount) : _format(format), _text(std::move(text)), _position(std::move(position)), _rawBytes(std::move(rawBytes)), - _symbologyIdentifier(symbologyIdentifier), _sai(sai), _readerInit(readerInit), _lineCount(lineCount) + _symbologyIdentifier(std::move(symbologyIdentifier)), _sai(std::move(sai)), _readerInit(readerInit), + _lineCount(lineCount) { _numBits = Size(_rawBytes) * 8; } -Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, ByteArray&& rawBytes, - std::string symbologyIdentifier, const bool readerInit) - : Result(TextDecoder::FromLatin1(text), Line(y, xStart, xStop), format, std::move(rawBytes), symbologyIdentifier, - {}, readerInit) +Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, + std::string&& symbologyIdentifier, ByteArray&& rawBytes, const bool readerInit) + : Result(TextDecoder::FromLatin1(text), Line(y, xStart, xStop), format, std::move(symbologyIdentifier), + std::move(rawBytes), {}, readerInit) {} Result::Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat format) diff --git a/core/src/Result.h b/core/src/Result.h index 4cb8b48369..c44c95455c 100644 --- a/core/src/Result.h +++ b/core/src/Result.h @@ -42,13 +42,13 @@ class Result public: explicit Result(DecodeStatus status) : _status(status) {} - Result(std::wstring&& text, Position&& position, BarcodeFormat format, ByteArray&& rawBytes = {}, - std::string symbologyIdentifier = "", StructuredAppendInfo sai = {}, const bool readerInit = false, + Result(std::wstring&& text, Position&& position, BarcodeFormat format, std::string&& symbologyIdentifier = "", + ByteArray&& rawBytes = {}, StructuredAppendInfo&& sai = {}, const bool readerInit = false, int lineCount = 0); // 1D convenience constructor - Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, ByteArray&& rawBytes = {}, - std::string symbologyIdentifier = "", const bool readerInit = false); + Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, + std::string&& symbologyIdentifier = "", ByteArray&& rawBytes = {}, const bool readerInit = false); Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat format); diff --git a/core/src/oned/ODCodabarReader.cpp b/core/src/oned/ODCodabarReader.cpp index aff361fcbe..354f068511 100644 --- a/core/src/oned/ODCodabarReader.cpp +++ b/core/src/oned/ODCodabarReader.cpp @@ -108,7 +108,7 @@ CodabarReader::decodePattern(int rowNumber, PatternView& next, std::unique_ptrleftPairs.erase(leftPair); diff --git a/core/src/oned/ODITFReader.cpp b/core/src/oned/ODITFReader.cpp index 7b0b30cac1..e314b99368 100644 --- a/core/src/oned/ODITFReader.cpp +++ b/core/src/oned/ODITFReader.cpp @@ -98,7 +98,7 @@ Result ITFReader::decodePattern(int rowNumber, PatternView& next, std::unique_pt symbologyIdentifier = "]I1"; // Modulo 10 symbol check character validated and transmitted int xStop = next.pixelsTillEnd(); - return Result(txt, rowNumber, xStart, xStop, BarcodeFormat::ITF, {}, std::move(symbologyIdentifier)); + return Result(txt, rowNumber, xStart, xStop, BarcodeFormat::ITF, std::move(symbologyIdentifier)); } } // namespace ZXing::OneD diff --git a/core/src/oned/ODMultiUPCEANReader.cpp b/core/src/oned/ODMultiUPCEANReader.cpp index 0d7bb86086..e567a07a84 100644 --- a/core/src/oned/ODMultiUPCEANReader.cpp +++ b/core/src/oned/ODMultiUPCEANReader.cpp @@ -329,8 +329,8 @@ Result MultiUPCEANReader::decodePattern(int rowNumber, PatternView& next, std::u if (_hints.eanAddOnSymbol() == EanAddOnSymbol::Require && !addOnRes.isValid()) return Result(DecodeStatus::NotFound); - return {res.txt, rowNumber, begin.pixelsInFront(), res.end.pixelsTillEnd(), res.format, {}, - std::move(symbologyIdentifier)}; + return { + res.txt, rowNumber, begin.pixelsInFront(), res.end.pixelsTillEnd(), res.format, std::move(symbologyIdentifier)}; } } // namespace ZXing::OneD diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 1d166babb3..278cd7171c 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -282,7 +282,8 @@ static Result readMultiple(const std::vector& imgPaths, std::string_vi const auto& first = allResults.front(); StructuredAppendInfo sai{ first.sequenceIndex(), first.sequenceSize(), first.sequenceId() }; - return Result(std::move(text), {}, first.format(), {}, first.symbologyIdentifier(), sai, first.readerInit()); + return Result(std::move(text), {}, first.format(), std::string(first.symbologyIdentifier()), {}, std::move(sai), + first.readerInit()); } static void doRunStructuredAppendTest( From eddca679158dd30683785a2bd8e36305e37517d6 Mon Sep 17 00:00:00 2001 From: axxel Date: Sun, 17 Apr 2022 19:27:48 +0200 Subject: [PATCH 146/185] ODReader: update TODO comment --- core/src/oned/ODReader.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index 0506d54e1a..c68ca28d89 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -127,11 +127,10 @@ static Results DoDecode(const std::vector>& readers, // While we have the image data in a PatternRow, it's fairly cheap to reverse it in place to // handle decoding upside down barcodes. - // Note: the DataBarExpanded decoder depends on seeing each line from both directions. This - // 'surprising' and inconsistent. It also requires the decoderState to be shared between - // normal and reversed scans, which makes no sense in general because it would mix partial - // detection data from two codes of the same type next to each other. TODO.. - // See also https://github.com/nu-book/zxing-cpp/issues/87 + // TODO: the DataBarExpanded (stacked) decoder depends on seeing each line from both directions. This + // 'surprising' and inconsistent. It also requires the decoderState to be shared between normal and reversed + // scans, which makes no sense in general because it would mix partial detection data from two codes of the same + // type next to each other. See also https://github.com/nu-book/zxing-cpp/issues/87 for (bool upsideDown : {false, true}) { // trying again? if (upsideDown) { From 1e39cbe0505dd3ae25836a123a0498ea90a850d1 Mon Sep 17 00:00:00 2001 From: axxel Date: Sun, 17 Apr 2022 19:40:10 +0200 Subject: [PATCH 147/185] example: use green instead of red to outline the found symbols in -pngout --- example/ZXingReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 8b9355254e..8211565e26 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -112,7 +112,7 @@ void drawLine(const ImageView& image, PointI a, PointI b) PointF dir = bresenhamDirection(PointF(b - a)); for (int i = 0; i < steps; ++i) { auto p = PointI(centered(a + i * dir)); - *((uint32_t*)image.data(p.x, p.y)) = 0xff0000ff; + *((uint32_t*)image.data(p.x, p.y)) = 0xff00ff00; } } From 45c7906ada4ff12e26f0d3dfc4cc29d6310199b8 Mon Sep 17 00:00:00 2001 From: Ralph Corby Date: Tue, 19 Apr 2022 15:27:05 +0100 Subject: [PATCH 148/185] Fix threshold test in VS2019 Debug build. The setup code for the test creates a buffer using vector's reserve. This works for the release build but in debug build VS uses checked iterators which causes an error to be generated. Changed reserve to resize. --- test/unit/ThresholdBinarizerTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/ThresholdBinarizerTest.cpp b/test/unit/ThresholdBinarizerTest.cpp index ce474938cd..0a9e54f189 100644 --- a/test/unit/ThresholdBinarizerTest.cpp +++ b/test/unit/ThresholdBinarizerTest.cpp @@ -41,7 +41,7 @@ static BitMatrix ParseBitMatrix(const std::string& str, const int width) // Helper to convert a BitMatrix into a black/white ImageView static ImageView getImageView(std::vector &buf, const BitMatrix &bits) { - buf.reserve(bits.width() * bits.height()); + buf.resize(bits.width() * bits.height()); for (int r = 0; r < bits.height(); r++) { const int k = r * bits.width(); for (int c = 0; c < bits.width(); c++) { From 6a0c7fc6aaf93aa7bf831d802cef9122ef8a5048 Mon Sep 17 00:00:00 2001 From: Markus Fisch Date: Tue, 19 Apr 2022 16:59:39 +0200 Subject: [PATCH 149/185] android: use row stride for Java impl too There are some really strange row strides on some devices when using the Camera2/CameraX API. Without this, the Java implementation would see a distorted image. --- .../src/main/java/com/example/zxingcppdemo/MainActivity.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/wrappers/android/app/src/main/java/com/example/zxingcppdemo/MainActivity.kt b/wrappers/android/app/src/main/java/com/example/zxingcppdemo/MainActivity.kt index 40768ba4b3..cffb77c455 100644 --- a/wrappers/android/app/src/main/java/com/example/zxingcppdemo/MainActivity.kt +++ b/wrappers/android/app/src/main/java/com/example/zxingcppdemo/MainActivity.kt @@ -118,7 +118,9 @@ class MainActivity : AppCompatActivity() { var resultPoints: List? = null if (binding.java.isChecked) { - val yBuffer = image.planes[0].buffer // Y + val yPlane = image.planes[0] + val yBuffer = yPlane.buffer + val yStride = yPlane.rowStride val data = ByteArray(yBuffer.remaining()) yBuffer.get(data, 0, data.size) image.close() @@ -132,7 +134,7 @@ class MainActivity : AppCompatActivity() { val bitmap = BinaryBitmap( HybridBinarizer( PlanarYUVLuminanceSource( - data, image.width, image.height, + data, yStride, image.height, cropRect.left, cropRect.top, cropRect.width(), cropRect.height(), false ) From 534f853c6bff1692d4482a55c945a48c607deee7 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 19 Apr 2022 21:17:48 +0200 Subject: [PATCH 150/185] example: support all pixel formats in drawLine and switch to RGB from RGBX --- example/ZXingReader.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 8211565e26..84b488de44 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -106,13 +106,16 @@ std::ostream& operator<<(std::ostream& os, const Position& points) return os; } -void drawLine(const ImageView& image, PointI a, PointI b) +void drawLine(const ImageView& iv, PointI a, PointI b) { int steps = maxAbsComponent(b - a); PointF dir = bresenhamDirection(PointF(b - a)); + int R = RedIndex(iv.format()), G = GreenIndex(iv.format()), B = BlueIndex(iv.format()); for (int i = 0; i < steps; ++i) { auto p = PointI(centered(a + i * dir)); - *((uint32_t*)image.data(p.x, p.y)) = 0xff00ff00; + auto* dst = const_cast(iv.data(p.x, p.y)); + dst[R] = dst[B] = 0; + dst[G] = 0xff; } } @@ -148,13 +151,13 @@ int main(int argc, char* argv[]) for (const auto& filePath : filePaths) { int width, height, channels; - std::unique_ptr buffer(stbi_load(filePath.c_str(), &width, &height, &channels, 4), stbi_image_free); + std::unique_ptr buffer(stbi_load(filePath.c_str(), &width, &height, &channels, 3), stbi_image_free); if (buffer == nullptr) { std::cerr << "Failed to read image: " << filePath << "\n"; return -1; } - ImageView image{buffer.get(), width, height, ImageFormat::RGBX}; + ImageView image{buffer.get(), width, height, ImageFormat::RGB}; auto results = ReadBarcodes(image, hints); // if we did not find anything, insert a dummy to produce some output for each file @@ -223,7 +226,7 @@ int main(int argc, char* argv[]) } if (Size(filePaths) == 1 && !outPath.empty()) - stbi_write_png(outPath.c_str(), image.width(), image.height(), 4, image.data(0, 0), image.rowStride()); + stbi_write_png(outPath.c_str(), image.width(), image.height(), 3, image.data(0, 0), image.rowStride()); } From 46fdb2f9661e434ea952a86d2ed5ae0a205dbc7b Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 19 Apr 2022 21:20:31 +0200 Subject: [PATCH 151/185] QRCode: slightly improve EstimateModuleSize in presence of noise --- core/src/qrcode/QRDetector.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/qrcode/QRDetector.cpp b/core/src/qrcode/QRDetector.cpp index 8d7b41525f..f886f30b72 100644 --- a/core/src/qrcode/QRDetector.cpp +++ b/core/src/qrcode/QRDetector.cpp @@ -183,9 +183,9 @@ static double EstimateModuleSize(const BitMatrix& image, PointF a, PointF b) assert(cur.isBlack()); - auto pattern = cur.readPattern>(); + auto pattern = cur.readPattern>(); - return Reduce(pattern) / 6.0 * length(cur.d); + return (2 * Reduce(pattern) - pattern[0] - pattern[4]) / 12.0 * length(cur.d); } struct DimensionEstimate From 3eea8fead876faff220bd6ccd0bb290ffb14b9a7 Mon Sep 17 00:00:00 2001 From: Qingnan Duan Date: Wed, 20 Apr 2022 15:21:10 +0800 Subject: [PATCH 152/185] Fix inf loop --- core/src/pdf417/PDFDetector.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/pdf417/PDFDetector.cpp b/core/src/pdf417/PDFDetector.cpp index 4570d075b3..5ddf915927 100644 --- a/core/src/pdf417/PDFDetector.cpp +++ b/core/src/pdf417/PDFDetector.cpp @@ -162,10 +162,11 @@ FindRowsWithPattern(const BitMatrix& matrix, int height, int width, int startRow { bool found = false; int startPos, endPos; + int minStartRow = startRow; std::vector counters(pattern.size(), 0); for (; startRow < height; startRow += ROW_STEP) { if (FindGuardPattern(matrix, startColumn, startRow, width, false, pattern, counters, startPos, endPos)) { - while (startRow > 0) { + while (startRow > minStartRow + 1) { if (!FindGuardPattern(matrix, startColumn, --startRow, width, false, pattern, counters, startPos, endPos)) { startRow++; break; From d61c3285208c5fdef9365d86a5c4c230f352108f Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 20 Apr 2022 17:56:26 +0200 Subject: [PATCH 153/185] MultiRes: fix issue with removing duplicate (rotated) 2D symbols --- core/src/Quadrilateral.h | 18 +++++++++++++++++- core/src/Result.cpp | 2 +- core/src/oned/ODReader.cpp | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/core/src/Quadrilateral.h b/core/src/Quadrilateral.h index e95a74e172..23d7e76a8f 100644 --- a/core/src/Quadrilateral.h +++ b/core/src/Quadrilateral.h @@ -109,7 +109,23 @@ Quadrilateral Scale(const Quadrilateral& q, int factor) } template -bool IsIntersecting(const Quadrilateral& a, const Quadrilateral& b) +PointT Center(const Quadrilateral& q) +{ + return Reduce(q) / Size(q); +} + +template +bool IsInside(const PointT& p, const Quadrilateral& q) +{ + // Test if p is on the same side (right or left) of all polygon segments + int pos = 0, neg = 0; + for (int i = 0; i < Size(q); ++i) + (cross(p - q[i], q[(i + 1) % Size(q)] - q[i]) < 0 ? neg : pos)++; + return pos == 0 || neg == 0; +} + +template +bool HaveIntersectingBoundingBoxes(const Quadrilateral& a, const Quadrilateral& b) { // TODO: this is only a quick and dirty approximation that works for the trivial standard cases bool x = b.topRight().x < a.topLeft().x || b.topLeft().x > a.topRight().x; diff --git a/core/src/Result.cpp b/core/src/Result.cpp index 1e5bc4d442..19ef576fac 100644 --- a/core/src/Result.cpp +++ b/core/src/Result.cpp @@ -63,7 +63,7 @@ bool Result::operator==(const Result& o) const return false; if (BarcodeFormats(BarcodeFormat::TwoDCodes).testFlag(format())) - return IsIntersecting(position(), o.position()); + return IsInside(Center(o.position()), position()); // if one line is less than half the length of the other away from the // latter, we consider it to belong to the same symbol diff --git a/core/src/oned/ODReader.cpp b/core/src/oned/ODReader.cpp index c68ca28d89..eb4d4fbcdb 100644 --- a/core/src/oned/ODReader.cpp +++ b/core/src/oned/ODReader.cpp @@ -221,7 +221,7 @@ static Results DoDecode(const std::vector>& readers, // if symbols overlap, remove the one with a lower line count for (auto a = res.begin(); a != res.end(); ++a) for (auto b = std::next(a); b != res.end(); ++b) - if (IsIntersecting(a->position(), b->position())) + if (HaveIntersectingBoundingBoxes(a->position(), b->position())) *(a->lineCount() < b->lineCount() ? a : b) = Result(DecodeStatus::NotFound); //TODO: C++20 res.erase_if() From e8d640f840db5d67f5f0d15e9172875937339277 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 20 Apr 2022 17:58:09 +0200 Subject: [PATCH 154/185] ImageView: add `subsampled` member function (not used, yet) --- core/src/ImageView.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/ImageView.h b/core/src/ImageView.h index 12d0582962..ec72d18ae2 100644 --- a/core/src/ImageView.h +++ b/core/src/ImageView.h @@ -97,6 +97,12 @@ class ImageView } return *this; } + + ImageView subsampled(int scale) const + { + return {_data, _width / scale, _height / scale, _format, _rowStride * scale, _pixStride * scale}; + } + }; } // ZXing From 0f6977c9d5e36f8c0d069de9a21349eba96619c1 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 20 Apr 2022 18:02:12 +0200 Subject: [PATCH 155/185] gitignore: add filters to hide build outputs from in-tree android builds --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 1661636184..a4223377f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ CMakeLists.txt.user +*.o +*.so +*.lib +*.d +*.a From c7098b48c1096a432ba503a75947a9bb7447f975 Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 21 Apr 2022 23:43:27 +0200 Subject: [PATCH 156/185] QRCode: implement yet another (hopefully simpler) fix for #300 and #308 Instead of stepping too far and then back, step only as far as we need. --- core/src/qrcode/QRDetector.cpp | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/core/src/qrcode/QRDetector.cpp b/core/src/qrcode/QRDetector.cpp index f886f30b72..4650b2603f 100644 --- a/core/src/qrcode/QRDetector.cpp +++ b/core/src/qrcode/QRDetector.cpp @@ -170,18 +170,12 @@ static double EstimateModuleSize(const BitMatrix& image, PointF a, PointF b) BitMatrixCursorF cur(image, a, b - a); assert(cur.isBlack()); - if (!cur.stepToEdge(3, distance(a, b) / 3)) + if (!cur.stepToEdge(3, distance(a, b) / 3, true)) return -1; + assert(cur.isBlack()); cur.turnBack(); - // the following is basically a simple "cur.step()" that reverts the very last step that crossed from back into - // white, but due to a numerical instability near an integer boundary (think .999999999995) it might be required - // to back up two steps. See issues #300 and #308. - if (!cur.stepToEdge(1, 2)) - return -1; - - assert(cur.isBlack()); auto pattern = cur.readPattern>(); @@ -217,12 +211,10 @@ static RegressionLine TraceLine(const BitMatrix& image, PointF p, PointF d, int RegressionLine line; line.setDirectionInward(cur.back()); - cur.stepToEdge(edge); - if (edge == 3) { - // collect points inside the black line -> go one step back + // collect points inside the black line -> backup on 3rd edge + cur.stepToEdge(edge, 0, edge == 3); + if (edge == 3) cur.turnBack(); - cur.step(); - } for (auto dir : {Direction::LEFT, Direction::RIGHT}) { auto c = BitMatrixCursorI(image, PointI(cur.p), PointI(mainDirection(cur.direction(dir)))); From d74213523aa132162f068a654ba0fe2574b603ee Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 21 Apr 2022 23:46:24 +0200 Subject: [PATCH 157/185] QRCode: fix finder pattern order on very slanted symbols --- core/src/qrcode/QRDetector.cpp | 14 ++++++++++---- .../qrcode-2/fix-finderpattern-order.jpg | Bin 0 -> 11524 bytes .../qrcode-2/fix-finderpattern-order.txt | 1 + 3 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 test/samples/qrcode-2/fix-finderpattern-order.jpg create mode 100644 test/samples/qrcode-2/fix-finderpattern-order.txt diff --git a/core/src/qrcode/QRDetector.cpp b/core/src/qrcode/QRDetector.cpp index 4650b2603f..2cfbd6fb0a 100644 --- a/core/src/qrcode/QRDetector.cpp +++ b/core/src/qrcode/QRDetector.cpp @@ -98,7 +98,13 @@ static FinderPatternSets GenerateFinderPatternSets(std::vector(); - auto squaredDistance = [](PointF a, PointF b) { return dot((a - b), (a - b)); }; + auto squaredDistance = [](const auto* a, const auto* b) { + // The scaling of the distance by the b/a size ratio is a very coarse compensation for the shortening effect of + // the camera projection on slanted symbols. The fact that the size of the finder pattern is proportional to the + // distance from the camera is used here. This approximation only works if a < b < 2*a (see below). + // Test image: fix-finderpattern-order.jpg + return dot((*a - *b), (*a - *b)) * std::pow(double(b->size) / a->size, 2); + }; int nbPatterns = Size(patterns); for (int i = 0; i < nbPatterns - 2; i++) { @@ -115,9 +121,9 @@ static FinderPatternSets GenerateFinderPatternSets(std::vector= distAB && distBC >= distAC) { std::swap(a, b); diff --git a/test/samples/qrcode-2/fix-finderpattern-order.jpg b/test/samples/qrcode-2/fix-finderpattern-order.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0250a0e5be617e2c41f56f1486dfa8712c4cdf81 GIT binary patch literal 11524 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 X7Azll7zJcweI(^{=*c;?_vQZs(&}`6 literal 0 HcmV?d00001 diff --git a/test/samples/qrcode-2/fix-finderpattern-order.txt b/test/samples/qrcode-2/fix-finderpattern-order.txt new file mode 100644 index 0000000000..97268c5c8a --- /dev/null +++ b/test/samples/qrcode-2/fix-finderpattern-order.txt @@ -0,0 +1 @@ +VERSION 2 8CM \ No newline at end of file From 654905538a20c229cb2a8e547f6e08c14a450ca4 Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 21 Apr 2022 23:57:08 +0200 Subject: [PATCH 158/185] QRCode: fix the orientation of the cursor in TraceLine --- core/src/qrcode/QRDetector.cpp | 25 ++++++++++++------------ test/blackbox/BlackboxTestRunner.cpp | 10 +++++----- test/samples/qrcode-2/fix-traceline.jpg | Bin 0 -> 15215 bytes test/samples/qrcode-2/fix-traceline.txt | 1 + 4 files changed, 19 insertions(+), 17 deletions(-) create mode 100644 test/samples/qrcode-2/fix-traceline.jpg create mode 100644 test/samples/qrcode-2/fix-traceline.txt diff --git a/core/src/qrcode/QRDetector.cpp b/core/src/qrcode/QRDetector.cpp index 2cfbd6fb0a..54572b05ec 100644 --- a/core/src/qrcode/QRDetector.cpp +++ b/core/src/qrcode/QRDetector.cpp @@ -222,19 +222,20 @@ static RegressionLine TraceLine(const BitMatrix& image, PointF p, PointF d, int if (edge == 3) cur.turnBack(); - for (auto dir : {Direction::LEFT, Direction::RIGHT}) { - auto c = BitMatrixCursorI(image, PointI(cur.p), PointI(mainDirection(cur.direction(dir)))); - // if cur.d is near diagonal, it could be c.p is at a corner, i.e. c is not currently at an edge and hence, - // stepAlongEdge() would fail. Going either a step forward or backward should do the trick. - if (!c.edgeAt(dir)) { - c.step(); - if (!c.edgeAt(dir)) { - c.step(-2); - if (!c.edgeAt(dir)) - return {}; - } - } + auto curI = BitMatrixCursorI(image, PointI(cur.p), PointI(mainDirection(cur.d))); + // make sure curI positioned such that the white->black edge is directly behind + // Test image: fix-traceline.jpg + while (!curI.edgeAtBack()) { + if (curI.edgeAtLeft()) + curI.turnRight(); + else if (curI.edgeAtRight()) + curI.turnLeft(); + else + curI.step(-1); + } + for (auto dir : {Direction::LEFT, Direction::RIGHT}) { + auto c = BitMatrixCursorI(image, curI.p, curI.direction(dir)); auto stepCount = static_cast(maxAbsComponent(cur.p - p)); do { line.add(centered(c.p)); diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 278cd7171c..119345aefa 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -578,11 +578,11 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set { 16, 16, 270 }, }); - runTests("qrcode-2", "QRCode", 43, { - { 41, 41, 0 }, - { 41, 41, 90 }, - { 41, 41, 180 }, - { 41, 41, 270 }, + runTests("qrcode-2", "QRCode", 45, { + { 43, 43, 0 }, + { 43, 43, 90 }, + { 43, 43, 180 }, + { 43, 43, 270 }, { 21, 1, pure }, // the misread is the 'outer' symbol in 16.png }); diff --git a/test/samples/qrcode-2/fix-traceline.jpg b/test/samples/qrcode-2/fix-traceline.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4bea7b8a99c2d7ea560c82ef61d471bfbeca2327 GIT binary patch literal 15215 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 literal 0 HcmV?d00001 diff --git a/test/samples/qrcode-2/fix-traceline.txt b/test/samples/qrcode-2/fix-traceline.txt new file mode 100644 index 0000000000..97268c5c8a --- /dev/null +++ b/test/samples/qrcode-2/fix-traceline.txt @@ -0,0 +1 @@ +VERSION 2 8CM \ No newline at end of file From 131bb0df0b31bf83173be42a7497ee2b61afbc41 Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 22 Apr 2022 00:00:08 +0200 Subject: [PATCH 159/185] QRCode: fix build regression of c7098b48c1096a432ba503a75947a9bb7447f975 Overlooked a hunk, again... --- core/src/BitMatrixCursor.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/BitMatrixCursor.h b/core/src/BitMatrixCursor.h index 57a36550eb..a7a98e1b61 100644 --- a/core/src/BitMatrixCursor.h +++ b/core/src/BitMatrixCursor.h @@ -119,9 +119,10 @@ class BitMatrixCursor * @brief stepToEdge advances cursor to one step behind the next (or n-th) edge. * @param nth number of edges to pass * @param range max number of steps to take + * @param backup whether or not to backup one step so we land in front of the edge * @return number of steps taken or 0 if moved outside of range/image */ - int stepToEdge(int nth = 1, int range = 0) + int stepToEdge(int nth = 1, int range = 0, bool backup = false) { // TODO: provide an alternative and faster out-of-bounds check than isIn() inside testAt() int steps = 0; @@ -135,6 +136,8 @@ class BitMatrixCursor --nth; } } + if (backup) + --steps; p += steps * d; return steps * (nth == 0); } From e448c0fd91b07929469e4a3e59db3dc4572257ec Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 22 Apr 2022 00:53:56 +0200 Subject: [PATCH 160/185] QRCode: use EstimateTilt(fp) to choose best sampling grid postition This is related to #199. --- core/src/qrcode/QRDetector.cpp | 14 +++++++++++--- test/blackbox/BlackboxTestRunner.cpp | 10 +++++----- test/samples/qrcode-2/estimate-tilt.jpg | Bin 0 -> 3173 bytes test/samples/qrcode-2/estimate-tilt.txt | 1 + 4 files changed, 17 insertions(+), 8 deletions(-) create mode 100644 test/samples/qrcode-2/estimate-tilt.jpg create mode 100644 test/samples/qrcode-2/estimate-tilt.txt diff --git a/core/src/qrcode/QRDetector.cpp b/core/src/qrcode/QRDetector.cpp index 54572b05ec..e2834481bb 100644 --- a/core/src/qrcode/QRDetector.cpp +++ b/core/src/qrcode/QRDetector.cpp @@ -250,6 +250,14 @@ static RegressionLine TraceLine(const BitMatrix& image, PointF p, PointF d, int return line; } +// estimate how tilted the symbol is (return value between 1 and 2, see also above) +static double EstimateTilt(const FinderPatternSet& fp) +{ + int min = std::min({fp.bl.size, fp.tl.size, fp.tr.size}); + int max = std::max({fp.bl.size, fp.tl.size, fp.tr.size}); + return double(max) / min; +} + DetectorResult SampleAtFinderPatternSet(const BitMatrix& image, const FinderPatternSet& fp) { auto top = EstimateDimension(image, fp.tl, fp.tr); @@ -301,9 +309,9 @@ DetectorResult SampleAtFinderPatternSet(const BitMatrix& image, const FinderPatt } } - // if the resolution of the RegressionLines is sufficient, use their intersection as the best estimate - // (see discussion in #199, TODO: tune threshold in RegressionLine::isHighRes()) - if (bl2.isHighRes() && bl3.isHighRes() && tr2.isHighRes() && tr3.isHighRes()) + // if the symbol is tilted or the resolution of the RegressionLines is sufficient, use their intersection + // as the best estimate (see discussion in #199 and test image estimate-tilt.jpg ) + if (EstimateTilt(fp) > 1.1 || (bl2.isHighRes() && bl3.isHighRes() && tr2.isHighRes() && tr3.isHighRes())) return sample(brInter, quad[2] - PointF(3, 3)); } } diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 119345aefa..900d664e6c 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -578,11 +578,11 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set { 16, 16, 270 }, }); - runTests("qrcode-2", "QRCode", 45, { - { 43, 43, 0 }, - { 43, 43, 90 }, - { 43, 43, 180 }, - { 43, 43, 270 }, + runTests("qrcode-2", "QRCode", 46, { + { 44, 44, 0 }, + { 44, 44, 90 }, + { 44, 44, 180 }, + { 44, 44, 270 }, { 21, 1, pure }, // the misread is the 'outer' symbol in 16.png }); diff --git a/test/samples/qrcode-2/estimate-tilt.jpg b/test/samples/qrcode-2/estimate-tilt.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e30b300588c2041e74c94fe59da131b8edf52911 GIT binary patch literal 3173 zcmbtWi8qu{AAV=Ei?J11X2uptWS8C8lYNY(AtfZqzEvU_A&e}f57+qz4zR6?s?DedG2%0ea^Etw)X+xHa9ji20$PH05KZan*ibf zroY9^%*4vZXig4xb`DOy16+*e6XO-&Wqe|Xk%t8tOHm4il2TMZe(bn9T3Z{9vB6xq zk_GyI2kdnKa2CJL>AzuvC2CxNXv>6wX{tCacKA^puI$*;@fLry}oBJ(Tvy-y5~UA}JSp-W>npdx#V<3(HC@(5 zVwm2o0cWqIwG6AnMd|A#UBc@EnXWJ%3=_Ct{Mm{8FBkkIASYA zf$(v0ONCrM;bLtlAi^iw^L$x)Jvv^+;b3pAHVsR0mRZ5sI&yJz_B^i3t)LGVatm6L zNo3+(AJ$*UIe6eVZG0%qAvw6B--}q-g37rY=vkkJuPjkhJhu~&c;rK{n*PG2d_VGF zXjJvcPCfO7g=NQVHPS-JLb>H_2W=(m!ZuEJXWOg8v835fV;rh|v0U%g^dvRyQfNcr z@&peTQ+{p)y&}JcS|1V`p(Ju71W0r2ox7j%Pk_1i|_0vWcmD;(XN;PI5!wF`G_W()}|3Lofiy=@@ zFeldjlGZJ%|I{8}e`wo=Li${0P{-- z>M^(1lLE|Pd1&+Pvk6r8`rhT6!oDHz3P*n=N2raK=V8xQ>zq^oYf5g25||Ay5Tpw% z&BYD3qRCr7@A=RySH4d4j}&EP)udkhW_bH~qbeQ~5<}>xw5iHY!qy2X876^fXPpLx z-CZd|HQXpYNc6tN(QV_J0o>HY_{0l8S2G8%5&tq$--BEppX`XOYj<+nz^RH-H-}qtXC09l8E!Aq$BxxDvndH)@W4LW= zFpmVRYB^=?yp(DbqQd7O?n%Y($4=x}ch#wxUrHGt&{lp9U+=?MPP*CVdaPX99+h|YS_hiBU5FYj)JEn8{uY8eFtcZ znTvIc`KzEb4vrp;d3lF;j!gHRCb~9NR74H8uvo!V!x~Vg_m(A6Yt)*2sgYRUM$o%=)*41-))q%u^sEFA;V06=KT4byH+WIs9 zeNWKj{$mJW2!s{J`o|O)mHPSpJ%Rh;yaiGDB2T+6!D2uMF6)x<*C;C3(e8=iin`9(|4@HF|AQ}pFR|}j(T0lbb&j( zrX`W)+M>jRyvsYM<$apF=GF{rlgQlC1YmE3coIfq0(2#Qd1u!M{uw(CPy9X!I1+IhjpBjLfh-m6BgtR>lcw+l0#{PfBw zK$NHQ=6Z)Ur@)Sg^}LMY7D~FC_TJ!AL8Q>Ssl(%V!BFjBEwO+esbcHt$TiK1~bWaEmV(#;ntjRgCgnl8($KKhmB^Q%*GJA) zZ#SKwsZ_*z@61lQp&%&>%-M!P!}1Ysk_RTivf|B%@60!`!ck@Vx@_cDSLq+I2d3|{ zJ-y{(Ve>ml82;s}y*X!Q?w#`~9{TdD$%j&0Vu$2Ws8!n;fLP+X+T)V?+ zM|lyZUX>j?GOs?FLVT3iY*xI|8`kW-_9;Ik+M$|jUG3L>R1k7^<&r%IM_Ukx&#IX7tNcFZux_RsvOR!Asp2?K z*8a||YjoF{!zTP3qn_zYM?DM>A_0LVqPw7vmR}rUbOi5;A(tn5vYiEZn9a zEz+DFY@ zcy6*nipr-Jp6>5jZBpfvqb?%9`=WywE^`zPfFNKf6!tH7?8i_r9KsFdQPhVS*c9<2 zkhW)ePw06N{zfSfNEfj8)~rreV=;Y(c%@b0p^*^E4L&@P{#%+qOMe`?03h z?8#LKaqK`s0iua0c2vwt8*E%etBm74gWs(sJa&~ia@PiExJiy;7j+KBGW8Jsb&2zt zNOrMIB&@S6Yn1Yyt)1l|mpvPSqAQR=p?q+`b9qk3G+^It~6Z1T5CuJIP?3BJW+6uhRQSvaWRq{5B Jc6ZyO{Raz5fXV;> literal 0 HcmV?d00001 diff --git a/test/samples/qrcode-2/estimate-tilt.txt b/test/samples/qrcode-2/estimate-tilt.txt new file mode 100644 index 0000000000..a837ad36a0 --- /dev/null +++ b/test/samples/qrcode-2/estimate-tilt.txt @@ -0,0 +1 @@ +VERSION 1 6CM \ No newline at end of file From 2c009d66035aaec2511602bf3a0a7775c3042735 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 25 Apr 2022 14:36:55 +0200 Subject: [PATCH 161/185] multi-res: introduce `tryDownscale` hint and rename threshold property Make the `DecodingHints` interface more consistent this way, enable the feature by default (the `ReadBarcode` function does not use it, yet) and set a proper default for the threshold. --- core/src/DecodeHints.h | 14 +++++++++----- core/src/ReadBarcode.cpp | 2 +- example/ZXingReader.cpp | 14 +++----------- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/core/src/DecodeHints.h b/core/src/DecodeHints.h index a67820b80e..6b37c0a62a 100644 --- a/core/src/DecodeHints.h +++ b/core/src/DecodeHints.h @@ -51,6 +51,7 @@ class DecodeHints { bool _tryHarder : 1; bool _tryRotate : 1; + bool _tryDownscale : 1; bool _isPure : 1; bool _tryCode39ExtendedMode : 1; bool _validateCode39CheckSum : 1; @@ -62,16 +63,16 @@ class DecodeHints std::string _characterSet; std::vector _allowedLengths; BarcodeFormats _formats = BarcodeFormat::None; - uint16_t _multiResolutionThreshold = 0xffff; + uint16_t _downscaleThreshold = 500; uint8_t _minLineCount = 2; uint8_t _maxNumberOfSymbols = 0xff; public: // bitfields don't get default initialized to 0. DecodeHints() - : _tryHarder(1), _tryRotate(1), _isPure(0), _tryCode39ExtendedMode(0), _validateCode39CheckSum(0), - _validateITFCheckSum(0), _returnCodabarStartEnd(0), _binarizer(Binarizer::LocalAverage), - _eanAddOnSymbol(EanAddOnSymbol::Ignore) + : _tryHarder(1), _tryRotate(1), _tryDownscale(1), _isPure(0), _tryCode39ExtendedMode(0), + _validateCode39CheckSum(0), _validateITFCheckSum(0), _returnCodabarStartEnd(0), + _binarizer(Binarizer::LocalAverage), _eanAddOnSymbol(EanAddOnSymbol::Ignore) {} #define ZX_PROPERTY(TYPE, GETTER, SETTER) \ @@ -87,6 +88,9 @@ class DecodeHints /// Also try detecting code in 90, 180 and 270 degree rotated images. ZX_PROPERTY(bool, tryRotate, setTryRotate) + /// Also try detecting code in downscaled images (depending on image size). + ZX_PROPERTY(bool, tryDownscale, setTryDownscale) + /// Binarizer to use internally when using the ReadBarcode function ZX_PROPERTY(Binarizer, binarizer, setBinarizer) @@ -95,7 +99,7 @@ class DecodeHints /// Image size (width or height) threshold at which to start multi-resolution scanning // WARNING: this API is experimental and may change/disappear - ZX_PROPERTY(uint16_t, multiResolutionThreshold, setMultiResolutionThreshold) + ZX_PROPERTY(uint16_t, downscaleThreshold, setDownscaleThreshold) /// The number of scan lines in a 1D barcode that have to be equal to accept the result, default is 2 ZX_PROPERTY(uint8_t, minLineCount, setMinLineCount) diff --git a/core/src/ReadBarcode.cpp b/core/src/ReadBarcode.cpp index be885b5053..5375ccb9ac 100644 --- a/core/src/ReadBarcode.cpp +++ b/core/src/ReadBarcode.cpp @@ -146,7 +146,7 @@ Results ReadBarcodes(const ImageView& _iv, const DecodeHints& hints) if (hints.isPure()) return {reader.read(*CreateBitmap(hints.binarizer(), iv))}; - LumImagePyramid pyramid(iv, hints.multiResolutionThreshold()); + LumImagePyramid pyramid(iv, hints.downscaleThreshold() * hints.tryDownscale()); Results results; int maxSymbols = hints.maxNumberOfSymbols(); diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 84b488de44..83eb038c29 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -40,8 +40,8 @@ static void PrintUsage(const char* exePath) std::cout << "Usage: " << exePath << " [-fast] [-norotate] [-format ] [-pngout ] [-ispure] [-1] ...\n" << " -fast Skip some lines/pixels during detection (faster)\n" << " -norotate Don't try rotated image during detection (faster)\n" + << " -noscale Don't try downscaled images during detection (faster)\n" << " -format Only detect given format(s) (faster)\n" - << " -multires Image size threshold at which to start multi-resolution scanning, 0 disables it\n" << " -ispure Assume the image contains only a 'pure'/perfect code (faster)\n" << " -1 Print only file name, text and status on one line per file/barcode\n" << " -escape Escape non-graphical characters in angle brackets (ignored for -1 option, which always escapes)\n" @@ -62,6 +62,8 @@ static bool ParseOptions(int argc, char* argv[], DecodeHints& hints, bool& oneLi hints.setTryHarder(false); } else if (strcmp(argv[i], "-norotate") == 0) { hints.setTryRotate(false); + } else if (strcmp(argv[i], "-noscale") == 0) { + hints.setDownscaleThreshold(0); } else if (strcmp(argv[i], "-ispure") == 0) { hints.setIsPure(true); hints.setBinarizer(Binarizer::FixedThreshold); @@ -74,15 +76,6 @@ static bool ParseOptions(int argc, char* argv[], DecodeHints& hints, bool& oneLi std::cerr << e.what() << "\n"; return false; } - } else if (strcmp(argv[i], "-multires") == 0) { - if (++i == argc) - return false; - try { - hints.setMultiResolutionThreshold(std::stoi(argv[i])); - } catch (const std::exception& e) { - std::cerr << e.what() << "\n"; - return false; - } } else if (strcmp(argv[i], "-1") == 0) { oneLine = true; } else if (strcmp(argv[i], "-escape") == 0) { @@ -134,7 +127,6 @@ int main(int argc, char* argv[]) bool angleEscape = false; int ret = 0; - hints.setMultiResolutionThreshold(500); // enable the feature by default if (!ParseOptions(argc, argv, hints, oneLine, angleEscape, filePaths, outPath)) { PrintUsage(argv[0]); From 5ab1a3de03ef63799d3aeba9a0b17ec4a041eeca Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 25 Apr 2022 14:38:32 +0200 Subject: [PATCH 162/185] android: introduce tryDownscale option (in lib and demo app) --- .../com/example/zxingcppdemo/MainActivity.kt | 3 ++- .../main/res/layout-land/activity_camera.xml | 5 +++++ .../src/main/res/layout/activity_camera.xml | 5 +++++ .../zxingcpp/src/main/cpp/BarcodeReader.cpp | 18 +++++++++++------- .../main/java/com/zxingcpp/BarcodeReader.kt | 10 ++++++---- 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/wrappers/android/app/src/main/java/com/example/zxingcppdemo/MainActivity.kt b/wrappers/android/app/src/main/java/com/example/zxingcppdemo/MainActivity.kt index cffb77c455..d023a70676 100644 --- a/wrappers/android/app/src/main/java/com/example/zxingcppdemo/MainActivity.kt +++ b/wrappers/android/app/src/main/java/com/example/zxingcppdemo/MainActivity.kt @@ -149,7 +149,8 @@ class MainActivity : AppCompatActivity() { readerCpp.options = BarcodeReader.Options( formats = if (binding.qrcode.isChecked) setOf(Format.QR_CODE) else setOf(), tryHarder = binding.tryHarder.isChecked, - tryRotate = binding.tryRotate.isChecked + tryRotate = binding.tryRotate.isChecked, + tryDownscale = binding.tryDownscale.isChecked ) resultText = try { diff --git a/wrappers/android/app/src/main/res/layout-land/activity_camera.xml b/wrappers/android/app/src/main/res/layout-land/activity_camera.xml index 3f7859de28..9cb819f82d 100644 --- a/wrappers/android/app/src/main/res/layout-land/activity_camera.xml +++ b/wrappers/android/app/src/main/res/layout-land/activity_camera.xml @@ -89,6 +89,11 @@ style="@style/Chip" android:text="tryRotate" /> + + + + (duration).count()); @@ -136,7 +140,7 @@ extern "C" JNIEXPORT jstring JNICALL Java_com_zxingcpp_BarcodeReader_readYBuffer( JNIEnv *env, jobject thiz, jobject yBuffer, jint rowStride, jint left, jint top, jint width, jint height, jint rotation, - jstring formats, jboolean tryHarder, jboolean tryRotate, + jstring formats, jboolean tryHarder, jboolean tryRotate, jboolean tryDownscale, jobject result) { const uint8_t* pixels = static_cast(env->GetDirectBufferAddress(yBuffer)); @@ -145,7 +149,7 @@ Java_com_zxingcpp_BarcodeReader_readYBuffer( ImageView{pixels + top * rowStride + left, width, height, ImageFormat::Lum, rowStride} .rotated(rotation); - return Read(env, image, formats, tryHarder, tryRotate, result); + return Read(env, image, formats, tryHarder, tryRotate, tryDownscale, result); } struct LockedPixels @@ -171,7 +175,7 @@ extern "C" JNIEXPORT jstring JNICALL Java_com_zxingcpp_BarcodeReader_readBitmap( JNIEnv* env, jobject thiz, jobject bitmap, jint left, jint top, jint width, jint height, jint rotation, - jstring formats, jboolean tryHarder, jboolean tryRotate, + jstring formats, jboolean tryHarder, jboolean tryRotate, jboolean tryDownscale, jobject result) { AndroidBitmapInfo bmInfo; @@ -193,5 +197,5 @@ Java_com_zxingcpp_BarcodeReader_readBitmap( .cropped(left, top, width, height) .rotated(rotation); - return Read(env, image, formats, tryHarder, tryRotate, result); + return Read(env, image, formats, tryHarder, tryRotate, tryDownscale, result); } diff --git a/wrappers/android/zxingcpp/src/main/java/com/zxingcpp/BarcodeReader.kt b/wrappers/android/zxingcpp/src/main/java/com/zxingcpp/BarcodeReader.kt index 1174683a51..52e4b3b380 100644 --- a/wrappers/android/zxingcpp/src/main/java/com/zxingcpp/BarcodeReader.kt +++ b/wrappers/android/zxingcpp/src/main/java/com/zxingcpp/BarcodeReader.kt @@ -36,7 +36,8 @@ class BarcodeReader { data class Options( val formats: Set = setOf(), val tryHarder: Boolean = false, - val tryRotate: Boolean = false + val tryRotate: Boolean = false, + val tryDownscale: Boolean = false ) data class Position( @@ -78,6 +79,7 @@ class BarcodeReader { options.formats.joinToString(), options.tryHarder, options.tryRotate, + options.tryDownscale, result ) } @@ -97,7 +99,7 @@ class BarcodeReader { val status = with(options) { readBitmap( bitmap, cropRect.left, cropRect.top, cropRect.width(), cropRect.height(), rotation, - formats.joinToString(), tryHarder, tryRotate, result + formats.joinToString(), tryHarder, tryRotate, tryDownscale, result ) } return try { @@ -110,13 +112,13 @@ class BarcodeReader { // setting the format enum from inside the JNI code is a hassle -> use returned String instead private external fun readYBuffer( yBuffer: ByteBuffer, rowStride: Int, left: Int, top: Int, width: Int, height: Int, rotation: Int, - formats: String, tryHarder: Boolean, tryRotate: Boolean, + formats: String, tryHarder: Boolean, tryRotate: Boolean, tryDownscale: Boolean, result: Result, ): String? private external fun readBitmap( bitmap: Bitmap, left: Int, top: Int, width: Int, height: Int, rotation: Int, - formats: String, tryHarder: Boolean, tryRotate: Boolean, + formats: String, tryHarder: Boolean, tryRotate: Boolean, tryDownscale: Boolean, result: Result, ): String? From 154af6cd60e0d983b210d1daeb1292dad432b5b9 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 25 Apr 2022 15:21:42 +0200 Subject: [PATCH 163/185] multi-res: disable downscaling in black box test runner --- test/blackbox/BlackboxTestRunner.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index 900d664e6c..9899cef628 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -262,7 +262,8 @@ static Result readMultiple(const std::vector& imgPaths, std::string_vi std::list allResults; for (const auto& imgPath : imgPaths) { auto results = - ReadBarcodes(ImageLoader::load(imgPath), DecodeHints().setFormats(BarcodeFormatFromString(format.data()))); + ReadBarcodes(ImageLoader::load(imgPath), + DecodeHints().setFormats(BarcodeFormatFromString(format.data())).setTryDownscale(false)); allResults.insert(allResults.end(), results.begin(), results.end()); } From 950c3b89d74fd22bc4baf46e7a41a90bf6cb4835 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 25 Apr 2022 22:23:16 +0200 Subject: [PATCH 164/185] multi-res: add hack to be able to call downscale code in ReadBarcode This is currently used in the android and python wrapper. See discussion in #282 why this is not generally enabled at the moment. It basically comes down to the fact, that we can not have different default values for `tryDownscale` based on whether we call the single or multi symbol function. --- core/src/ReadBarcode.cpp | 20 ++++++++++--------- .../zxingcpp/src/main/cpp/BarcodeReader.cpp | 8 +++----- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/core/src/ReadBarcode.cpp b/core/src/ReadBarcode.cpp index 5375ccb9ac..912b9763f4 100644 --- a/core/src/ReadBarcode.cpp +++ b/core/src/ReadBarcode.cpp @@ -126,15 +126,17 @@ std::unique_ptr CreateBitmap(ZXing::Binarizer binarizer, const Ima Result ReadBarcode(const ImageView& _iv, const DecodeHints& hints) { -#if 0 - auto ress = ReadBarcodes(_iv, DecodeHints(hints).setMaxNumberOfSymbols(1)); - return ress.empty() ? Result(DecodeStatus::NotFound) : ress.front(); -#else - LumImage lum; - ImageView iv = SetupLumImageView(_iv, lum, hints); - - return MultiFormatReader(hints).read(*CreateBitmap(hints.binarizer(), iv)); -#endif + if (hints.maxNumberOfSymbols() == 1) { + // HACK: use the maxNumberOfSymbols value as a switch to ReadBarcodes to enable the downscaling + // see python and android wrapper + auto ress = ReadBarcodes(_iv, DecodeHints(hints).setMaxNumberOfSymbols(1)); + return ress.empty() ? Result(DecodeStatus::NotFound) : ress.front(); + } else { + LumImage lum; + ImageView iv = SetupLumImageView(_iv, lum, hints); + + return MultiFormatReader(hints).read(*CreateBitmap(hints.binarizer(), iv)); + } } Results ReadBarcodes(const ImageView& _iv, const DecodeHints& hints) diff --git a/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp b/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp index 6c19adb05a..965ed6e2a1 100644 --- a/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp +++ b/wrappers/android/zxingcpp/src/main/cpp/BarcodeReader.cpp @@ -92,15 +92,13 @@ jstring Read(JNIEnv *env, ImageView image, jstring formats, jboolean tryHarder, .setFormats(BarcodeFormatsFromString(J2CString(env, formats))) .setTryHarder(tryHarder) .setTryRotate( tryRotate ) - .setTryDownscale(tryDownscale); + .setTryDownscale(tryDownscale) + .setMaxNumberOfSymbols(1); // see ReadBarcode implementation // return C2JString(env, ToString(DecodeStatus::NotFound)); auto startTime = std::chrono::high_resolution_clock::now(); - Result res(DecodeStatus::NotFound); - auto results = ReadBarcodes(image, DecodeHints(hints).setMaxNumberOfSymbols(1)); - if (!results.empty()) - res = results.front(); + auto res = ReadBarcode(image, hints); auto duration = std::chrono::high_resolution_clock::now() - startTime; // LOGD("time: %4d ms\n", (int)std::chrono::duration_cast(duration).count()); From 1291b9cc60626a741ae7a5743719640488621bb7 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 25 Apr 2022 22:27:04 +0200 Subject: [PATCH 165/185] python: add try_downscale option to read_barcode(s) functions --- wrappers/python/zxing.cpp | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/wrappers/python/zxing.cpp b/wrappers/python/zxing.cpp index 2e815d2985..e7fdfa1425 100644 --- a/wrappers/python/zxing.cpp +++ b/wrappers/python/zxing.cpp @@ -35,14 +35,17 @@ std::ostream& operator<<(std::ostream& os, const Position& points) { } template -auto read_barcode_impl(FUNC func, py::object _image, const BarcodeFormats& formats, bool try_rotate, - Binarizer binarizer, bool is_pure, EanAddOnSymbol ean_add_on_symbol) +auto read_barcode_impl(FUNC func, py::object _image, const BarcodeFormats& formats, bool try_rotate, bool try_downscale, + Binarizer binarizer, bool is_pure, EanAddOnSymbol ean_add_on_symbol, + uint8_t max_number_of_symbols = 0xff) { const auto hints = DecodeHints() .setFormats(formats) .setTryRotate(try_rotate) + .setTryDownscale(try_downscale) .setBinarizer(binarizer) .setIsPure(is_pure) + .setMaxNumberOfSymbols(max_number_of_symbols) .setEanAddOnSymbol(ean_add_on_symbol); const auto _type = std::string(py::str(py::type::of(_image))); Image image; @@ -84,16 +87,18 @@ auto read_barcode_impl(FUNC func, py::object _image, const BarcodeFormats& forma return func({bytes, width, height, imgfmt, width * channels, channels}, hints); } -Result read_barcode(py::object _image, const BarcodeFormats& formats, bool try_rotate, Binarizer binarizer, - bool is_pure, EanAddOnSymbol ean_add_on_symbol) +Result read_barcode(py::object _image, const BarcodeFormats& formats, bool try_rotate, bool try_downscale, + Binarizer binarizer, bool is_pure, EanAddOnSymbol ean_add_on_symbol) { - return read_barcode_impl(ReadBarcode, _image, formats, try_rotate, binarizer, is_pure, ean_add_on_symbol); + return read_barcode_impl(ReadBarcode, _image, formats, try_rotate, try_downscale, binarizer, is_pure, + ean_add_on_symbol, 1); } -Results read_barcodes(py::object _image, const BarcodeFormats& formats, bool try_rotate, Binarizer binarizer, - bool is_pure, EanAddOnSymbol ean_add_on_symbol) +Results read_barcodes(py::object _image, const BarcodeFormats& formats, bool try_rotate, bool try_downscale, + Binarizer binarizer, bool is_pure, EanAddOnSymbol ean_add_on_symbol) { - return read_barcode_impl(ReadBarcodes, _image, formats, try_rotate, binarizer, is_pure, ean_add_on_symbol); + return read_barcode_impl(ReadBarcodes, _image, formats, try_rotate, try_downscale, binarizer, is_pure, + ean_add_on_symbol); } Image write_barcode(BarcodeFormat format, std::string text, int width, int height, int quiet_zone, int ec_level) @@ -222,6 +227,7 @@ PYBIND11_MODULE(zxingcpp, m) py::arg("image"), py::arg("formats") = BarcodeFormats{}, py::arg("try_rotate") = true, + py::arg("try_downscale") = true, py::arg("binarizer") = Binarizer::LocalAverage, py::arg("is_pure") = false, py::arg("ean_add_on_symbol") = EanAddOnSymbol::Ignore, @@ -233,8 +239,11 @@ PYBIND11_MODULE(zxingcpp, m) ":type formats: zxing.BarcodeFormat|zxing.BarcodeFormats\n" ":param formats: the format(s) to decode. If ``None``, decode all formats.\n" ":type try_rotate: bool\n" - ":param try_rotate: if ``True`` (the default), decoder searched for barcodes in any direction; \n" + ":param try_rotate: if ``True`` (the default), decoder searches for barcodes in any direction; \n" " if ``False``, it will not search for 90° / 270° rotated barcodes.\n" + ":type try_downscale: bool\n" + ":param try_downscale: if ``True`` (the default), decoder also scans downscaled versions of the input; \n" + " if ``False``, it will only search in the resolution provided.\n" ":type binarizer: zxing.Binarizer\n" ":param binarizer: the binarizer used to convert image before decoding barcodes.\n" " Defaults to :py:attr:`zxing.Binarizer.LocalAverage`." @@ -251,6 +260,7 @@ PYBIND11_MODULE(zxingcpp, m) py::arg("image"), py::arg("formats") = BarcodeFormats{}, py::arg("try_rotate") = true, + py::arg("try_downscale") = true, py::arg("binarizer") = Binarizer::LocalAverage, py::arg("is_pure") = false, py::arg("ean_add_on_symbol") = EanAddOnSymbol::Ignore, @@ -262,8 +272,11 @@ PYBIND11_MODULE(zxingcpp, m) ":type formats: zxing.BarcodeFormat|zxing.BarcodeFormats\n" ":param formats: the format(s) to decode. If ``None``, decode all formats.\n" ":type try_rotate: bool\n" - ":param try_rotate: if ``True`` (the default), decoder searched for barcodes in any direction; \n" + ":param try_rotate: if ``True`` (the default), decoder searches for barcodes in any direction; \n" " if ``False``, it will not search for 90° / 270° rotated barcodes.\n" + ":type try_downscale: bool\n" + ":param try_downscale: if ``True`` (the default), decoder also scans downscaled versions of the input; \n" + " if ``False``, it will only search in the resolution provided.\n" ":type binarizer: zxing.Binarizer\n" ":param binarizer: the binarizer used to convert image before decoding barcodes.\n" " Defaults to :py:attr:`zxing.Binarizer.LocalAverage`." From fc4f2d5693b530e660ae7f9369cfffe8e6033347 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 25 Apr 2022 23:10:26 +0200 Subject: [PATCH 166/185] ci: drop python 3.7 and add 3.10 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f3f5a0046..f1654eb90d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,7 +92,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - python-version: [3.7, 3.8, 3.9] + python-version: [3.8, 3.9, 3.10] os: [ubuntu-latest, macos-latest, windows-latest] steps: From 5d7d95e3bf7eb7fb26fa7eda8c3f87a2214a76e0 Mon Sep 17 00:00:00 2001 From: axxel Date: Mon, 25 Apr 2022 23:21:00 +0200 Subject: [PATCH 167/185] ci: fix python builds ('3.10' but be put in quotes for the YAML parser) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f1654eb90d..79becdffc6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,7 +92,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - python-version: [3.8, 3.9, 3.10] + python-version: ['3.8', '3.9', '3.10'] os: [ubuntu-latest, macos-latest, windows-latest] steps: From 2d232b7b9b79d3dacf0627d5be4bd05a2ddb13ae Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 26 Apr 2022 09:40:04 +0200 Subject: [PATCH 168/185] multi-res: use min(width, height) in threshold check --- core/src/DecodeHints.h | 2 +- core/src/ReadBarcode.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/DecodeHints.h b/core/src/DecodeHints.h index 6b37c0a62a..bbe73d50e8 100644 --- a/core/src/DecodeHints.h +++ b/core/src/DecodeHints.h @@ -97,7 +97,7 @@ class DecodeHints /// Set to true if the input contains nothing but a single perfectly aligned barcode (generated image) ZX_PROPERTY(bool, isPure, setIsPure) - /// Image size (width or height) threshold at which to start multi-resolution scanning + /// Image size ( min(width, height) ) threshold at which to start downscaled scanning // WARNING: this API is experimental and may change/disappear ZX_PROPERTY(uint16_t, downscaleThreshold, setDownscaleThreshold) diff --git a/core/src/ReadBarcode.cpp b/core/src/ReadBarcode.cpp index 912b9763f4..90c33bdad5 100644 --- a/core/src/ReadBarcode.cpp +++ b/core/src/ReadBarcode.cpp @@ -83,7 +83,7 @@ class LumImagePyramid LumImagePyramid(const ImageView& iv, int threshold) { layers.push_back(iv); - while (threshold > 0 && std::max(layers.back().width(), layers.back().height()) > threshold) + while (threshold > 0 && std::min(layers.back().width(), layers.back().height()) > threshold) addLayer(); #if 0 // Reversing the layers means we'd start with the smallest. that can make sense if we are only looking for a From 457ad4ca643b86888a915b9a4f17f8059ef00642 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 26 Apr 2022 09:41:24 +0200 Subject: [PATCH 169/185] ReadBarcode: micro performance optimization in ExtractLum --- core/src/ReadBarcode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/ReadBarcode.cpp b/core/src/ReadBarcode.cpp index 90c33bdad5..d65dabcf3c 100644 --- a/core/src/ReadBarcode.cpp +++ b/core/src/ReadBarcode.cpp @@ -48,7 +48,7 @@ static LumImage ExtractLum(const ImageView& iv, P projection) auto* dst = res.data(); for(int y = 0; y < iv.height(); ++y) - for(int x = 0; x < iv.width(); ++x) + for(int x = 0, w = iv.width(); x < w; ++x) *dst++ = projection(iv.data(x, y)); return res; From 00d07c57b416d41afaa3e4a59acfcdc940387803 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 26 Apr 2022 16:07:43 +0200 Subject: [PATCH 170/185] ci: update python-build-dist (new cibuildwheel + cp310-* output) --- .github/workflows/python-build.yml | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/.github/workflows/python-build.yml b/.github/workflows/python-build.yml index 71dafce622..ca5e4f5e35 100644 --- a/.github/workflows/python-build.yml +++ b/.github/workflows/python-build.yml @@ -26,24 +26,23 @@ jobs: steps: - uses: actions/checkout@v2 - with: - submodules: recursive + - name: Set up Python uses: actions/setup-python@v2 - with: - python-version: 3.8 + + - name: Install cibuildwheel + run: python -m pip install cibuildwheel==2.4.0 - name: Build wheels - uses: joerick/cibuildwheel@v1.10.0 - with: - package-dir: ./wrappers/python - output-dir: ./wrappers/python/wheelhouse + run: python -m cibuildwheel --output-dir wheelhouse wrappers/python env: - CIBW_BUILD: cp37-* cp38-* cp39-* + CIBW_BUILD: cp38-* cp39-* cp310-* + # "Installing Python cp310" fails on macOS on 2022-04-26 -> disable + CIBW_SKIP: "*musllinux* *310-macos*" - uses: actions/upload-artifact@v2 with: - path: ./wrappers/python/wheelhouse/*.whl + path: ./wheelhouse/*.whl build-sdist: name: Build source distribution @@ -55,8 +54,6 @@ jobs: - name: Set up Python uses: actions/setup-python@v2 - with: - python-version: 3.8 - name: Build sdist working-directory: wrappers/python From f57b760162bb6aff3ba3e1f777f731b0dbfc61e5 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 27 Apr 2022 01:18:57 +0200 Subject: [PATCH 171/185] example: fix usage/help text of ZXingReader (red -> green outline color) --- example/ZXingReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/ZXingReader.cpp b/example/ZXingReader.cpp index 83eb038c29..15150e2897 100644 --- a/example/ZXingReader.cpp +++ b/example/ZXingReader.cpp @@ -45,7 +45,7 @@ static void PrintUsage(const char* exePath) << " -ispure Assume the image contains only a 'pure'/perfect code (faster)\n" << " -1 Print only file name, text and status on one line per file/barcode\n" << " -escape Escape non-graphical characters in angle brackets (ignored for -1 option, which always escapes)\n" - << " -pngout Write a copy of the input image with barcodes outlined by a red line\n" + << " -pngout Write a copy of the input image with barcodes outlined by a green line\n" << "\n" << "Supported formats are:\n"; for (auto f : BarcodeFormats::all()) { From 62e3b2cc715b2958d0ef169ee16680bbd310be5a Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 27 Apr 2022 01:35:23 +0200 Subject: [PATCH 172/185] python: prepare the 1.3.0 release --- wrappers/python/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrappers/python/setup.py b/wrappers/python/setup.py index 4aef419662..873767a51d 100644 --- a/wrappers/python/setup.py +++ b/wrappers/python/setup.py @@ -59,7 +59,7 @@ def build_extension(self, ext): # "local_scheme": "no-local-version", # "tag_regex": "v?([0-9]+.[0-9]+.[0-9]+)", # }, - version='1.2.0', + version='1.3.0', description='Python bindings for the zxing-cpp barcode library', long_description=long_description, long_description_content_type="text/markdown", From 0f2ed91b7d41772f48afb575560604cc7ee992d9 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 27 Apr 2022 01:49:52 +0200 Subject: [PATCH 173/185] README: minor text improvements and clarifications --- README.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 9b6132fb70..58a274cf5f 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ It was originally ported from the Java [ZXing Library](https://github.com/zxing/ ## Features -* In pure C++17, no third-party dependencies +* In pure C++17, no third-party dependencies (for the library) * Stateless, thread-safe readers/scanners and writers/generators * Wrapper/Bindings for: * WinRT @@ -27,7 +27,7 @@ It was originally ported from the Java [ZXing Library](https://github.com/zxing/ | DataBar | ITF | MaxiCode (beta) | | DataBar Expanded | -Note: DataBar used to be called RSS. +Note: DataBar used to be called RSS. DataBar is not supported for writing. ## Getting Started @@ -57,20 +57,21 @@ PM> Install-Package huycn.zxingcpp.winrt ## Build Instructions ### Standard setup on Windows/macOS/Linux -1. Make sure [CMake](https://cmake.org) version 3.10 or newer is installed. -2. Make sure a C++17 compliant compiler is installed (minimum VS 2019 16.8 / gcc 7 / clang 5) +1. Make sure [CMake](https://cmake.org) version 3.14 or newer is installed. +2. Make sure a C++17 compliant compiler is installed (minimum VS 2019 16.8 / gcc 7 / clang 5). 3. See the cmake `BUILD_...` options to enable the testing code, python wrapper, etc. ### Windows Universal Platform -1. Download and install [CMake](https://cmake.org) 3.4 or more recent if it's not already installed. -2. Edit the file [`wrappers/winrt/BuildWinCom.bat`](wrappers/winrt/BuildWinCom.bat) to adjust the path to your CMake installation. -3. Double-click on the batch script to run it. -4. If the build succeeds, it will put the results in the folder UAP which is ready-to-use SDK extension. +1. Make sure [CMake](https://cmake.org) version 3.4 or newer is installed. +2. Make sure a C++17 compliant compiler is installed (minimum VS 2019 16.8). +3. Edit the file [`wrappers/winrt/BuildWinCom.bat`](wrappers/winrt/BuildWinCom.bat) to adjust the path to your CMake installation. +4. Double-click on the batch script to run it. +5. If the build succeeds, it will put the results in the folder UAP which is ready-to-use SDK extension. ### Android 1. Install AndroidStudio including NDK and CMake (see 'SDK Tools'). 2. Open the project in folder [wrappers/android](wrappers/android). -3. The project contains 2 modules: `zxingcpp` is the wrapper library, `app` is the demo app using `zxingcpp` +3. The project contains 2 modules: `zxingcpp` is the wrapper library, `app` is the demo app using `zxingcpp`. ### WebAssembly 1. [Install Emscripten](https://kripken.github.io/emscripten-site/docs/getting_started/) if not done already. From c92fb9c87114800f80260530ab148a1f634f284b Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 27 Apr 2022 02:25:15 +0200 Subject: [PATCH 174/185] python: update demo code to use new `read_barcodes()` function --- wrappers/python/README.md | 11 ++++++----- wrappers/python/demo_reader.py | 8 ++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/wrappers/python/README.md b/wrappers/python/README.md index b6d43c81c8..00bd382893 100644 --- a/wrappers/python/README.md +++ b/wrappers/python/README.md @@ -21,9 +21,10 @@ import cv2 import zxingcpp img = cv2.imread('myimage.png') -result = zxingcpp.read_barcode(img) -if result.valid: - print("Found barcode with value '{}' (format: {})".format(result.text, str(result.format))) -else: - print("could not read barcode") +results = zxingcpp.read_barcodes(img) +for result in results: + print("Found barcode:\n Text: '{}'\n Format: {}\n Position: {}" + .format(result.text, result.format, result.position)) +if len(results) == 0: + print("Could not find any barcode.") ``` diff --git a/wrappers/python/demo_reader.py b/wrappers/python/demo_reader.py index 93de1f3a5c..d8e95cb9ef 100644 --- a/wrappers/python/demo_reader.py +++ b/wrappers/python/demo_reader.py @@ -2,9 +2,9 @@ from PIL import Image img = Image.open(sys.argv[1]) -result = zxingcpp.read_barcode(img) -if result.valid: +results = zxingcpp.read_barcodes(img) +for result in results: print("Found barcode:\n Text: '{}'\n Format: {}\n Position: {}" .format(result.text, result.format, result.position)) -else: - print("Could not read barcode") +if len(results) == 0: + print("Could not find any barcode.") From 45cf13287d08b99d6c544643f4059e8da9a3e97f Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 27 Apr 2022 02:33:50 +0200 Subject: [PATCH 175/185] python: add test for new `read_barcodes()` function --- wrappers/python/test.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/wrappers/python/test.py b/wrappers/python/test.py index cfcc760b9a..350d08cd0e 100644 --- a/wrappers/python/test.py +++ b/wrappers/python/test.py @@ -51,6 +51,14 @@ def test_write_read_oned_cycle(self): self.check_res(res, format, text) self.assertEqual(res.position.top_left.x, 61) + def test_write_read_multi_cycle(self): + format = BF.QRCode + text = "I have the best words." + img = zxingcpp.write_barcode(format, text) + + res = zxingcpp.read_barcodes(img)[0] + self.check_res(res, format, text) + def test_failed_read(self): import numpy as np res = zxingcpp.read_barcode( From d3acd6d6e7b188e5e8746a34cddf06ca8a68bece Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 27 Apr 2022 10:09:43 +0200 Subject: [PATCH 176/185] multi-res: add DecodeHints::downscaleFactor property For a discussion see #282. --- core/src/DecodeHints.h | 5 +++++ core/src/ReadBarcode.cpp | 9 ++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/core/src/DecodeHints.h b/core/src/DecodeHints.h index bbe73d50e8..d4ef5e6e32 100644 --- a/core/src/DecodeHints.h +++ b/core/src/DecodeHints.h @@ -64,6 +64,7 @@ class DecodeHints std::vector _allowedLengths; BarcodeFormats _formats = BarcodeFormat::None; uint16_t _downscaleThreshold = 500; + uint8_t _downscaleFactor = 3; uint8_t _minLineCount = 2; uint8_t _maxNumberOfSymbols = 0xff; @@ -101,6 +102,10 @@ class DecodeHints // WARNING: this API is experimental and may change/disappear ZX_PROPERTY(uint16_t, downscaleThreshold, setDownscaleThreshold) + /// Scale factor used during downscaling, meaningful values are 2, 3 and 4 + // WARNING: this API is experimental and may change/disappear + ZX_PROPERTY(uint8_t, downscaleFactor, setDownscaleFactor) + /// The number of scan lines in a 1D barcode that have to be equal to accept the result, default is 2 ZX_PROPERTY(uint8_t, minLineCount, setMinLineCount) diff --git a/core/src/ReadBarcode.cpp b/core/src/ReadBarcode.cpp index d65dabcf3c..add15b2db7 100644 --- a/core/src/ReadBarcode.cpp +++ b/core/src/ReadBarcode.cpp @@ -56,7 +56,7 @@ static LumImage ExtractLum(const ImageView& iv, P projection) class LumImagePyramid { - static constexpr int N = 3; + int N = 3; std::vector buffers; void addLayer() @@ -80,8 +80,11 @@ class LumImagePyramid public: std::vector layers; - LumImagePyramid(const ImageView& iv, int threshold) + LumImagePyramid(const ImageView& iv, int threshold, int factor) : N(factor) { + if (factor < 2) + throw std::invalid_argument("Invalid DecodeHints::downscaleFactor"); + layers.push_back(iv); while (threshold > 0 && std::min(layers.back().width(), layers.back().height()) > threshold) addLayer(); @@ -148,7 +151,7 @@ Results ReadBarcodes(const ImageView& _iv, const DecodeHints& hints) if (hints.isPure()) return {reader.read(*CreateBitmap(hints.binarizer(), iv))}; - LumImagePyramid pyramid(iv, hints.downscaleThreshold() * hints.tryDownscale()); + LumImagePyramid pyramid(iv, hints.downscaleThreshold() * hints.tryDownscale(), hints.downscaleFactor()); Results results; int maxSymbols = hints.maxNumberOfSymbols(); From 513ae351c17015de633f649d2de36608b7e15c39 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 27 Apr 2022 14:15:15 +0200 Subject: [PATCH 177/185] style: add .clang-format configuration (not applied consistently, yet) --- .clang-format | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000..5520e59df9 --- /dev/null +++ b/.clang-format @@ -0,0 +1,32 @@ +--- +Language: Cpp +Standard: c++17 +BasedOnStyle: LLVM + +IndentWidth: 4 +TabWidth: 4 +UseTab: ForContinuationAndIndentation # ForIndentation + +AccessModifierOffset: -4 +BreakBeforeBraces: Mozilla +ColumnLimit: 120 + +#AlignConsecutiveAssignments: true +AlignEscapedNewlines: DontAlign +AlignTrailingComments: true +AllowShortCaseLabelsOnASingleLine: true + +AllowShortFunctionsOnASingleLine: Inline +#AllowShortLambdasOnASingleLine: Inline + +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeBraces: Mozilla +BreakBeforeTernaryOperators: true +ConstructorInitializerAllOnOneLineOrOnePerLine: false +FixNamespaceComments: true +IncludeBlocks: Regroup +KeepEmptyLinesAtTheStartOfBlocks: false +PointerAlignment: Left +ReflowComments: true +SortIncludes: true From e3556b544be6eb767d4f14714f80295a76ab4ba4 Mon Sep 17 00:00:00 2001 From: axxel Date: Wed, 27 Apr 2022 14:17:49 +0200 Subject: [PATCH 178/185] style: make formatting of switch statement more consistent --- core/src/aztec/AZHighLevelEncoder.cpp | 22 +-- core/src/datamatrix/DMHighLevelEncoder.cpp | 34 ++--- core/src/datamatrix/DMSymbolInfo.cpp | 36 ++--- core/src/maxicode/MCDecoder.cpp | 74 +++++----- core/src/oned/ODCode128Writer.cpp | 49 +++---- core/src/pdf417/PDFDecodedBitStreamParser.cpp | 114 +++++++-------- core/src/pdf417/PDFHighLevelEncoder.cpp | 134 ++++++++---------- core/src/qrcode/QRCodecMode.cpp | 10 +- core/src/qrcode/QREncoder.cpp | 19 +-- core/src/qrcode/QRErrorCorrectionLevel.cpp | 13 +- 10 files changed, 211 insertions(+), 294 deletions(-) diff --git a/core/src/aztec/AZHighLevelEncoder.cpp b/core/src/aztec/AZHighLevelEncoder.cpp index 2a473f180b..f27bac3b5f 100644 --- a/core/src/aztec/AZHighLevelEncoder.cpp +++ b/core/src/aztec/AZHighLevelEncoder.cpp @@ -367,28 +367,18 @@ HighLevelEncoder::Encode(const std::string& text) int pairCode; int nextChar = index + 1 < Size(text) ? text[index + 1] : 0; switch (text[index]) { - case '\r': - pairCode = nextChar == '\n' ? 2 : 0; - break; - case '.': - pairCode = nextChar == ' ' ? 3 : 0; - break; - case ',': - pairCode = nextChar == ' ' ? 4 : 0; - break; - case ':': - pairCode = nextChar == ' ' ? 5 : 0; - break; - default: - pairCode = 0; + case '\r': pairCode = nextChar == '\n' ? 2 : 0; break; + case '.': pairCode = nextChar == ' ' ? 3 : 0; break; + case ',': pairCode = nextChar == ' ' ? 4 : 0; break; + case ':': pairCode = nextChar == ' ' ? 5 : 0; break; + default: pairCode = 0; } if (pairCode > 0) { // We have one of the four special PUNCT pairs. Treat them specially. // Get a new set of states for the two new characters. states = UpdateStateListForPair(states, index, pairCode); index++; - } - else { + } else { // Get a new set of states for the new character. states = UpdateStateListForChar(states, text, index); } diff --git a/core/src/datamatrix/DMHighLevelEncoder.cpp b/core/src/datamatrix/DMHighLevelEncoder.cpp index dc44a53a4c..0b43f6a7eb 100644 --- a/core/src/datamatrix/DMHighLevelEncoder.cpp +++ b/core/src/datamatrix/DMHighLevelEncoder.cpp @@ -607,26 +607,16 @@ namespace X12Encoder { static int EncodeChar(int c, std::string& sb) { switch (c) { - case '\r': - sb.push_back('\0'); - break; - case '*': - sb.push_back('\1'); - break; - case '>': - sb.push_back('\2'); - break; - case ' ': - sb.push_back('\3'); - break; + case '\r': sb.push_back('\0'); break; + case '*': sb.push_back('\1'); break; + case '>': sb.push_back('\2'); break; + case ' ': sb.push_back('\3'); break; default: if (c >= '0' && c <= '9') { sb.push_back((char)(c - 48 + 4)); - } - else if (c >= 'A' && c <= 'Z') { + } else if (c >= 'A' && c <= 'Z') { sb.push_back((char)(c - 65 + 14)); - } - else { + } else { throw std::invalid_argument("Illegal character: " + ToHexString(c)); } break; @@ -915,12 +905,12 @@ ByteArray Encode(const std::wstring& msg, SymbolShape shape, int minWidth, int m int encodingMode = ASCII_ENCODATION; //Default mode while (context.hasMoreCharacters()) { switch (encodingMode) { - case ASCII_ENCODATION: ASCIIEncoder::EncodeASCII(context); break; - case C40_ENCODATION: C40Encoder::EncodeC40(context); break; - case TEXT_ENCODATION: DMTextEncoder::EncodeText(context); break; - case X12_ENCODATION: X12Encoder::EncodeX12(context); break; - case EDIFACT_ENCODATION: EdifactEncoder::EncodeEdifact(context); break; - case BASE256_ENCODATION: Base256Encoder::EncodeBase256(context); break; + case ASCII_ENCODATION: ASCIIEncoder::EncodeASCII(context); break; + case C40_ENCODATION: C40Encoder::EncodeC40(context); break; + case TEXT_ENCODATION: DMTextEncoder::EncodeText(context); break; + case X12_ENCODATION: X12Encoder::EncodeX12(context); break; + case EDIFACT_ENCODATION: EdifactEncoder::EncodeEdifact(context); break; + case BASE256_ENCODATION: Base256Encoder::EncodeBase256(context); break; } if (context.newEncoding() >= 0) { encodingMode = context.newEncoding(); diff --git a/core/src/datamatrix/DMSymbolInfo.cpp b/core/src/datamatrix/DMSymbolInfo.cpp index 97ac6c0b90..344a0d4832 100644 --- a/core/src/datamatrix/DMSymbolInfo.cpp +++ b/core/src/datamatrix/DMSymbolInfo.cpp @@ -129,36 +129,24 @@ int SymbolInfo::horizontalDataRegions() const { switch (_dataRegions) { - case 1: - return 1; - case 2: - return 2; - case 4: - return 2; - case 16: - return 4; - case 36: - return 6; - default: - throw std::out_of_range("Cannot handle this number of data regions"); + case 1: return 1; + case 2: return 2; + case 4: return 2; + case 16: return 4; + case 36: return 6; + default: throw std::out_of_range("Cannot handle this number of data regions"); } } int SymbolInfo::verticalDataRegions() const { switch (_dataRegions) { - case 1: - return 1; - case 2: - return 1; - case 4: - return 2; - case 16: - return 4; - case 36: - return 6; - default: - throw std::out_of_range("Cannot handle this number of data regions"); + case 1: return 1; + case 2: return 1; + case 4: return 2; + case 16: return 4; + case 36: return 6; + default: throw std::out_of_range("Cannot handle this number of data regions"); } } diff --git a/core/src/maxicode/MCDecoder.cpp b/core/src/maxicode/MCDecoder.cpp index 5c4a28b71d..15b9a7bfa5 100644 --- a/core/src/maxicode/MCDecoder.cpp +++ b/core/src/maxicode/MCDecoder.cpp @@ -294,27 +294,22 @@ namespace DecodedBitStreamParser result.reserve(144); StructuredAppendInfo sai; switch (mode) { - case 2: - case 3: { - auto postcode = mode == 2 ? ToString(GetPostCode2(bytes), GetPostCode2Length(bytes)) : GetPostCode3(bytes); - auto country = ToString(GetCountry(bytes), 3); - auto service = ToString(GetServiceClass(bytes), 3); - result.append(GetMessage(bytes, 10, 84, characterSet, sai)); - if (result.size() >= 9 && result.compare(0, 7, L"[)>\u001E01\u001D") == 0) { // "[)>" + RS + "01" + GS - result.insert(9, TextDecoder::FromLatin1(postcode + GS + country + GS + service + GS)); - } - else { - result.insert(0, TextDecoder::FromLatin1(postcode + GS + country + GS + service + GS)); - } - break; + case 2: + case 3: { + auto postcode = mode == 2 ? ToString(GetPostCode2(bytes), GetPostCode2Length(bytes)) : GetPostCode3(bytes); + auto country = ToString(GetCountry(bytes), 3); + auto service = ToString(GetServiceClass(bytes), 3); + result.append(GetMessage(bytes, 10, 84, characterSet, sai)); + if (result.size() >= 9 && result.compare(0, 7, L"[)>\u001E01\u001D") == 0) { // "[)>" + RS + "01" + GS + result.insert(9, TextDecoder::FromLatin1(postcode + GS + country + GS + service + GS)); + } else { + result.insert(0, TextDecoder::FromLatin1(postcode + GS + country + GS + service + GS)); } - case 4: - case 6: - result.append(GetMessage(bytes, 1, 93, characterSet, sai)); - break; - case 5: - result.append(GetMessage(bytes, 1, 77, characterSet, sai)); - break; + break; + } + case 4: + case 6: result.append(GetMessage(bytes, 1, 93, characterSet, sai)); break; + case 5: result.append(GetMessage(bytes, 1, 77, characterSet, sai)); break; } // As converting character set ECIs ourselves and ignoring/skipping non-character ECIs, not using modifiers @@ -350,27 +345,24 @@ Decoder::Decode(const BitMatrix& bits, const std::string& characterSet) int mode = codewords[0] & 0x0F; ByteArray datawords; switch (mode) { - case 2: // Structured Carrier Message (numeric postcode) - case 3: // Structured Carrier Message (alphanumeric postcode) - case 4: // Standard Symbol - case 6: // Reader Programming - if (CorrectErrors(codewords, 20, 84, 40, EVEN) && CorrectErrors(codewords, 20, 84, 40, ODD)) { - datawords.resize(94, 0); - } - else { - return DecodeStatus::ChecksumError; - } - break; - case 5: // Full ECC - if (CorrectErrors(codewords, 20, 68, 56, EVEN) && CorrectErrors(codewords, 20, 68, 56, ODD)) { - datawords.resize(78, 0); - } - else { - return DecodeStatus::ChecksumError; - } - break; - default: - return DecodeStatus::FormatError; + case 2: // Structured Carrier Message (numeric postcode) + case 3: // Structured Carrier Message (alphanumeric postcode) + case 4: // Standard Symbol + case 6: // Reader Programming + if (CorrectErrors(codewords, 20, 84, 40, EVEN) && CorrectErrors(codewords, 20, 84, 40, ODD)) { + datawords.resize(94, 0); + } else { + return DecodeStatus::ChecksumError; + } + break; + case 5: // Full ECC + if (CorrectErrors(codewords, 20, 68, 56, EVEN) && CorrectErrors(codewords, 20, 68, 56, ODD)) { + datawords.resize(78, 0); + } else { + return DecodeStatus::ChecksumError; + } + break; + default: return DecodeStatus::FormatError; } std::copy_n(codewords.begin(), 10, datawords.begin()); diff --git a/core/src/oned/ODCode128Writer.cpp b/core/src/oned/ODCode128Writer.cpp index 9c3f2d2f51..24a097e3bf 100644 --- a/core/src/oned/ODCode128Writer.cpp +++ b/core/src/oned/ODCode128Writer.cpp @@ -155,16 +155,15 @@ Code128Writer::encode(const std::wstring& contents, int width, int height) const for (int i = 0; i < length; ++i) { int c = contents[i]; switch (c) { - case ESCAPE_FNC_1: - case ESCAPE_FNC_2: - case ESCAPE_FNC_3: - case ESCAPE_FNC_4: - break; - default: - if (c > 127) { - // support for FNC4 isn't implemented, no full Latin-1 character set available at the moment - throw std::invalid_argument(std::string("Bad character in input: ") + static_cast(c)); - } + case ESCAPE_FNC_1: + case ESCAPE_FNC_2: + case ESCAPE_FNC_3: + case ESCAPE_FNC_4: break; + default: + if (c > 127) { + // support for FNC4 isn't implemented, no full Latin-1 character set available at the moment + throw std::invalid_argument(std::string("Bad character in input: ") + static_cast(c)); + } } } @@ -184,23 +183,10 @@ Code128Writer::encode(const std::wstring& contents, int width, int height) const // Encode the current character // First handle escapes switch (contents[position]) { - case ESCAPE_FNC_1: - patternIndex = CODE_FNC_1; - break; - case ESCAPE_FNC_2: - patternIndex = CODE_FNC_2; - break; - case ESCAPE_FNC_3: - patternIndex = CODE_FNC_3; - break; - case ESCAPE_FNC_4: - if (codeSet == CODE_CODE_A) { - patternIndex = CODE_FNC_4_A; - } - else { - patternIndex = CODE_FNC_4_B; - } - break; + case ESCAPE_FNC_1: patternIndex = CODE_FNC_1; break; + case ESCAPE_FNC_2: patternIndex = CODE_FNC_2; break; + case ESCAPE_FNC_3: patternIndex = CODE_FNC_3; break; + case ESCAPE_FNC_4: patternIndex = (codeSet == CODE_CODE_A) ? CODE_FNC_4_A : CODE_FNC_4_B; break; default: // Then handle normal characters otherwise if (codeSet == CODE_CODE_A) { @@ -209,12 +195,11 @@ Code128Writer::encode(const std::wstring& contents, int width, int height) const // everything below a space character comes behind the underscore in the code patterns table patternIndex += '`'; } - } - else if (codeSet == CODE_CODE_B) { + } else if (codeSet == CODE_CODE_B) { patternIndex = contents[position] - ' '; - } - else { // CODE_CODE_C - patternIndex = (contents[position] - '0') * 10 + (position+1 < length ? contents[position+1] - '0' : 0); + } else { // CODE_CODE_C + patternIndex = + (contents[position] - '0') * 10 + (position + 1 < length ? contents[position + 1] - '0' : 0); position++; // Also incremented below } } diff --git a/core/src/pdf417/PDFDecodedBitStreamParser.cpp b/core/src/pdf417/PDFDecodedBitStreamParser.cpp index 7bfc311703..51b1f8f088 100644 --- a/core/src/pdf417/PDFDecodedBitStreamParser.cpp +++ b/core/src/pdf417/PDFDecodedBitStreamParser.cpp @@ -91,9 +91,7 @@ static bool TerminatesCompaction(int code) case BYTE_COMPACTION_MODE_LATCH_6: case BEGIN_MACRO_PDF417_CONTROL_BLOCK: case BEGIN_MACRO_PDF417_OPTIONAL_FIELD: - case MACRO_PDF417_TERMINATOR: - return true; - break; + case MACRO_PDF417_TERMINATOR: return true; } return false; } @@ -683,70 +681,64 @@ DecodeStatus DecodeMacroBlock(const std::vector& codewords, int codeIndex, while (codeIndex < codewords[0]) { switch (codewords[codeIndex]) { - case BEGIN_MACRO_PDF417_OPTIONAL_FIELD: { - codeIndex++; - if (codeIndex >= codewords[0]) { - break; - } - switch (codewords[codeIndex]) { - case MACRO_PDF417_OPTIONAL_FIELD_FILE_NAME: { - std::string fileName; - codeIndex = DecodeMacroOptionalTextField(status, codewords, codeIndex + 1, fileName); - resultMetadata.setFileName(fileName); - break; - } - case MACRO_PDF417_OPTIONAL_FIELD_SENDER: { - std::string sender; - codeIndex = DecodeMacroOptionalTextField(status, codewords, codeIndex + 1, sender); - resultMetadata.setSender(sender); - break; - } - case MACRO_PDF417_OPTIONAL_FIELD_ADDRESSEE: { - std::string addressee; - codeIndex = DecodeMacroOptionalTextField(status, codewords, codeIndex + 1, addressee); - resultMetadata.setAddressee(addressee); - break; - } - case MACRO_PDF417_OPTIONAL_FIELD_SEGMENT_COUNT: { - uint64_t segmentCount; - codeIndex = DecodeMacroOptionalNumericField(status, codewords, codeIndex + 1, segmentCount); - resultMetadata.setSegmentCount(static_cast(segmentCount)); - break; - } - case MACRO_PDF417_OPTIONAL_FIELD_TIME_STAMP: { - uint64_t timestamp; - codeIndex = DecodeMacroOptionalNumericField(status, codewords, codeIndex + 1, timestamp); - resultMetadata.setTimestamp(timestamp); - break; - } - case MACRO_PDF417_OPTIONAL_FIELD_CHECKSUM: { - uint64_t checksum; - codeIndex = DecodeMacroOptionalNumericField(status, codewords, codeIndex + 1, checksum); - resultMetadata.setChecksum(static_cast(checksum)); - break; - } - case MACRO_PDF417_OPTIONAL_FIELD_FILE_SIZE: { - uint64_t fileSize; - codeIndex = DecodeMacroOptionalNumericField(status, codewords, codeIndex + 1, fileSize); - resultMetadata.setFileSize(fileSize); - break; - } - default: { - status = DecodeStatus::FormatError; - break; - } - } + case BEGIN_MACRO_PDF417_OPTIONAL_FIELD: { + codeIndex++; + if (codeIndex >= codewords[0]) { break; } - case MACRO_PDF417_TERMINATOR: { - codeIndex++; - resultMetadata.setLastSegment(true); + switch (codewords[codeIndex]) { + case MACRO_PDF417_OPTIONAL_FIELD_FILE_NAME: { + std::string fileName; + codeIndex = DecodeMacroOptionalTextField(status, codewords, codeIndex + 1, fileName); + resultMetadata.setFileName(fileName); break; } - default: { - status = DecodeStatus::FormatError; + case MACRO_PDF417_OPTIONAL_FIELD_SENDER: { + std::string sender; + codeIndex = DecodeMacroOptionalTextField(status, codewords, codeIndex + 1, sender); + resultMetadata.setSender(sender); + break; + } + case MACRO_PDF417_OPTIONAL_FIELD_ADDRESSEE: { + std::string addressee; + codeIndex = DecodeMacroOptionalTextField(status, codewords, codeIndex + 1, addressee); + resultMetadata.setAddressee(addressee); break; } + case MACRO_PDF417_OPTIONAL_FIELD_SEGMENT_COUNT: { + uint64_t segmentCount; + codeIndex = DecodeMacroOptionalNumericField(status, codewords, codeIndex + 1, segmentCount); + resultMetadata.setSegmentCount(static_cast(segmentCount)); + break; + } + case MACRO_PDF417_OPTIONAL_FIELD_TIME_STAMP: { + uint64_t timestamp; + codeIndex = DecodeMacroOptionalNumericField(status, codewords, codeIndex + 1, timestamp); + resultMetadata.setTimestamp(timestamp); + break; + } + case MACRO_PDF417_OPTIONAL_FIELD_CHECKSUM: { + uint64_t checksum; + codeIndex = DecodeMacroOptionalNumericField(status, codewords, codeIndex + 1, checksum); + resultMetadata.setChecksum(static_cast(checksum)); + break; + } + case MACRO_PDF417_OPTIONAL_FIELD_FILE_SIZE: { + uint64_t fileSize; + codeIndex = DecodeMacroOptionalNumericField(status, codewords, codeIndex + 1, fileSize); + resultMetadata.setFileSize(fileSize); + break; + } + default: status = DecodeStatus::FormatError; break; + } + break; + } + case MACRO_PDF417_TERMINATOR: { + codeIndex++; + resultMetadata.setLastSegment(true); + break; + } + default: status = DecodeStatus::FormatError; break; } if (StatusIsError(status)) { return status; diff --git a/core/src/pdf417/PDFHighLevelEncoder.cpp b/core/src/pdf417/PDFHighLevelEncoder.cpp index 6bd2f4a6ab..c37b41c11b 100644 --- a/core/src/pdf417/PDFHighLevelEncoder.cpp +++ b/core/src/pdf417/PDFHighLevelEncoder.cpp @@ -231,80 +231,70 @@ static int EncodeText(const std::wstring& msg, int startpos, int count, int subm while (true) { int ch = msg[startpos + idx]; switch (submode) { - case SUBMODE_ALPHA: - if (IsAlphaUpper(ch)) { - tmp.push_back(ch == ' ' ? 26 : (ch - 65)); //space - } - else if (IsAlphaLower(ch)) { - submode = SUBMODE_LOWER; - tmp.push_back(27); //ll - continue; - } - else if (IsMixed(ch)) { - submode = SUBMODE_MIXED; - tmp.push_back(28); //ml - continue; - } - else { - tmp.push_back(29); //ps - tmp.push_back(PUNCTUATION[ch]); - } - break; - case SUBMODE_LOWER: - if (IsAlphaLower(ch)) { - tmp.push_back(ch == ' ' ? 26 : (ch - 97)); //space - } - else if (IsAlphaUpper(ch)) { - tmp.push_back(27); //as - tmp.push_back(ch - 65); - //space cannot happen here, it is also in "Lower" - } - else if (IsMixed(ch)) { - submode = SUBMODE_MIXED; - tmp.push_back(28); //ml - continue; - } - else { - tmp.push_back(29); //ps - tmp.push_back(PUNCTUATION[ch]); - } - break; - case SUBMODE_MIXED: - if (IsMixed(ch)) { - tmp.push_back(MIXED[ch]); - } - else if (IsAlphaUpper(ch)) { - submode = SUBMODE_ALPHA; - tmp.push_back(28); //al - continue; - } - else if (IsAlphaLower(ch)) { - submode = SUBMODE_LOWER; - tmp.push_back(27); //ll - continue; - } - else { - if (startpos + idx + 1 < count) { - int next = msg[startpos + idx + 1]; - if (IsPunctuation(next)) { - submode = SUBMODE_PUNCTUATION; - tmp.push_back(25); //pl - continue; - } + case SUBMODE_ALPHA: + if (IsAlphaUpper(ch)) { + tmp.push_back(ch == ' ' ? 26 : (ch - 65)); // space + } else if (IsAlphaLower(ch)) { + submode = SUBMODE_LOWER; + tmp.push_back(27); // ll + continue; + } else if (IsMixed(ch)) { + submode = SUBMODE_MIXED; + tmp.push_back(28); // ml + continue; + } else { + tmp.push_back(29); // ps + tmp.push_back(PUNCTUATION[ch]); + } + break; + case SUBMODE_LOWER: + if (IsAlphaLower(ch)) { + tmp.push_back(ch == ' ' ? 26 : (ch - 97)); // space + } else if (IsAlphaUpper(ch)) { + tmp.push_back(27); // as + tmp.push_back(ch - 65); + // space cannot happen here, it is also in "Lower" + } else if (IsMixed(ch)) { + submode = SUBMODE_MIXED; + tmp.push_back(28); // ml + continue; + } else { + tmp.push_back(29); // ps + tmp.push_back(PUNCTUATION[ch]); + } + break; + case SUBMODE_MIXED: + if (IsMixed(ch)) { + tmp.push_back(MIXED[ch]); + } else if (IsAlphaUpper(ch)) { + submode = SUBMODE_ALPHA; + tmp.push_back(28); // al + continue; + } else if (IsAlphaLower(ch)) { + submode = SUBMODE_LOWER; + tmp.push_back(27); // ll + continue; + } else { + if (startpos + idx + 1 < count) { + int next = msg[startpos + idx + 1]; + if (IsPunctuation(next)) { + submode = SUBMODE_PUNCTUATION; + tmp.push_back(25); // pl + continue; } - tmp.push_back(29); //ps - tmp.push_back(PUNCTUATION[ch]); - } - break; - default: //SUBMODE_PUNCTUATION - if (IsPunctuation(ch)) { - tmp.push_back(PUNCTUATION[ch]); - } - else { - submode = SUBMODE_ALPHA; - tmp.push_back(29); //al - continue; } + tmp.push_back(29); // ps + tmp.push_back(PUNCTUATION[ch]); + } + break; + default: // SUBMODE_PUNCTUATION + if (IsPunctuation(ch)) { + tmp.push_back(PUNCTUATION[ch]); + } else { + submode = SUBMODE_ALPHA; + tmp.push_back(29); // al + continue; + } } idx++; if (idx >= count) { diff --git a/core/src/qrcode/QRCodecMode.cpp b/core/src/qrcode/QRCodecMode.cpp index 05e32038a9..a64e6bd9c6 100644 --- a/core/src/qrcode/QRCodecMode.cpp +++ b/core/src/qrcode/QRCodecMode.cpp @@ -44,12 +44,12 @@ int CharacterCountBits(CodecMode mode, const Version& version) i = 2; switch (mode) { - case CodecMode::NUMERIC: return std::array{10, 12, 14}[i]; + case CodecMode::NUMERIC: return std::array{10, 12, 14}[i]; case CodecMode::ALPHANUMERIC: return std::array{9, 11, 13}[i]; - case CodecMode::BYTE: return std::array{8, 16, 16}[i]; - case CodecMode::KANJI: [[fallthrough]]; - case CodecMode::HANZI: return std::array{8, 10, 12}[i]; - default: return 0; + case CodecMode::BYTE: return std::array{8, 16, 16}[i]; + case CodecMode::KANJI: [[fallthrough]]; + case CodecMode::HANZI: return std::array{8, 10, 12}[i]; + default: return 0; } } diff --git a/core/src/qrcode/QREncoder.cpp b/core/src/qrcode/QREncoder.cpp index 9c05877482..297da96a31 100644 --- a/core/src/qrcode/QREncoder.cpp +++ b/core/src/qrcode/QREncoder.cpp @@ -250,20 +250,11 @@ ZXING_EXPORT_TEST_ONLY void AppendBytes(const std::wstring& content, CodecMode mode, CharacterSet encoding, BitArray& bits) { switch (mode) { - case CodecMode::NUMERIC: - AppendNumericBytes(content, bits); - break; - case CodecMode::ALPHANUMERIC: - AppendAlphanumericBytes(content, bits); - break; - case CodecMode::BYTE: - Append8BitBytes(content, encoding, bits); - break; - case CodecMode::KANJI: - AppendKanjiBytes(content, bits); - break; - default: - throw std::invalid_argument("Invalid mode: " + std::to_string(static_cast(mode))); + case CodecMode::NUMERIC: AppendNumericBytes(content, bits); break; + case CodecMode::ALPHANUMERIC: AppendAlphanumericBytes(content, bits); break; + case CodecMode::BYTE: Append8BitBytes(content, encoding, bits); break; + case CodecMode::KANJI: AppendKanjiBytes(content, bits); break; + default: throw std::invalid_argument("Invalid mode: " + std::to_string(static_cast(mode))); } } diff --git a/core/src/qrcode/QRErrorCorrectionLevel.cpp b/core/src/qrcode/QRErrorCorrectionLevel.cpp index 80890161f0..7ff749a515 100644 --- a/core/src/qrcode/QRErrorCorrectionLevel.cpp +++ b/core/src/qrcode/QRErrorCorrectionLevel.cpp @@ -30,13 +30,12 @@ const wchar_t* ToString(ErrorCorrectionLevel l) ErrorCorrectionLevel ECLevelFromString(const char* str) { - switch (str[0]) - { - case 'L': return ErrorCorrectionLevel::Low; - case 'M': return ErrorCorrectionLevel::Medium; - case 'Q': return ErrorCorrectionLevel::Quality; - case 'H': return ErrorCorrectionLevel::High; - default: return ErrorCorrectionLevel::Invalid; + switch (str[0]) { + case 'L': return ErrorCorrectionLevel::Low; + case 'M': return ErrorCorrectionLevel::Medium; + case 'Q': return ErrorCorrectionLevel::Quality; + case 'H': return ErrorCorrectionLevel::High; + default: return ErrorCorrectionLevel::Invalid; } } From 00c649898c995b1a3076edc9dfa5dbfc4d405551 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 3 May 2022 01:38:30 +0200 Subject: [PATCH 179/185] c++: remove unused variable in PDFScanningDecoder --- core/src/pdf417/PDFDecodedBitStreamParser.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/pdf417/PDFDecodedBitStreamParser.cpp b/core/src/pdf417/PDFDecodedBitStreamParser.cpp index 51b1f8f088..ab61c4c6c8 100644 --- a/core/src/pdf417/PDFDecodedBitStreamParser.cpp +++ b/core/src/pdf417/PDFDecodedBitStreamParser.cpp @@ -668,8 +668,9 @@ DecodeStatus DecodeMacroBlock(const std::vector& codewords, int codeIndex, // the fileId using text compaction, so in those cases the fileId will appear mangled. std::ostringstream fileId; fileId.fill('0'); - for (int i = 0; codeIndex < codewords[0] && codewords[codeIndex] != MACRO_PDF417_TERMINATOR - && codewords[codeIndex] != BEGIN_MACRO_PDF417_OPTIONAL_FIELD; i++, codeIndex++) { + for (; codeIndex < codewords[0] && codewords[codeIndex] != MACRO_PDF417_TERMINATOR && + codewords[codeIndex] != BEGIN_MACRO_PDF417_OPTIONAL_FIELD; + codeIndex++) { fileId << std::setw(3) << codewords[codeIndex]; } resultMetadata.setFileId(fileId.str()); From 840d8d779ab9ef88c6d524101f0cca6d672a785b Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 3 May 2022 01:43:21 +0200 Subject: [PATCH 180/185] c++: remove unused variables in textcodecs (fix clang warning) --- core/src/textcodec/Big5TextDecoder.cpp | 8 ++++---- core/src/textcodec/GBTextDecoder.cpp | 22 +++++++++++----------- core/src/textcodec/JPTextDecoder.cpp | 18 +++++++++--------- core/src/textcodec/KRTextDecoder.cpp | 10 +++++----- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/core/src/textcodec/Big5TextDecoder.cpp b/core/src/textcodec/Big5TextDecoder.cpp index 04ffc71c40..48ec9bdf16 100644 --- a/core/src/textcodec/Big5TextDecoder.cpp +++ b/core/src/textcodec/Big5TextDecoder.cpp @@ -1747,7 +1747,7 @@ Big5TextDecoder::AppendBig5(std::vector& result, const uint8_t* bytes, { uint8_t buf[2] = { 0 }; int nbuf = 0; - int invalid = 0; +// int invalid = 0; result.reserve(length); for (size_t i = 0; i& result, const uint8_t* bytes, else { // Invalid result.push_back(REPLACEMENT); - ++invalid; +// ++invalid; } break; case 1: @@ -1779,13 +1779,13 @@ Big5TextDecoder::AppendBig5(std::vector& result, const uint8_t* bytes, else { // Error result.push_back(REPLACEMENT); - ++invalid; +// ++invalid; } } else { // Error result.push_back(REPLACEMENT); - ++invalid; +// ++invalid; } nbuf = 0; break; diff --git a/core/src/textcodec/GBTextDecoder.cpp b/core/src/textcodec/GBTextDecoder.cpp index cef0368897..e104b595e9 100644 --- a/core/src/textcodec/GBTextDecoder.cpp +++ b/core/src/textcodec/GBTextDecoder.cpp @@ -3433,7 +3433,7 @@ GBTextDecoder::AppendGB18030(std::vector& result, const uint8_t* bytes { uint8_t buf[4]; int nbuf = 0; - int invalid = 0; +// int invalid = 0; result.resize(length); int unicodeLen = 0; @@ -3454,7 +3454,7 @@ GBTextDecoder::AppendGB18030(std::vector& result, const uint8_t* bytes else { // Invalid result[unicodeLen++] = REPLACEMENT; - ++invalid; +// ++invalid; } break; case 1: @@ -3468,7 +3468,7 @@ GBTextDecoder::AppendGB18030(std::vector& result, const uint8_t* bytes } else { result[unicodeLen++] = REPLACEMENT; - ++invalid; +// ++invalid; } nbuf = 0; } @@ -3479,7 +3479,7 @@ GBTextDecoder::AppendGB18030(std::vector& result, const uint8_t* bytes else { // Error result[unicodeLen++] = REPLACEMENT; - ++invalid; +// ++invalid; nbuf = 0; } break; @@ -3491,7 +3491,7 @@ GBTextDecoder::AppendGB18030(std::vector& result, const uint8_t* bytes } else { result[unicodeLen++] = REPLACEMENT; - ++invalid; +// ++invalid; nbuf = 0; } break; @@ -3506,12 +3506,12 @@ GBTextDecoder::AppendGB18030(std::vector& result, const uint8_t* bytes } else { result[unicodeLen++] = REPLACEMENT; - ++invalid; +// ++invalid; } } else { result[unicodeLen++] = REPLACEMENT; - ++invalid; +// ++invalid; } nbuf = 0; break; @@ -3525,7 +3525,7 @@ GBTextDecoder::AppendGB2312(std::vector& result, const uint8_t* bytes, { uint8_t buf[2]; int nbuf = 0; - int invalid = 0; +// int invalid = 0; result.resize(length); int unicodeLen = 0; @@ -3545,7 +3545,7 @@ GBTextDecoder::AppendGB2312(std::vector& result, const uint8_t* bytes, else { // Invalid result[unicodeLen++] = REPLACEMENT; - ++invalid; +// ++invalid; } break; case 1: @@ -3559,14 +3559,14 @@ GBTextDecoder::AppendGB2312(std::vector& result, const uint8_t* bytes, } else { result[unicodeLen++] = REPLACEMENT; - ++invalid; +// ++invalid; } nbuf = 0; } else { // Error result[unicodeLen++] = REPLACEMENT; - ++invalid; +// ++invalid; nbuf = 0; } break; diff --git a/core/src/textcodec/JPTextDecoder.cpp b/core/src/textcodec/JPTextDecoder.cpp index 4859e647ec..22874a4961 100644 --- a/core/src/textcodec/JPTextDecoder.cpp +++ b/core/src/textcodec/JPTextDecoder.cpp @@ -2634,7 +2634,7 @@ JPTextDecoder::AppendShiftJIS(std::vector& result, const uint8_t* byte { uint8_t buf[1] = { 0 }; int nbuf = 0; - int invalid = 0; +// int invalid = 0; unsigned u = 0; for (size_t i = 0; i& result, const uint8_t* byte else { // Invalid result.push_back(REPLACEMENT); - ++invalid; +// ++invalid; } break; case 1: @@ -2675,7 +2675,7 @@ JPTextDecoder::AppendShiftJIS(std::vector& result, const uint8_t* byte else { // Invalid result.push_back(REPLACEMENT); - ++invalid; +// ++invalid; } nbuf = 0; break; @@ -2696,7 +2696,7 @@ JPTextDecoder::AppendEUCJP(std::vector& result, const uint8_t* bytes, uint8_t buf[2] = { 0, 0 }; int nbuf = 0; - int invalid = 0; +// int invalid = 0; for (size_t i = 0; i& result, const uint8_t* bytes, else { // Invalid result.push_back(REPLACEMENT); - ++invalid; +// ++invalid; } break; case 1: @@ -2730,7 +2730,7 @@ JPTextDecoder::AppendEUCJP(std::vector& result, const uint8_t* bytes, } else { result.push_back(REPLACEMENT); - ++invalid; +// ++invalid; } nbuf = 0; } @@ -2743,7 +2743,7 @@ JPTextDecoder::AppendEUCJP(std::vector& result, const uint8_t* bytes, else { // Error result.push_back(REPLACEMENT); - ++invalid; +// ++invalid; nbuf = 0; } } @@ -2755,7 +2755,7 @@ JPTextDecoder::AppendEUCJP(std::vector& result, const uint8_t* bytes, else { // Error result.push_back(REPLACEMENT); - ++invalid; +// ++invalid; } nbuf = 0; } @@ -2767,7 +2767,7 @@ JPTextDecoder::AppendEUCJP(std::vector& result, const uint8_t* bytes, } else { result.push_back(REPLACEMENT); - ++invalid; +// ++invalid; } nbuf = 0; } diff --git a/core/src/textcodec/KRTextDecoder.cpp b/core/src/textcodec/KRTextDecoder.cpp index 08e0b61895..dcbc0646ac 100644 --- a/core/src/textcodec/KRTextDecoder.cpp +++ b/core/src/textcodec/KRTextDecoder.cpp @@ -1029,7 +1029,7 @@ KRTextDecoder::AppendEucKr(std::vector& result, const uint8_t* bytes, { uint8_t buf[2] = { 0, 0 }; int nbuf = 0; - int invalid = 0; +// int invalid = 0; for (size_t i = 0; i& result, const uint8_t* bytes, else { // Invalid result.push_back(REPLACEMENT); - ++invalid; +// ++invalid; } break; case 1: @@ -1072,7 +1072,7 @@ KRTextDecoder::AppendEucKr(std::vector& result, const uint8_t* bytes, column = ch - 0x81 + 52; else { result.push_back(REPLACEMENT); - ++invalid; +// ++invalid; break; } @@ -1084,7 +1084,7 @@ KRTextDecoder::AppendEucKr(std::vector& result, const uint8_t* bytes, // check whether the conversion avialble in the table. if (internal_code < 0 || internal_code >= 8822) { result.push_back(REPLACEMENT); - ++invalid; +// ++invalid; break; } else @@ -1094,4 +1094,4 @@ KRTextDecoder::AppendEucKr(std::vector& result, const uint8_t* bytes, break; } } -} \ No newline at end of file +} From 75648494b0e378721e3a76fd2f440bbfebe4f6a2 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 3 May 2022 01:44:09 +0200 Subject: [PATCH 181/185] wasm: enable new `tryDownscale` hint --- wrappers/wasm/BarcodeReader.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wrappers/wasm/BarcodeReader.cpp b/wrappers/wasm/BarcodeReader.cpp index 6c25aa4cc4..5dd2dab2bf 100644 --- a/wrappers/wasm/BarcodeReader.cpp +++ b/wrappers/wasm/BarcodeReader.cpp @@ -39,6 +39,7 @@ ReadResult readBarcodeFromImage(int bufferPtr, int bufferLength, bool tryHarder, DecodeHints hints; hints.setTryHarder(tryHarder); hints.setTryRotate(tryHarder); + hints.setTryDownscale(tryHarder); hints.setFormats(BarcodeFormatsFromString(format)); int width, height, channels; @@ -71,6 +72,7 @@ ReadResult readBarcodeFromPixmap(int bufferPtr, int imgWidth, int imgHeight, boo DecodeHints hints; hints.setTryHarder(tryHarder); hints.setTryRotate(tryHarder); + hints.setTryDownscale(tryHarder); hints.setFormats(BarcodeFormatsFromString(format)); auto result = From d529003f4ae37507e11dc8fff57f173b9c844226 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 3 May 2022 01:52:25 +0200 Subject: [PATCH 182/185] c++: remove unused variables in (more) textcodecs (fix clang warning) --- core/src/textcodec/GBTextEncoder.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/textcodec/GBTextEncoder.cpp b/core/src/textcodec/GBTextEncoder.cpp index 707ae60594..8bbb84826d 100644 --- a/core/src/textcodec/GBTextEncoder.cpp +++ b/core/src/textcodec/GBTextEncoder.cpp @@ -3977,7 +3977,7 @@ void GBTextEncoder::EncodeGB18030(const std::wstring& str, std::string& bytes) { static const char replacement = '?'; unsigned high = 0; - int invalid = 0; +// int invalid = 0; bytes.resize(4 * str.length() + 1); int index = 0; @@ -3998,14 +3998,14 @@ void GBTextEncoder::EncodeGB18030(const std::wstring& str, std::string& bytes) } else { bytes[index++] = replacement; - ++invalid; +// ++invalid; } high = 0; continue; } else { bytes[index++] = replacement; - ++invalid; +// ++invalid; high = 0; } } @@ -4026,7 +4026,7 @@ void GBTextEncoder::EncodeGB18030(const std::wstring& str, std::string& bytes) else { // Error bytes[index++] = replacement; - ++invalid; +// ++invalid; } } bytes.resize(index); From 00af76cd724aeec64a47fa7e7b7abf943e109bc8 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 3 May 2022 02:03:42 +0200 Subject: [PATCH 183/185] android: upgrade kotlin-plugin to 1.6.21 --- wrappers/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrappers/android/build.gradle b/wrappers/android/build.gradle index 2eec1a2072..68b25395d4 100644 --- a/wrappers/android/build.gradle +++ b/wrappers/android/build.gradle @@ -1,6 +1,6 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = "1.6.20" + ext.kotlin_version = "1.6.21" repositories { google() mavenCentral() From bdddf42ba366aaa2842ef746e5f1d13a821b1848 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 3 May 2022 02:04:48 +0200 Subject: [PATCH 184/185] android: use latest ZXing upstream version 3.5.0 in demo app --- wrappers/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrappers/android/app/build.gradle b/wrappers/android/app/build.gradle index 08b2c89fdf..c430af75f7 100644 --- a/wrappers/android/app/build.gradle +++ b/wrappers/android/app/build.gradle @@ -53,7 +53,7 @@ dependencies { implementation 'androidx.camera:camera-view:1.0.0-alpha32' // Java 'upstream' version of zxing (to compare performance) - implementation 'com.google.zxing:core:3.4.1' + implementation 'com.google.zxing:core:3.5.0' implementation project(':zxingcpp') } From 50f53ef73f610788feaebd4159f8e4b32e6d38d8 Mon Sep 17 00:00:00 2001 From: axxel Date: Tue, 3 May 2022 09:36:14 +0200 Subject: [PATCH 185/185] Release 1.3.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ebe586975..7d01ee88b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required (VERSION 3.14) -project (ZXing VERSION "1.2.0" LANGUAGES CXX) +project (ZXing VERSION "1.3.0" LANGUAGES CXX) option (BUILD_WRITERS "Build with writer support (encoders)" ON) option (BUILD_READERS "Build with reader support (decoders)" ON) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy