react-native-nitro-zxing 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -0
- package/VisionCameraZxing.podspec +48 -0
- package/android/CMakeLists.txt +43 -0
- package/android/build.gradle +150 -0
- package/android/fix-prefab.gradle +51 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/cpp/cpp-adapter.cpp +7 -0
- package/android/src/main/java/com/margelo/nitro/camera/zxing/VisionCameraZxingPackage.kt +21 -0
- package/cpp/BarcodeValueTypeClassifier.cpp +70 -0
- package/cpp/BarcodeValueTypeClassifier.hpp +14 -0
- package/cpp/FrameImageView.cpp +75 -0
- package/cpp/FrameImageView.hpp +34 -0
- package/cpp/HybridBarcode.cpp +59 -0
- package/cpp/HybridBarcode.hpp +26 -0
- package/cpp/HybridBarcodeScanner.cpp +121 -0
- package/cpp/HybridBarcodeScanner.hpp +26 -0
- package/cpp/HybridBarcodeScannerFactory.cpp +11 -0
- package/cpp/HybridBarcodeScannerFactory.hpp +14 -0
- package/cpp/ImageDownscaler.cpp +34 -0
- package/cpp/ImageDownscaler.hpp +21 -0
- package/cpp/ZXingDecoder.cpp +189 -0
- package/cpp/ZXingDecoder.hpp +72 -0
- package/cpp/zxing-core/LICENSE +202 -0
- package/cpp/zxing-core/README.md +156 -0
- package/cpp/zxing-core/core/CMakeLists.txt +784 -0
- package/cpp/zxing-core/core/Version.h.in +28 -0
- package/cpp/zxing-core/core/ZXingConfig.cmake.in +9 -0
- package/cpp/zxing-core/core/src/Barcode.cpp +255 -0
- package/cpp/zxing-core/core/src/Barcode.h +191 -0
- package/cpp/zxing-core/core/src/BarcodeData.h +100 -0
- package/cpp/zxing-core/core/src/BarcodeFormat.cpp +286 -0
- package/cpp/zxing-core/core/src/BarcodeFormat.h +272 -0
- package/cpp/zxing-core/core/src/BinaryBitmap.cpp +111 -0
- package/cpp/zxing-core/core/src/BinaryBitmap.h +66 -0
- package/cpp/zxing-core/core/src/BitArray.cpp +38 -0
- package/cpp/zxing-core/core/src/BitArray.h +181 -0
- package/cpp/zxing-core/core/src/BitMatrix.cpp +175 -0
- package/cpp/zxing-core/core/src/BitMatrix.h +199 -0
- package/cpp/zxing-core/core/src/BitMatrixCursor.h +253 -0
- package/cpp/zxing-core/core/src/BitMatrixIO.cpp +101 -0
- package/cpp/zxing-core/core/src/BitMatrixIO.h +21 -0
- package/cpp/zxing-core/core/src/BitSource.cpp +77 -0
- package/cpp/zxing-core/core/src/BitSource.h +72 -0
- package/cpp/zxing-core/core/src/ByteArray.h +42 -0
- package/cpp/zxing-core/core/src/CharacterSet.cpp +78 -0
- package/cpp/zxing-core/core/src/CharacterSet.h +58 -0
- package/cpp/zxing-core/core/src/ConcentricFinder.cpp +312 -0
- package/cpp/zxing-core/core/src/ConcentricFinder.h +156 -0
- package/cpp/zxing-core/core/src/Content.cpp +471 -0
- package/cpp/zxing-core/core/src/Content.h +82 -0
- package/cpp/zxing-core/core/src/ContentType.h +19 -0
- package/cpp/zxing-core/core/src/CreateBarcode.cpp +490 -0
- package/cpp/zxing-core/core/src/CreateBarcode.h +132 -0
- package/cpp/zxing-core/core/src/DecoderResult.h +92 -0
- package/cpp/zxing-core/core/src/DetectorResult.h +44 -0
- package/cpp/zxing-core/core/src/ECI.cpp +81 -0
- package/cpp/zxing-core/core/src/ECI.h +78 -0
- package/cpp/zxing-core/core/src/Error.cpp +29 -0
- package/cpp/zxing-core/core/src/Error.h +79 -0
- package/cpp/zxing-core/core/src/Flags.h +165 -0
- package/cpp/zxing-core/core/src/GTIN.cpp +253 -0
- package/cpp/zxing-core/core/src/GTIN.h +58 -0
- package/cpp/zxing-core/core/src/GlobalHistogramBinarizer.cpp +197 -0
- package/cpp/zxing-core/core/src/GlobalHistogramBinarizer.h +34 -0
- package/cpp/zxing-core/core/src/GridSampler.cpp +124 -0
- package/cpp/zxing-core/core/src/GridSampler.h +59 -0
- package/cpp/zxing-core/core/src/HRI.cpp +343 -0
- package/cpp/zxing-core/core/src/HRI.h +16 -0
- package/cpp/zxing-core/core/src/HybridBinarizer.cpp +315 -0
- package/cpp/zxing-core/core/src/HybridBinarizer.h +40 -0
- package/cpp/zxing-core/core/src/ImageView.h +168 -0
- package/cpp/zxing-core/core/src/JSON.cpp +154 -0
- package/cpp/zxing-core/core/src/JSON.h +66 -0
- package/cpp/zxing-core/core/src/LocalGrid.cpp +242 -0
- package/cpp/zxing-core/core/src/LocalGrid.h +91 -0
- package/cpp/zxing-core/core/src/LogMatrix.h +105 -0
- package/cpp/zxing-core/core/src/Matrix.h +125 -0
- package/cpp/zxing-core/core/src/MultiFormatReader.cpp +107 -0
- package/cpp/zxing-core/core/src/MultiFormatReader.h +34 -0
- package/cpp/zxing-core/core/src/MultiFormatWriter.cpp +111 -0
- package/cpp/zxing-core/core/src/MultiFormatWriter.h +62 -0
- package/cpp/zxing-core/core/src/Pattern.h +472 -0
- package/cpp/zxing-core/core/src/PerspectiveTransform.cpp +81 -0
- package/cpp/zxing-core/core/src/PerspectiveTransform.h +47 -0
- package/cpp/zxing-core/core/src/Point.h +185 -0
- package/cpp/zxing-core/core/src/Quadrilateral.h +215 -0
- package/cpp/zxing-core/core/src/Range.h +112 -0
- package/cpp/zxing-core/core/src/ReadBarcode.cpp +335 -0
- package/cpp/zxing-core/core/src/ReadBarcode.h +33 -0
- package/cpp/zxing-core/core/src/Reader.h +32 -0
- package/cpp/zxing-core/core/src/ReaderOptions.h +210 -0
- package/cpp/zxing-core/core/src/ReedSolomon.cpp +100 -0
- package/cpp/zxing-core/core/src/ReedSolomon.h +66 -0
- package/cpp/zxing-core/core/src/RegressionLine.h +161 -0
- package/cpp/zxing-core/core/src/ResultPoint.cpp +20 -0
- package/cpp/zxing-core/core/src/ResultPoint.h +35 -0
- package/cpp/zxing-core/core/src/StdGenerator.h +121 -0
- package/cpp/zxing-core/core/src/StdPrint.h +38 -0
- package/cpp/zxing-core/core/src/StdScope.h +55 -0
- package/cpp/zxing-core/core/src/StructuredAppend.h +19 -0
- package/cpp/zxing-core/core/src/SymbologyIdentifier.h +29 -0
- package/cpp/zxing-core/core/src/TextDecoder.cpp +59 -0
- package/cpp/zxing-core/core/src/TextDecoder.h +23 -0
- package/cpp/zxing-core/core/src/TextEncoder.cpp +75 -0
- package/cpp/zxing-core/core/src/TextEncoder.h +31 -0
- package/cpp/zxing-core/core/src/ThresholdBinarizer.h +58 -0
- package/cpp/zxing-core/core/src/TritMatrix.h +29 -0
- package/cpp/zxing-core/core/src/Utf.cpp +276 -0
- package/cpp/zxing-core/core/src/Utf.h +26 -0
- package/cpp/zxing-core/core/src/WhiteRectDetector.cpp +276 -0
- package/cpp/zxing-core/core/src/WhiteRectDetector.h +36 -0
- package/cpp/zxing-core/core/src/WriteBarcode.cpp +213 -0
- package/cpp/zxing-core/core/src/WriteBarcode.h +72 -0
- package/cpp/zxing-core/core/src/ZXAlgorithms.h +353 -0
- package/cpp/zxing-core/core/src/ZXConfig.h +17 -0
- package/cpp/zxing-core/core/src/ZXTestSupport.h +9 -0
- package/cpp/zxing-core/core/src/ZXVersion.h +14 -0
- package/cpp/zxing-core/core/src/ZXingC.cpp +460 -0
- package/cpp/zxing-core/core/src/ZXingC.h +296 -0
- package/cpp/zxing-core/core/src/ZXingCpp.cpp +18 -0
- package/cpp/zxing-core/core/src/ZXingCpp.h +76 -0
- package/cpp/zxing-core/core/src/ZXingQt.h +606 -0
- package/cpp/zxing-core/core/src/aztec/AZDecoder.cpp +376 -0
- package/cpp/zxing-core/core/src/aztec/AZDecoder.h +20 -0
- package/cpp/zxing-core/core/src/aztec/AZDetector.cpp +508 -0
- package/cpp/zxing-core/core/src/aztec/AZDetector.h +25 -0
- package/cpp/zxing-core/core/src/aztec/AZDetectorResult.h +51 -0
- package/cpp/zxing-core/core/src/aztec/AZEncoder.cpp +318 -0
- package/cpp/zxing-core/core/src/aztec/AZEncoder.h +44 -0
- package/cpp/zxing-core/core/src/aztec/AZEncodingState.h +40 -0
- package/cpp/zxing-core/core/src/aztec/AZHighLevelEncoder.cpp +377 -0
- package/cpp/zxing-core/core/src/aztec/AZHighLevelEncoder.h +36 -0
- package/cpp/zxing-core/core/src/aztec/AZReader.cpp +45 -0
- package/cpp/zxing-core/core/src/aztec/AZReader.h +21 -0
- package/cpp/zxing-core/core/src/aztec/AZToken.cpp +44 -0
- package/cpp/zxing-core/core/src/aztec/AZToken.h +39 -0
- package/cpp/zxing-core/core/src/aztec/AZWriter.cpp +38 -0
- package/cpp/zxing-core/core/src/aztec/AZWriter.h +55 -0
- package/cpp/zxing-core/core/src/datamatrix/DMBitLayout.cpp +194 -0
- package/cpp/zxing-core/core/src/datamatrix/DMBitLayout.h +21 -0
- package/cpp/zxing-core/core/src/datamatrix/DMDataBlock.cpp +63 -0
- package/cpp/zxing-core/core/src/datamatrix/DMDataBlock.h +41 -0
- package/cpp/zxing-core/core/src/datamatrix/DMDecoder.cpp +457 -0
- package/cpp/zxing-core/core/src/datamatrix/DMDecoder.h +19 -0
- package/cpp/zxing-core/core/src/datamatrix/DMDetector.cpp +1230 -0
- package/cpp/zxing-core/core/src/datamatrix/DMDetector.h +23 -0
- package/cpp/zxing-core/core/src/datamatrix/DMECEncoder.cpp +137 -0
- package/cpp/zxing-core/core/src/datamatrix/DMECEncoder.h +26 -0
- package/cpp/zxing-core/core/src/datamatrix/DMEncoderContext.h +128 -0
- package/cpp/zxing-core/core/src/datamatrix/DMHighLevelEncoder.cpp +919 -0
- package/cpp/zxing-core/core/src/datamatrix/DMHighLevelEncoder.h +29 -0
- package/cpp/zxing-core/core/src/datamatrix/DMReader.cpp +40 -0
- package/cpp/zxing-core/core/src/datamatrix/DMReader.h +21 -0
- package/cpp/zxing-core/core/src/datamatrix/DMSymbolInfo.cpp +141 -0
- package/cpp/zxing-core/core/src/datamatrix/DMSymbolInfo.h +78 -0
- package/cpp/zxing-core/core/src/datamatrix/DMSymbolShape.h +17 -0
- package/cpp/zxing-core/core/src/datamatrix/DMVersion.cpp +82 -0
- package/cpp/zxing-core/core/src/datamatrix/DMVersion.h +76 -0
- package/cpp/zxing-core/core/src/datamatrix/DMWriter.cpp +114 -0
- package/cpp/zxing-core/core/src/datamatrix/DMWriter.h +62 -0
- package/cpp/zxing-core/core/src/librscpp/README.md +65 -0
- package/cpp/zxing-core/core/src/librscpp/decode.h +258 -0
- package/cpp/zxing-core/core/src/librscpp/encode.h +124 -0
- package/cpp/zxing-core/core/src/librscpp/field.h +158 -0
- package/cpp/zxing-core/core/src/librscpp/poly.h +194 -0
- package/cpp/zxing-core/core/src/libzint/2of5inter.c +1 -0
- package/cpp/zxing-core/core/src/libzint/2of5inter_based.c +1 -0
- package/cpp/zxing-core/core/src/libzint/aztec.c +1 -0
- package/cpp/zxing-core/core/src/libzint/aztec.h +1 -0
- package/cpp/zxing-core/core/src/libzint/big5.h +1 -0
- package/cpp/zxing-core/core/src/libzint/codabar.c +1 -0
- package/cpp/zxing-core/core/src/libzint/code.c +1 -0
- package/cpp/zxing-core/core/src/libzint/code128.c +1 -0
- package/cpp/zxing-core/core/src/libzint/code128.h +1 -0
- package/cpp/zxing-core/core/src/libzint/common.c +1 -0
- package/cpp/zxing-core/core/src/libzint/common.h +1 -0
- package/cpp/zxing-core/core/src/libzint/dmatrix.c +1 -0
- package/cpp/zxing-core/core/src/libzint/dmatrix.h +1 -0
- package/cpp/zxing-core/core/src/libzint/dxfilmedge.c +1 -0
- package/cpp/zxing-core/core/src/libzint/eci.c +1 -0
- package/cpp/zxing-core/core/src/libzint/eci.h +1 -0
- package/cpp/zxing-core/core/src/libzint/eci_sb.h +1 -0
- package/cpp/zxing-core/core/src/libzint/filemem.c +1 -0
- package/cpp/zxing-core/core/src/libzint/filemem.h +1 -0
- package/cpp/zxing-core/core/src/libzint/fonts/normal_woff2.h +1 -0
- package/cpp/zxing-core/core/src/libzint/fonts/upcean_woff2.h +1 -0
- package/cpp/zxing-core/core/src/libzint/gb18030.h +1 -0
- package/cpp/zxing-core/core/src/libzint/gb2312.h +1 -0
- package/cpp/zxing-core/core/src/libzint/gbk.h +1 -0
- package/cpp/zxing-core/core/src/libzint/general_field.c +1 -0
- package/cpp/zxing-core/core/src/libzint/general_field.h +1 -0
- package/cpp/zxing-core/core/src/libzint/gs1.c +1 -0
- package/cpp/zxing-core/core/src/libzint/gs1.h +1 -0
- package/cpp/zxing-core/core/src/libzint/gs1_lint.h +1 -0
- package/cpp/zxing-core/core/src/libzint/iso3166.h +1 -0
- package/cpp/zxing-core/core/src/libzint/iso4217.h +1 -0
- package/cpp/zxing-core/core/src/libzint/ksx1001.h +1 -0
- package/cpp/zxing-core/core/src/libzint/large.c +1 -0
- package/cpp/zxing-core/core/src/libzint/large.h +1 -0
- package/cpp/zxing-core/core/src/libzint/library.c +1 -0
- package/cpp/zxing-core/core/src/libzint/maxicode.c +1 -0
- package/cpp/zxing-core/core/src/libzint/maxicode.h +1 -0
- package/cpp/zxing-core/core/src/libzint/medical.c +1 -0
- package/cpp/zxing-core/core/src/libzint/output.c +1 -0
- package/cpp/zxing-core/core/src/libzint/output.h +1 -0
- package/cpp/zxing-core/core/src/libzint/pdf417.c +1 -0
- package/cpp/zxing-core/core/src/libzint/pdf417.h +1 -0
- package/cpp/zxing-core/core/src/libzint/pdf417_tabs.h +1 -0
- package/cpp/zxing-core/core/src/libzint/qr.c +1 -0
- package/cpp/zxing-core/core/src/libzint/qr.h +1 -0
- package/cpp/zxing-core/core/src/libzint/raster.c +1 -0
- package/cpp/zxing-core/core/src/libzint/raster_font.h +1 -0
- package/cpp/zxing-core/core/src/libzint/reedsol.c +1 -0
- package/cpp/zxing-core/core/src/libzint/reedsol.h +1 -0
- package/cpp/zxing-core/core/src/libzint/reedsol_logs.h +1 -0
- package/cpp/zxing-core/core/src/libzint/rss.c +1 -0
- package/cpp/zxing-core/core/src/libzint/rss.h +1 -0
- package/cpp/zxing-core/core/src/libzint/sjis.h +1 -0
- package/cpp/zxing-core/core/src/libzint/stubs.c +137 -0
- package/cpp/zxing-core/core/src/libzint/svg.c +1 -0
- package/cpp/zxing-core/core/src/libzint/telepen.c +1 -0
- package/cpp/zxing-core/core/src/libzint/upcean.c +1 -0
- package/cpp/zxing-core/core/src/libzint/vector.c +1 -0
- package/cpp/zxing-core/core/src/libzint/zfiletypes.h +1 -0
- package/cpp/zxing-core/core/src/libzint/zint.h +1 -0
- package/cpp/zxing-core/core/src/libzint/zintconfig.h +1 -0
- package/cpp/zxing-core/core/src/libzueci/README.md +11 -0
- package/cpp/zxing-core/core/src/libzueci/zueci.c +1718 -0
- package/cpp/zxing-core/core/src/libzueci/zueci.h +179 -0
- package/cpp/zxing-core/core/src/libzueci/zueci_big5.h +3884 -0
- package/cpp/zxing-core/core/src/libzueci/zueci_common.h +31 -0
- package/cpp/zxing-core/core/src/libzueci/zueci_gb18030.h +264 -0
- package/cpp/zxing-core/core/src/libzueci/zueci_gb2312.h +2381 -0
- package/cpp/zxing-core/core/src/libzueci/zueci_gbk.h +4047 -0
- package/cpp/zxing-core/core/src/libzueci/zueci_ksx1001.h +2932 -0
- package/cpp/zxing-core/core/src/libzueci/zueci_sb.h +1112 -0
- package/cpp/zxing-core/core/src/libzueci/zueci_sjis.h +2286 -0
- package/cpp/zxing-core/core/src/maxicode/MCBitMatrixParser.cpp +68 -0
- package/cpp/zxing-core/core/src/maxicode/MCBitMatrixParser.h +30 -0
- package/cpp/zxing-core/core/src/maxicode/MCDecoder.cpp +329 -0
- package/cpp/zxing-core/core/src/maxicode/MCDecoder.h +19 -0
- package/cpp/zxing-core/core/src/maxicode/MCReader.cpp +65 -0
- package/cpp/zxing-core/core/src/maxicode/MCReader.h +21 -0
- package/cpp/zxing-core/core/src/oned/ODCodabarReader.cpp +102 -0
- package/cpp/zxing-core/core/src/oned/ODCodabarReader.h +21 -0
- package/cpp/zxing-core/core/src/oned/ODCodabarWriter.cpp +136 -0
- package/cpp/zxing-core/core/src/oned/ODCodabarWriter.h +34 -0
- package/cpp/zxing-core/core/src/oned/ODCode128Patterns.h +130 -0
- package/cpp/zxing-core/core/src/oned/ODCode128Reader.cpp +236 -0
- package/cpp/zxing-core/core/src/oned/ODCode128Reader.h +21 -0
- package/cpp/zxing-core/core/src/oned/ODCode128Writer.cpp +261 -0
- package/cpp/zxing-core/core/src/oned/ODCode128Writer.h +34 -0
- package/cpp/zxing-core/core/src/oned/ODCode39Reader.cpp +213 -0
- package/cpp/zxing-core/core/src/oned/ODCode39Reader.h +26 -0
- package/cpp/zxing-core/core/src/oned/ODCode39Writer.cpp +176 -0
- package/cpp/zxing-core/core/src/oned/ODCode39Writer.h +34 -0
- package/cpp/zxing-core/core/src/oned/ODCode93Patterns.h +80 -0
- package/cpp/zxing-core/core/src/oned/ODCode93Reader.cpp +112 -0
- package/cpp/zxing-core/core/src/oned/ODCode93Reader.h +21 -0
- package/cpp/zxing-core/core/src/oned/ODCode93Writer.cpp +192 -0
- package/cpp/zxing-core/core/src/oned/ODCode93Writer.h +32 -0
- package/cpp/zxing-core/core/src/oned/ODDXFilmEdgeReader.cpp +246 -0
- package/cpp/zxing-core/core/src/oned/ODDXFilmEdgeReader.h +26 -0
- package/cpp/zxing-core/core/src/oned/ODDataBarCommon.cpp +172 -0
- package/cpp/zxing-core/core/src/oned/ODDataBarCommon.h +204 -0
- package/cpp/zxing-core/core/src/oned/ODDataBarExpandedBitDecoder.cpp +253 -0
- package/cpp/zxing-core/core/src/oned/ODDataBarExpandedBitDecoder.h +19 -0
- package/cpp/zxing-core/core/src/oned/ODDataBarExpandedReader.cpp +383 -0
- package/cpp/zxing-core/core/src/oned/ODDataBarExpandedReader.h +25 -0
- package/cpp/zxing-core/core/src/oned/ODDataBarLimitedReader.cpp +163 -0
- package/cpp/zxing-core/core/src/oned/ODDataBarLimitedReader.h +23 -0
- package/cpp/zxing-core/core/src/oned/ODDataBarReader.cpp +240 -0
- package/cpp/zxing-core/core/src/oned/ODDataBarReader.h +25 -0
- package/cpp/zxing-core/core/src/oned/ODEAN13Writer.cpp +62 -0
- package/cpp/zxing-core/core/src/oned/ODEAN13Writer.h +34 -0
- package/cpp/zxing-core/core/src/oned/ODEAN8Writer.cpp +51 -0
- package/cpp/zxing-core/core/src/oned/ODEAN8Writer.h +34 -0
- package/cpp/zxing-core/core/src/oned/ODITFReader.cpp +97 -0
- package/cpp/zxing-core/core/src/oned/ODITFReader.h +33 -0
- package/cpp/zxing-core/core/src/oned/ODITFWriter.cpp +78 -0
- package/cpp/zxing-core/core/src/oned/ODITFWriter.h +34 -0
- package/cpp/zxing-core/core/src/oned/ODMultiUPCEANReader.cpp +323 -0
- package/cpp/zxing-core/core/src/oned/ODMultiUPCEANReader.h +32 -0
- package/cpp/zxing-core/core/src/oned/ODReader.cpp +259 -0
- package/cpp/zxing-core/core/src/oned/ODReader.h +35 -0
- package/cpp/zxing-core/core/src/oned/ODRowReader.h +222 -0
- package/cpp/zxing-core/core/src/oned/ODTelepenReader.cpp +199 -0
- package/cpp/zxing-core/core/src/oned/ODTelepenReader.h +25 -0
- package/cpp/zxing-core/core/src/oned/ODUPCAWriter.cpp +33 -0
- package/cpp/zxing-core/core/src/oned/ODUPCAWriter.h +34 -0
- package/cpp/zxing-core/core/src/oned/ODUPCEANCommon.cpp +80 -0
- package/cpp/zxing-core/core/src/oned/ODUPCEANCommon.h +129 -0
- package/cpp/zxing-core/core/src/oned/ODUPCEWriter.cpp +56 -0
- package/cpp/zxing-core/core/src/oned/ODUPCEWriter.h +34 -0
- package/cpp/zxing-core/core/src/oned/ODWriterHelper.cpp +39 -0
- package/cpp/zxing-core/core/src/oned/ODWriterHelper.h +48 -0
- package/cpp/zxing-core/core/src/pdf417/MicroPDFReader.cpp +796 -0
- package/cpp/zxing-core/core/src/pdf417/MicroPDFReader.h +23 -0
- package/cpp/zxing-core/core/src/pdf417/PDF417.cpp +85 -0
- package/cpp/zxing-core/core/src/pdf417/PDF417.h +81 -0
- package/cpp/zxing-core/core/src/pdf417/PDFBarcodeMetadata.h +53 -0
- package/cpp/zxing-core/core/src/pdf417/PDFBarcodeValue.cpp +48 -0
- package/cpp/zxing-core/core/src/pdf417/PDFBarcodeValue.h +38 -0
- package/cpp/zxing-core/core/src/pdf417/PDFBoundingBox.cpp +116 -0
- package/cpp/zxing-core/core/src/pdf417/PDFBoundingBox.h +75 -0
- package/cpp/zxing-core/core/src/pdf417/PDFCodeword.h +71 -0
- package/cpp/zxing-core/core/src/pdf417/PDFCodewordDecoder.cpp +588 -0
- package/cpp/zxing-core/core/src/pdf417/PDFCodewordDecoder.h +41 -0
- package/cpp/zxing-core/core/src/pdf417/PDFCompaction.h +20 -0
- package/cpp/zxing-core/core/src/pdf417/PDFCustomData.h +32 -0
- package/cpp/zxing-core/core/src/pdf417/PDFDecoder.cpp +725 -0
- package/cpp/zxing-core/core/src/pdf417/PDFDecoder.h +20 -0
- package/cpp/zxing-core/core/src/pdf417/PDFDetectionResult.cpp +247 -0
- package/cpp/zxing-core/core/src/pdf417/PDFDetectionResult.h +74 -0
- package/cpp/zxing-core/core/src/pdf417/PDFDetectionResultColumn.cpp +321 -0
- package/cpp/zxing-core/core/src/pdf417/PDFDetectionResultColumn.h +83 -0
- package/cpp/zxing-core/core/src/pdf417/PDFDetector.cpp +371 -0
- package/cpp/zxing-core/core/src/pdf417/PDFDetector.h +45 -0
- package/cpp/zxing-core/core/src/pdf417/PDFEncoder.cpp +570 -0
- package/cpp/zxing-core/core/src/pdf417/PDFEncoder.h +182 -0
- package/cpp/zxing-core/core/src/pdf417/PDFHighLevelEncoder.cpp +570 -0
- package/cpp/zxing-core/core/src/pdf417/PDFHighLevelEncoder.h +32 -0
- package/cpp/zxing-core/core/src/pdf417/PDFReader.cpp +333 -0
- package/cpp/zxing-core/core/src/pdf417/PDFReader.h +28 -0
- package/cpp/zxing-core/core/src/pdf417/PDFScanningDecoder.cpp +559 -0
- package/cpp/zxing-core/core/src/pdf417/PDFScanningDecoder.h +41 -0
- package/cpp/zxing-core/core/src/pdf417/PDFWriter.cpp +170 -0
- package/cpp/zxing-core/core/src/pdf417/PDFWriter.h +74 -0
- package/cpp/zxing-core/core/src/pdf417/ZXBigInteger.cpp +623 -0
- package/cpp/zxing-core/core/src/pdf417/ZXBigInteger.h +81 -0
- package/cpp/zxing-core/core/src/pdf417/ZXNullable.h +105 -0
- package/cpp/zxing-core/core/src/qrcode/QRBitMatrixParser.cpp +294 -0
- package/cpp/zxing-core/core/src/qrcode/QRBitMatrixParser.h +41 -0
- package/cpp/zxing-core/core/src/qrcode/QRCodecMode.cpp +95 -0
- package/cpp/zxing-core/core/src/qrcode/QRCodecMode.h +59 -0
- package/cpp/zxing-core/core/src/qrcode/QRDataBlock.cpp +93 -0
- package/cpp/zxing-core/core/src/qrcode/QRDataBlock.h +52 -0
- package/cpp/zxing-core/core/src/qrcode/QRDataMask.h +50 -0
- package/cpp/zxing-core/core/src/qrcode/QRDecoder.cpp +359 -0
- package/cpp/zxing-core/core/src/qrcode/QRDecoder.h +19 -0
- package/cpp/zxing-core/core/src/qrcode/QRDetector.cpp +920 -0
- package/cpp/zxing-core/core/src/qrcode/QRDetector.h +45 -0
- package/cpp/zxing-core/core/src/qrcode/QRECB.h +52 -0
- package/cpp/zxing-core/core/src/qrcode/QREncodeResult.h +33 -0
- package/cpp/zxing-core/core/src/qrcode/QREncoder.cpp +568 -0
- package/cpp/zxing-core/core/src/qrcode/QREncoder.h +21 -0
- package/cpp/zxing-core/core/src/qrcode/QRErrorCorrectionLevel.cpp +51 -0
- package/cpp/zxing-core/core/src/qrcode/QRErrorCorrectionLevel.h +39 -0
- package/cpp/zxing-core/core/src/qrcode/QRFormatInformation.cpp +160 -0
- package/cpp/zxing-core/core/src/qrcode/QRFormatInformation.h +61 -0
- package/cpp/zxing-core/core/src/qrcode/QRMaskUtil.cpp +156 -0
- package/cpp/zxing-core/core/src/qrcode/QRMaskUtil.h +15 -0
- package/cpp/zxing-core/core/src/qrcode/QRMatrixUtil.cpp +318 -0
- package/cpp/zxing-core/core/src/qrcode/QRMatrixUtil.h +25 -0
- package/cpp/zxing-core/core/src/qrcode/QRReader.cpp +149 -0
- package/cpp/zxing-core/core/src/qrcode/QRReader.h +21 -0
- package/cpp/zxing-core/core/src/qrcode/QRVersion.cpp +754 -0
- package/cpp/zxing-core/core/src/qrcode/QRVersion.h +127 -0
- package/cpp/zxing-core/core/src/qrcode/QRWriter.cpp +51 -0
- package/cpp/zxing-core/core/src/qrcode/QRWriter.h +74 -0
- package/cpp/zxing-core/core/version.rc.in +34 -0
- package/cpp/zxing-core/core/zxing.pc.in +8 -0
- package/cpp/zxing-core/wrappers/ios/README.md +25 -0
- package/cpp/zxing-core/wrappers/ios/Sources/Wrapper/Version.h +29 -0
- package/cpp/zxing-core/zxing-cpp.podspec +45 -0
- package/lib/factory.d.ts +10 -0
- package/lib/factory.js +12 -0
- package/lib/index.d.ts +9 -0
- package/lib/index.js +9 -0
- package/lib/specs/Barcode.nitro.d.ts +63 -0
- package/lib/specs/Barcode.nitro.js +1 -0
- package/lib/specs/BarcodeFormat.d.ts +11 -0
- package/lib/specs/BarcodeFormat.js +1 -0
- package/lib/specs/BarcodeScanner.nitro.d.ts +89 -0
- package/lib/specs/BarcodeScanner.nitro.js +1 -0
- package/lib/specs/BarcodeScannerFactory.nitro.d.ts +26 -0
- package/lib/specs/BarcodeScannerFactory.nitro.js +1 -0
- package/lib/specs/BarcodeValueType.d.ts +9 -0
- package/lib/specs/BarcodeValueType.js +1 -0
- package/lib/specs/Point.d.ts +14 -0
- package/lib/specs/Point.js +1 -0
- package/lib/specs/Rect.d.ts +22 -0
- package/lib/specs/Rect.js +1 -0
- package/lib/useBarcodeScanner.d.ts +23 -0
- package/lib/useBarcodeScanner.js +25 -0
- package/nitro.json +20 -0
- package/nitrogen/generated/.gitattributes +1 -0
- package/nitrogen/generated/android/VisionCameraZxing+autolinking.cmake +83 -0
- package/nitrogen/generated/android/VisionCameraZxing+autolinking.gradle +27 -0
- package/nitrogen/generated/android/VisionCameraZxingOnLoad.cpp +49 -0
- package/nitrogen/generated/android/VisionCameraZxingOnLoad.hpp +34 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/zxing/VisionCameraZxingOnLoad.kt +35 -0
- package/nitrogen/generated/ios/VisionCameraZxing+autolinking.rb +62 -0
- package/nitrogen/generated/ios/VisionCameraZxing-Swift-Cxx-Bridge.cpp +17 -0
- package/nitrogen/generated/ios/VisionCameraZxing-Swift-Cxx-Bridge.hpp +27 -0
- package/nitrogen/generated/ios/VisionCameraZxing-Swift-Cxx-Umbrella.hpp +38 -0
- package/nitrogen/generated/ios/VisionCameraZxingAutolinking.mm +35 -0
- package/nitrogen/generated/ios/VisionCameraZxingAutolinking.swift +16 -0
- package/nitrogen/generated/shared/c++/BarcodeFormat.hpp +124 -0
- package/nitrogen/generated/shared/c++/BarcodeScannerOptions.hpp +85 -0
- package/nitrogen/generated/shared/c++/BarcodeValueType.hpp +120 -0
- package/nitrogen/generated/shared/c++/HybridBarcodeScannerSpec.cpp +23 -0
- package/nitrogen/generated/shared/c++/HybridBarcodeScannerSpec.hpp +74 -0
- package/nitrogen/generated/shared/c++/HybridBarcodeSpec.cpp +27 -0
- package/nitrogen/generated/shared/c++/HybridBarcodeSpec.hpp +82 -0
- package/nitrogen/generated/shared/c++/HybridZXingBarcodeScannerFactorySpec.cpp +21 -0
- package/nitrogen/generated/shared/c++/HybridZXingBarcodeScannerFactorySpec.hpp +67 -0
- package/nitrogen/generated/shared/c++/Point.hpp +87 -0
- package/nitrogen/generated/shared/c++/Rect.hpp +95 -0
- package/nitrogen/generated/shared/c++/TargetBarcodeFormat.hpp +124 -0
- package/package.json +108 -0
- package/react-native.config.js +16 -0
- package/src/factory.ts +26 -0
- package/src/index.ts +9 -0
- package/src/specs/Barcode.nitro.ts +70 -0
- package/src/specs/BarcodeFormat.ts +31 -0
- package/src/specs/BarcodeScanner.nitro.ts +91 -0
- package/src/specs/BarcodeScannerFactory.nitro.ts +27 -0
- package/src/specs/BarcodeValueType.ts +25 -0
- package/src/specs/Point.ts +14 -0
- package/src/specs/Rect.ts +22 -0
- package/src/useBarcodeScanner.ts +36 -0
|
@@ -0,0 +1,920 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2016 Nu-book Inc.
|
|
3
|
+
* Copyright 2016 ZXing authors
|
|
4
|
+
* Copyright 2020 Axel Waggershauser
|
|
5
|
+
* Copyright 2023 gitlost
|
|
6
|
+
*/
|
|
7
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
8
|
+
|
|
9
|
+
#include "QRDetector.h"
|
|
10
|
+
|
|
11
|
+
#include "BitArray.h"
|
|
12
|
+
#include "BitMatrix.h"
|
|
13
|
+
#include "BitMatrixCursor.h"
|
|
14
|
+
#include "ConcentricFinder.h"
|
|
15
|
+
#include "GridSampler.h"
|
|
16
|
+
#include "LogMatrix.h"
|
|
17
|
+
#include "Matrix.h"
|
|
18
|
+
#include "Pattern.h"
|
|
19
|
+
#include "QRFormatInformation.h"
|
|
20
|
+
#include "QRVersion.h"
|
|
21
|
+
#include "Quadrilateral.h"
|
|
22
|
+
#include "RegressionLine.h"
|
|
23
|
+
|
|
24
|
+
#include <algorithm>
|
|
25
|
+
#include <cmath>
|
|
26
|
+
#include <cstdlib>
|
|
27
|
+
#include <iterator>
|
|
28
|
+
#include <map>
|
|
29
|
+
#include <numbers>
|
|
30
|
+
#include <utility>
|
|
31
|
+
#include <vector>
|
|
32
|
+
|
|
33
|
+
#ifdef PRINT_DEBUG
|
|
34
|
+
#include "BitMatrixIO.h"
|
|
35
|
+
#else
|
|
36
|
+
#define printf(...){}
|
|
37
|
+
#endif
|
|
38
|
+
|
|
39
|
+
namespace ZXing::QRCode {
|
|
40
|
+
|
|
41
|
+
constexpr auto PATTERN = FixedPattern<5, 7>{1, 1, 3, 1, 1};
|
|
42
|
+
constexpr bool E2E = true;
|
|
43
|
+
|
|
44
|
+
PatternView FindPattern(const PatternView& view)
|
|
45
|
+
{
|
|
46
|
+
return FindLeftGuard<PATTERN.size()>(view, PATTERN.size(), [](const PatternView& view, int spaceInPixel) {
|
|
47
|
+
// perform a fast plausibility test for 1:1:3:1:1 pattern
|
|
48
|
+
if (view[2] < 3 || view[2] < 2 * std::max(view[0], view[4]) || view[2] < std::max(view[1], view[3]))
|
|
49
|
+
return 0.;
|
|
50
|
+
return IsPattern<E2E>(view, PATTERN, spaceInPixel, 0.1); // the requires 4, here we accept almost 0
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
std::vector<ConcentricPattern> FindFinderPatterns(const BitMatrix& image, bool tryHarder)
|
|
55
|
+
{
|
|
56
|
+
constexpr int MIN_SKIP = 3; // 1 pixel/module times 3 modules/center
|
|
57
|
+
constexpr int MAX_MODULES_FAST = 20 * 4 + 17; // support up to version 20 for mobile clients
|
|
58
|
+
|
|
59
|
+
// Let's assume that the maximum version QR Code we support takes up 1/4 the height of the
|
|
60
|
+
// image, and then account for the center being 3 modules in size. This gives the smallest
|
|
61
|
+
// number of pixels the center could be, so skip this often. When trying harder, look for all
|
|
62
|
+
// QR versions regardless of how dense they are.
|
|
63
|
+
int height = image.height();
|
|
64
|
+
int skip = (3 * height) / (4 * MAX_MODULES_FAST);
|
|
65
|
+
if (skip < MIN_SKIP || tryHarder)
|
|
66
|
+
skip = MIN_SKIP;
|
|
67
|
+
|
|
68
|
+
std::vector<ConcentricPattern> res;
|
|
69
|
+
[[maybe_unused]] int N = 0;
|
|
70
|
+
PatternRow row;
|
|
71
|
+
|
|
72
|
+
for (int y = skip - 1; y < height; y += skip) {
|
|
73
|
+
GetPatternRow(image, y, row, false);
|
|
74
|
+
PatternView next = row;
|
|
75
|
+
|
|
76
|
+
while (next = FindPattern(next), next.isValid()) {
|
|
77
|
+
PointF p(next.pixelsInFront() + next[0] + next[1] + next[2] / 2.0, y + 0.5);
|
|
78
|
+
|
|
79
|
+
// make sure p is not 'inside' an already found pattern area
|
|
80
|
+
if (FindIf(res, [p](const auto& old) { return distance(p, old) < old.size / 2; }) == res.end()) {
|
|
81
|
+
log(p);
|
|
82
|
+
N++;
|
|
83
|
+
auto width = 2 * next.sum(); // the factor 2 allows for a maximum aspect ratio of 4:1 due to perspective distortion
|
|
84
|
+
auto pattern = LocateConcentricPattern<E2E>(image, PATTERN, p, width);
|
|
85
|
+
if (pattern) {
|
|
86
|
+
log(*pattern, 3);
|
|
87
|
+
log(*pattern + PointF(.2, 0), 3);
|
|
88
|
+
log(*pattern - PointF(.2, 0), 3);
|
|
89
|
+
log(*pattern + PointF(0, .2), 3);
|
|
90
|
+
log(*pattern - PointF(0, .2), 3);
|
|
91
|
+
assert(image.get(pattern->x, pattern->y));
|
|
92
|
+
res.push_back(*pattern);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
next.skipPair();
|
|
97
|
+
next.skipPair();
|
|
98
|
+
next.extend();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
printf("FPs: FindPattern: %d, LocateConcentric: %d\n", N, Size(res));
|
|
103
|
+
|
|
104
|
+
return res;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @brief GenerateFinderPatternSets
|
|
109
|
+
* @param patterns list of ConcentricPattern objects, i.e. found finder pattern squares
|
|
110
|
+
* @return list of plausible finder pattern sets, sorted by decreasing plausibility
|
|
111
|
+
*/
|
|
112
|
+
FinderPatternSets GenerateFinderPatternSets(FinderPatterns& patterns)
|
|
113
|
+
{
|
|
114
|
+
std::sort(patterns.begin(), patterns.end(), [](const auto& a, const auto& b) { return a.size > b.size; });
|
|
115
|
+
|
|
116
|
+
struct {
|
|
117
|
+
int rejSize = 0;
|
|
118
|
+
int nearFPs = 0;
|
|
119
|
+
int candidates = 0;
|
|
120
|
+
int rejLegRatio = 0;
|
|
121
|
+
int rejModCount = 0;
|
|
122
|
+
int rejAngle = 0;
|
|
123
|
+
int accepted = 0;
|
|
124
|
+
} stats;
|
|
125
|
+
|
|
126
|
+
auto sets = std::multimap<double, FinderPatternSet>();
|
|
127
|
+
auto squaredDistance = [](const auto* a, const auto* b) {
|
|
128
|
+
// The scaling of the distance based on the b/a size ratio is a very coarse compensation for the shortening effect of
|
|
129
|
+
// the camera projection on slanted symbols. The fact that the size of the finder pattern is proportional to the
|
|
130
|
+
// distance from the camera is used here. This approximation only works if a < b < 2*a (see below).
|
|
131
|
+
// Test image: fix-finderpattern-order.jpg
|
|
132
|
+
// Originally, I scaled the squaredDistance with the (b/a)^2 ratio but that could skew the cosine calculation
|
|
133
|
+
// below too much, resulting in the acceptance of degenerate triangles (a, b and c on a line).
|
|
134
|
+
return dot((*a - *b), (*a - *b)) * double(b->size) / a->size;
|
|
135
|
+
};
|
|
136
|
+
const double cosUpper = std::cos(60. / 180 * std::numbers::pi);
|
|
137
|
+
const double cosLower = std::cos(120. / 180 * std::numbers::pi);
|
|
138
|
+
|
|
139
|
+
#if 1
|
|
140
|
+
if (Size(patterns) < 3)
|
|
141
|
+
return {};
|
|
142
|
+
|
|
143
|
+
// Bin finder patterns into spatial bins to reduce the number of candidates to compare with geometry heuristics below.
|
|
144
|
+
// For each finder pattern, we only compare it to patterns in bins that are not further away than the largest symbol (177 modules)
|
|
145
|
+
// can occupy. We search from inside out and stop after we found a limited number of candidates which reduces the complexity from
|
|
146
|
+
// O(n^3) to O(n). E.g. a sample with 140 small QRCodes has 420 finder patterns, which results in 12 million candidates to process
|
|
147
|
+
// while with the binning, we only compare 20k candidates -> total runtime goes from 190ms to 9ms.
|
|
148
|
+
auto [mX, MX] = std::ranges::minmax_element(patterns, {}, &PointF::x);
|
|
149
|
+
auto [mY, MY] = std::ranges::minmax_element(patterns, {}, &PointF::y);
|
|
150
|
+
int medianSize = patterns[Size(patterns) / 2].size;
|
|
151
|
+
int binSize = std::max(32, medianSize * 3); // 3 for minimum symbol size of 21 modules
|
|
152
|
+
Matrix<std::vector<int>> bins(std::ceil((MX->x - mX->x + 1) / binSize), std::ceil((MY->y - mY->y + 1) / binSize));
|
|
153
|
+
|
|
154
|
+
printf("medianSize=%d binSize=%d bins=(%dx%d) ", medianSize, binSize, bins.width(), bins.height());
|
|
155
|
+
|
|
156
|
+
auto bin = [&](PointF p) {
|
|
157
|
+
return PointI(std::clamp(int((p.x - mX->x) / binSize), 0, bins.width() - 1),
|
|
158
|
+
std::clamp(int((p.y - mY->y) / binSize), 0, bins.height() - 1));
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
for (int idx = 0; idx < Size(patterns); ++idx)
|
|
162
|
+
bins(bin(patterns[idx])).push_back(idx);
|
|
163
|
+
|
|
164
|
+
constexpr double maxModuleCount = 177 * 1.5;
|
|
165
|
+
// manually tuned to work with e.g. https://github.com/eventualbuddha/zedbar/blob/f0d9d9fa6158c108a21f7cde42c0339fb32dff69/examples/qr-code-140-grid02.jpg
|
|
166
|
+
constexpr size_t maxCandidates = 15;
|
|
167
|
+
auto candidates = std::vector<int>();
|
|
168
|
+
candidates.reserve(maxCandidates * 2);
|
|
169
|
+
|
|
170
|
+
int nbPatterns = Size(patterns);
|
|
171
|
+
bool useFilters = nbPatterns > 5; // for a small number of patterns, we apply no/less filters (like size ratio, leg ratio, angle)
|
|
172
|
+
for (int i = 0; i < nbPatterns - 2; i++) {
|
|
173
|
+
const auto* c0 = &patterns[i];
|
|
174
|
+
double maxDistToC = c0->size / 7.0 * maxModuleCount;
|
|
175
|
+
auto cBin = bin(*c0);
|
|
176
|
+
int binRadius = std::ceil(maxDistToC / binSize);
|
|
177
|
+
candidates.clear();
|
|
178
|
+
|
|
179
|
+
for (auto d : Spiral(binRadius)) {
|
|
180
|
+
auto b = cBin + d;
|
|
181
|
+
if (b.x < 0 || b.x >= bins.width() || b.y < 0 || b.y >= bins.height())
|
|
182
|
+
continue;
|
|
183
|
+
|
|
184
|
+
for (int idx : bins(b)) {
|
|
185
|
+
if (idx <= i)
|
|
186
|
+
continue;
|
|
187
|
+
|
|
188
|
+
const auto* p = &patterns[idx];
|
|
189
|
+
if (useFilters && c0->size > p->size * 2 + 2) {
|
|
190
|
+
stats.rejSize++;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
candidates.push_back(idx);
|
|
195
|
+
stats.nearFPs++;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (candidates.size() >= maxCandidates)
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
for (int u = 0; u < Size(candidates) - 1; ++u) {
|
|
203
|
+
for (int v = u + 1; v < Size(candidates); ++v) {
|
|
204
|
+
stats.candidates++;
|
|
205
|
+
int j = candidates[u];
|
|
206
|
+
int k = candidates[v];
|
|
207
|
+
|
|
208
|
+
// patterns is sorted descending by size (the larger the pattern, the less likely is it noise),
|
|
209
|
+
// but the geometry/size heuristics below assume a <= b <= c in size. Keep that convention by remapping indices.
|
|
210
|
+
const auto* a = &patterns[std::max(j, k)];
|
|
211
|
+
const auto* b = &patterns[std::min(j, k)];
|
|
212
|
+
const auto* c = c0;
|
|
213
|
+
|
|
214
|
+
#else
|
|
215
|
+
|
|
216
|
+
int nbPatterns = Size(patterns);
|
|
217
|
+
for (int i = 0; i < nbPatterns - 2; i++) {
|
|
218
|
+
for (int j = i + 1; j < nbPatterns - 1; j++) {
|
|
219
|
+
for (int k = j + 1; k < nbPatterns - 0; k++) {
|
|
220
|
+
stats.candidates++;
|
|
221
|
+
// patterns is sorted descending by size (the larger the pattern, the less likely is it noise),
|
|
222
|
+
// but the geometry/size heuristics below assume a <= b <= c in size. Keep that convention by remapping i/j/k.
|
|
223
|
+
const auto* a = &patterns[k];
|
|
224
|
+
const auto* b = &patterns[j];
|
|
225
|
+
const auto* c = &patterns[i];
|
|
226
|
+
|
|
227
|
+
// if the pattern sizes are too different to be part of the same symbol, skip this
|
|
228
|
+
// and the rest of the innermost loop (sorted list)
|
|
229
|
+
if (c->size > a->size * 2)
|
|
230
|
+
break;
|
|
231
|
+
#endif
|
|
232
|
+
|
|
233
|
+
// Orders the three points in an order [A,B,C] such that AB is less than AC
|
|
234
|
+
// and BC is less than AC, and the angle between BC and BA is less than 180 degrees.
|
|
235
|
+
|
|
236
|
+
auto distAB2 = squaredDistance(a, b);
|
|
237
|
+
auto distBC2 = squaredDistance(b, c);
|
|
238
|
+
auto distAC2 = squaredDistance(a, c);
|
|
239
|
+
|
|
240
|
+
if (distBC2 >= distAB2 && distBC2 >= distAC2) {
|
|
241
|
+
std::swap(a, b);
|
|
242
|
+
std::swap(distBC2, distAC2);
|
|
243
|
+
} else if (distAB2 >= distAC2 && distAB2 >= distBC2) {
|
|
244
|
+
std::swap(b, c);
|
|
245
|
+
std::swap(distAB2, distAC2);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Make sure distAB and distBC don't differ more than reasonable:
|
|
249
|
+
// equivalent to distAB > 2 * distBC || distBC > 2 * distAB but avoids sqrt.
|
|
250
|
+
// TODO: make sure the constant 2 is not too conservative for reasonably tilted symbols
|
|
251
|
+
if (useFilters && (distAB2 > 4 * distBC2 || distBC2 > 4 * distAB2)) {
|
|
252
|
+
stats.rejLegRatio++;
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
auto distAB = std::sqrt(distAB2);
|
|
257
|
+
auto distBC = std::sqrt(distBC2);
|
|
258
|
+
|
|
259
|
+
// Estimate the module count and ignore this set if it can not result in a valid decoding
|
|
260
|
+
if (auto moduleCount = (distAB + distBC) / (2 * (a->size + b->size + c->size) / (3 * 7.f)) + 7;
|
|
261
|
+
moduleCount < 21 * 0.9 || moduleCount > 177 * 1.5) { // moduleCount may be overestimated, see above
|
|
262
|
+
stats.rejModCount++;
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Make sure the angle between AB and BC does not deviate from 90° too much
|
|
267
|
+
auto cosAB_BC = (distAB2 + distBC2 - distAC2) / (2 * distAB * distBC);
|
|
268
|
+
if (useFilters && (std::isnan(cosAB_BC) || cosAB_BC > cosUpper || cosAB_BC < cosLower)) {
|
|
269
|
+
stats.rejAngle++;
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// a^2 + b^2 = c^2 (Pythagorean theorem), and a = b (isosceles triangle).
|
|
274
|
+
// Since any right triangle satisfies the formula c^2 - b^2 - a^2 = 0,
|
|
275
|
+
// we need to check both two equal sides separately.
|
|
276
|
+
// The value of |c^2 - 2 * b^2| + |c^2 - 2 * a^2| increases as dissimilarity
|
|
277
|
+
// from isosceles right triangle.
|
|
278
|
+
// double score = (std::abs(distAC2 - 2 * distAB2) + std::abs(distAC2 - 2 * distBC2));
|
|
279
|
+
|
|
280
|
+
// Calculate a score that is used to determine wich sets are most likely to be actual finder pattern sets,
|
|
281
|
+
// the smaller the better. Prefer finder patterns that are close to each with similar distances to each other.
|
|
282
|
+
// Note: experiments incorporating cosAB_BC or the difference of the finder pattern sizes did not yield better results.
|
|
283
|
+
auto score = distAB + distBC + std::abs(distAB - distBC);
|
|
284
|
+
|
|
285
|
+
// arbitrarily limit the number of potential sets
|
|
286
|
+
// (this has performance implications while limiting the maximal number of detected symbols)
|
|
287
|
+
const size_t setSizeLimit = 256;
|
|
288
|
+
if (sets.size() < setSizeLimit || sets.crbegin()->first > score) {
|
|
289
|
+
// Use cross product to figure out whether A and C are correct or flipped.
|
|
290
|
+
// This asks whether BC x BA has a positive z component, which is the arrangement
|
|
291
|
+
// we want for A, B, C. If it's negative then swap A and C.
|
|
292
|
+
if (cross(*c - *b, *a - *b) < 0)
|
|
293
|
+
std::swap(a, c);
|
|
294
|
+
|
|
295
|
+
sets.emplace(score, FinderPatternSet{*a, *b, *c});
|
|
296
|
+
if (sets.size() > setSizeLimit)
|
|
297
|
+
sets.erase(std::prev(sets.end()));
|
|
298
|
+
stats.accepted++;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
printf("rejectSize=%d nearFPs=%d candidates=%d rejectLeg=%d rejectMod=%d rejectAng=%d accepted=%d\n", stats.rejSize,
|
|
305
|
+
stats.nearFPs, stats.candidates, stats.rejLegRatio, stats.rejModCount, stats.rejAngle, stats.accepted);
|
|
306
|
+
|
|
307
|
+
// convert from multimap to vector
|
|
308
|
+
FinderPatternSets res;
|
|
309
|
+
res.reserve(sets.size());
|
|
310
|
+
for (auto& [d, s] : sets)
|
|
311
|
+
res.push_back(s);
|
|
312
|
+
|
|
313
|
+
printf("FPSets: %d\n", Size(res));
|
|
314
|
+
|
|
315
|
+
return res;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
static double EstimateModuleSize(const BitMatrix& image, ConcentricPattern a, ConcentricPattern b)
|
|
319
|
+
{
|
|
320
|
+
BitMatrixCursorF cur(image, a, b - a);
|
|
321
|
+
assert(cur.isBlack());
|
|
322
|
+
|
|
323
|
+
auto pattern = ReadSymmetricPattern<5>(cur, a.size * 2);
|
|
324
|
+
if (!pattern || !IsPattern<true>(*pattern, PATTERN))
|
|
325
|
+
return -1;
|
|
326
|
+
|
|
327
|
+
return (2 * Reduce(*pattern) - (*pattern)[0] - (*pattern)[4]) / 12.0 * length(cur.d);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
struct DimensionEstimate
|
|
331
|
+
{
|
|
332
|
+
int dim = 0;
|
|
333
|
+
double ms = 0;
|
|
334
|
+
int err = 4;
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
static DimensionEstimate EstimateDimension(const BitMatrix& image, ConcentricPattern a, ConcentricPattern b)
|
|
338
|
+
{
|
|
339
|
+
auto ms_a = EstimateModuleSize(image, a, b);
|
|
340
|
+
auto ms_b = EstimateModuleSize(image, b, a);
|
|
341
|
+
|
|
342
|
+
if (ms_a < 0 || ms_b < 0)
|
|
343
|
+
return {};
|
|
344
|
+
|
|
345
|
+
auto moduleSize = (ms_a + ms_b) / 2;
|
|
346
|
+
|
|
347
|
+
int dimension = narrow_cast<int>(std::lround(distance(a, b) / moduleSize) + 7);
|
|
348
|
+
int error = 1 - (dimension % 4);
|
|
349
|
+
|
|
350
|
+
return {dimension + error, moduleSize, std::abs(error)};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
static RegressionLine TraceLine(const BitMatrix& image, PointF p, PointF d, int edge)
|
|
354
|
+
{
|
|
355
|
+
BitMatrixCursorF cur(image, p, d - p);
|
|
356
|
+
RegressionLine line;
|
|
357
|
+
line.setDirectionInward(cur.back());
|
|
358
|
+
|
|
359
|
+
// collect points inside the black line -> backup on 3rd edge
|
|
360
|
+
cur.stepToEdge(edge, 0, edge == 3);
|
|
361
|
+
if (edge == 3)
|
|
362
|
+
cur.turnBack();
|
|
363
|
+
|
|
364
|
+
auto curI = BitMatrixCursorI(image, PointI(cur.p), PointI(mainDirection(cur.d)));
|
|
365
|
+
// make sure curI positioned such that the white->black edge is directly behind
|
|
366
|
+
// Test image: fix-traceline.jpg
|
|
367
|
+
while (curI.isIn() && !curI.edgeAtBack()) {
|
|
368
|
+
if (curI.edgeAtLeft())
|
|
369
|
+
curI.turnRight();
|
|
370
|
+
else if (curI.edgeAtRight())
|
|
371
|
+
curI.turnLeft();
|
|
372
|
+
else
|
|
373
|
+
curI.step(-1);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
for (auto dir : {Direction::LEFT, Direction::RIGHT}) {
|
|
377
|
+
auto c = BitMatrixCursorI(image, curI.p, curI.direction(dir));
|
|
378
|
+
auto stepCount = static_cast<int>(maxAbsComponent(cur.p - p));
|
|
379
|
+
do {
|
|
380
|
+
line.add(centered(c.p));
|
|
381
|
+
} while (--stepCount > 0 && c.stepAlongEdge(dir, true));
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
line.evaluate(1.0, true);
|
|
385
|
+
|
|
386
|
+
for (auto p : line.points())
|
|
387
|
+
log(p, 2);
|
|
388
|
+
|
|
389
|
+
return line;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// estimate how tilted the symbol is (return value between 1 and 2, see also above)
|
|
393
|
+
static double EstimateTilt(const FinderPatternSet& fp)
|
|
394
|
+
{
|
|
395
|
+
int min = std::min({fp.bl.size, fp.tl.size, fp.tr.size});
|
|
396
|
+
int max = std::max({fp.bl.size, fp.tl.size, fp.tr.size});
|
|
397
|
+
return double(max) / min;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
static PerspectiveTransform Mod2Pix(int dimension, PointF brOffset, QuadrilateralF pix)
|
|
401
|
+
{
|
|
402
|
+
auto quad = Rectangle(dimension, dimension, 3.5);
|
|
403
|
+
quad[2] = quad[2] - brOffset;
|
|
404
|
+
return {quad, pix};
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
static std::optional<PointF> LocateAlignmentPattern(const BitMatrix& image, int moduleSize, PointF estimate)
|
|
408
|
+
{
|
|
409
|
+
log(estimate, 4);
|
|
410
|
+
|
|
411
|
+
for (auto d : {PointF{0, 0}, {0, -1}, {0, 1}, {-1, 0}, {1, 0}, {-1, -1}, {1, -1}, {1, 1}, {-1, 1},
|
|
412
|
+
#if 1
|
|
413
|
+
}) {
|
|
414
|
+
#else
|
|
415
|
+
{0, -2}, {0, 2}, {-2, 0}, {2, 0}, {-1, -2}, {1, -2}, {-1, 2}, {1, 2}, {-2, -1}, {-2, 1}, {2, -1}, {2, 1}}) {
|
|
416
|
+
#endif
|
|
417
|
+
auto p = estimate + moduleSize * 2.25 * d;
|
|
418
|
+
if (!image.isIn(p))
|
|
419
|
+
continue;
|
|
420
|
+
|
|
421
|
+
auto cor = CenterOfRing(image, PointI(p), moduleSize * 3, 1, false);
|
|
422
|
+
|
|
423
|
+
// if we did not land on a black pixel the concentric pattern finder will fail
|
|
424
|
+
if (!cor || !image.get(*cor))
|
|
425
|
+
continue;
|
|
426
|
+
|
|
427
|
+
if (auto cor1 = CenterOfRing(image, PointI(*cor), moduleSize * 2, 1))
|
|
428
|
+
if (auto cor2 = CenterOfRing(image, PointI(*cor), moduleSize * 3, 2))
|
|
429
|
+
if (distance(*cor1, *cor2) < moduleSize / 2 && cor2->size > cor1->size) {
|
|
430
|
+
auto res = (*cor1 + *cor2) / 2;
|
|
431
|
+
log(res, 3);
|
|
432
|
+
return res;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return {};
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
static const Version* ReadVersion(const BitMatrix& image, int dimension, const PerspectiveTransform& mod2Pix)
|
|
440
|
+
{
|
|
441
|
+
int bits[2] = {};
|
|
442
|
+
|
|
443
|
+
for (bool mirror : {false, true}) {
|
|
444
|
+
// Read top-right/bottom-left version info: 3 wide by 6 tall (depending on mirrored)
|
|
445
|
+
int versionBits = 0;
|
|
446
|
+
for (int y = 5; y >= 0; --y)
|
|
447
|
+
for (int x = dimension - 9; x >= dimension - 11; --x) {
|
|
448
|
+
auto mod = mirror ? PointI{y, x} : PointI{x, y};
|
|
449
|
+
auto pix = mod2Pix(centered(mod));
|
|
450
|
+
if (!image.isIn(pix))
|
|
451
|
+
versionBits = -1;
|
|
452
|
+
else
|
|
453
|
+
AppendBit(versionBits, image.get(pix));
|
|
454
|
+
log(pix, 3);
|
|
455
|
+
}
|
|
456
|
+
bits[static_cast<int>(mirror)] = versionBits;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
return Version::DecodeVersionInformation(bits[0], bits[1]);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
DetectorResults SampleQR(const BitMatrix& image, const FinderPatternSet& fp)
|
|
463
|
+
{
|
|
464
|
+
auto top = EstimateDimension(image, fp.tl, fp.tr);
|
|
465
|
+
auto left = EstimateDimension(image, fp.tl, fp.bl);
|
|
466
|
+
|
|
467
|
+
if (!top.dim && !left.dim)
|
|
468
|
+
co_return;
|
|
469
|
+
|
|
470
|
+
auto best = top.err == left.err ? (top.dim > left.dim ? top : left) : (top.err < left.err ? top : left);
|
|
471
|
+
int dimension = best.dim;
|
|
472
|
+
int moduleSize = static_cast<int>(top.dim == left.dim ? std::midpoint(top.ms, left.ms) : best.ms) + 1;
|
|
473
|
+
|
|
474
|
+
auto br = PointF{-1, -1};
|
|
475
|
+
auto brOffset = PointF{3, 3};
|
|
476
|
+
bool brFound = false;
|
|
477
|
+
|
|
478
|
+
// Everything except version 1 (21 modules) has an alignment pattern. Estimate the center of that by intersecting
|
|
479
|
+
// line extensions of the 1 module wide square around the finder patterns. This could also help with detecting
|
|
480
|
+
// slanted symbols of version 1.
|
|
481
|
+
|
|
482
|
+
// generate 4 lines: outer and inner edge of the 1 module wide black line between the two outer and the inner
|
|
483
|
+
// (tl) finder pattern
|
|
484
|
+
auto bl2 = TraceLine(image, fp.bl, fp.tl, 2);
|
|
485
|
+
auto bl3 = TraceLine(image, fp.bl, fp.tl, 3);
|
|
486
|
+
auto tr2 = TraceLine(image, fp.tr, fp.tl, 2);
|
|
487
|
+
auto tr3 = TraceLine(image, fp.tr, fp.tl, 3);
|
|
488
|
+
|
|
489
|
+
if (bl2.isValid() && tr2.isValid() && bl3.isValid() && tr3.isValid()) {
|
|
490
|
+
// intersect both outer and inner line pairs and take the center point between the two intersection points
|
|
491
|
+
auto brInter = (intersect(bl2, tr2) + intersect(bl3, tr3)) / 2;
|
|
492
|
+
log(brInter, 3);
|
|
493
|
+
|
|
494
|
+
if (dimension > 21)
|
|
495
|
+
if (auto brCP = LocateAlignmentPattern(image, moduleSize, brInter))
|
|
496
|
+
br = *brCP;
|
|
497
|
+
|
|
498
|
+
brFound = image.isIn(br);
|
|
499
|
+
if (!brFound)
|
|
500
|
+
br = brInter;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// otherwise or if finder patterns are not square, the simple estimation used by upstream is used as a best guess fallback
|
|
504
|
+
if (!image.isIn(br) || !FitSquareToPoints(image, fp.bl, fp.bl.size, 2, false)) {
|
|
505
|
+
br = fp.tr - fp.tl + fp.bl;
|
|
506
|
+
brOffset = PointF(0, 0);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
log(br, 3);
|
|
510
|
+
auto mod2Pix = Mod2Pix(dimension, brOffset, {fp.tl, fp.tr, br, fp.bl});
|
|
511
|
+
|
|
512
|
+
if( dimension >= Version::SymbolSize(7, Type::Model2).x) {
|
|
513
|
+
auto version = ReadVersion(image, dimension, mod2Pix);
|
|
514
|
+
|
|
515
|
+
// if the version bits are garbage -> discard the detection
|
|
516
|
+
if (!version || std::min(std::abs(version->dimension() - top.dim), std::abs(version->dimension() - left.dim)) > 8)
|
|
517
|
+
co_return;
|
|
518
|
+
if (version->dimension() != dimension) {
|
|
519
|
+
printf("update dimension: %d -> %d\n", dimension, version->dimension());
|
|
520
|
+
dimension = version->dimension();
|
|
521
|
+
mod2Pix = Mod2Pix(dimension, brOffset, {fp.tl, fp.tr, br, fp.bl});
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
#if 1 // finding and evaluating the alignment patterns to enable a tiled sampling of the symbol
|
|
525
|
+
|
|
526
|
+
auto& apM = version->alignmentPatternCenters(); // alignment pattern positions in modules
|
|
527
|
+
auto apP = Matrix<std::optional<PointF>>(Size(apM), Size(apM)); // found/guessed alignment pattern positions in pixels
|
|
528
|
+
const int N = Size(apM) - 1;
|
|
529
|
+
|
|
530
|
+
// project the alignment pattern at module coordinates x/y to pixel coordinate based on current mod2Pix
|
|
531
|
+
auto projectM2P = [&mod2Pix, &apM](int x, int y) { return mod2Pix(centered(PointI(apM[x], apM[y]))); };
|
|
532
|
+
|
|
533
|
+
auto findInnerCornerOfConcentricPattern = [&image, &apP, &projectM2P](int x, int y, const ConcentricPattern& fp) {
|
|
534
|
+
auto pc = *apP.set(x, y, projectM2P(x, y));
|
|
535
|
+
if (auto fpQuad = FindConcentricPatternCorners(image, fp, fp.size, 2))
|
|
536
|
+
for (auto c : *fpQuad)
|
|
537
|
+
if (distance(c, pc) < fp.size / 2)
|
|
538
|
+
apP.set(x, y, c);
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
findInnerCornerOfConcentricPattern(0, 0, fp.tl);
|
|
542
|
+
findInnerCornerOfConcentricPattern(0, N, fp.bl);
|
|
543
|
+
findInnerCornerOfConcentricPattern(N, 0, fp.tr);
|
|
544
|
+
|
|
545
|
+
auto bestGuessAPP = [&](int x, int y){
|
|
546
|
+
if (auto p = apP(x, y))
|
|
547
|
+
return *p;
|
|
548
|
+
return projectM2P(x, y);
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
for (int y = 0; y <= N; ++y)
|
|
552
|
+
for (int x = 0; x <= N; ++x) {
|
|
553
|
+
if (apP(x, y))
|
|
554
|
+
continue;
|
|
555
|
+
|
|
556
|
+
PointF guessed =
|
|
557
|
+
x * y == 0 ? bestGuessAPP(x, y) : bestGuessAPP(x - 1, y) + bestGuessAPP(x, y - 1) - bestGuessAPP(x - 1, y - 1);
|
|
558
|
+
if (auto found = LocateAlignmentPattern(image, moduleSize, guessed))
|
|
559
|
+
apP.set(x, y, found);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// go over the whole set of alignment patters again and try to fill any remaining gap by using available neighbors as guides
|
|
563
|
+
for (int y = 0; y <= N; ++y)
|
|
564
|
+
for (int x = 0; x <= N; ++x) {
|
|
565
|
+
if (apP(x, y))
|
|
566
|
+
continue;
|
|
567
|
+
|
|
568
|
+
// find the two closest valid alignment pattern pixel positions both horizontally and vertically
|
|
569
|
+
std::vector<PointF> hori, verti;
|
|
570
|
+
for (int i = 2; i < 2 * N + 2 && Size(hori) < 2; ++i) {
|
|
571
|
+
int xi = x + i / 2 * (i%2 ? 1 : -1);
|
|
572
|
+
if (0 <= xi && xi <= N && apP(xi, y))
|
|
573
|
+
hori.push_back(*apP(xi, y));
|
|
574
|
+
}
|
|
575
|
+
for (int i = 2; i < 2 * N + 2 && Size(verti) < 2; ++i) {
|
|
576
|
+
int yi = y + i / 2 * (i%2 ? 1 : -1);
|
|
577
|
+
if (0 <= yi && yi <= N && apP(x, yi))
|
|
578
|
+
verti.push_back(*apP(x, yi));
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// if we found 2 each, intersect the two lines that are formed by connecting the point pairs
|
|
582
|
+
if (Size(hori) == 2 && Size(verti) == 2) {
|
|
583
|
+
auto guessed = intersect(RegressionLine(hori[0], hori[1]), RegressionLine(verti[0], verti[1]));
|
|
584
|
+
auto found = LocateAlignmentPattern(image, moduleSize, guessed);
|
|
585
|
+
// search again near that intersection and if the search fails, use the intersection
|
|
586
|
+
if (!found) printf("location guessed at %dx%d\n", x, y);
|
|
587
|
+
apP.set(x, y, found ? *found : guessed);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
if (auto c = apP.get(N, N))
|
|
592
|
+
mod2Pix = Mod2Pix(dimension, PointF(3, 3), {fp.tl, fp.tr, *c, fp.bl});
|
|
593
|
+
|
|
594
|
+
co_yield SampleGrid(image, dimension, dimension, mod2Pix, std::move(apP), apM, apM);
|
|
595
|
+
#endif
|
|
596
|
+
}
|
|
597
|
+
else
|
|
598
|
+
co_yield SampleGrid(image, dimension, dimension, mod2Pix);
|
|
599
|
+
|
|
600
|
+
// if we have not found the br alignment pattern, we check
|
|
601
|
+
// a) if we have a version 1 symbol and tried and failed with the intersection of the trace lines (#1086), or
|
|
602
|
+
// b) if the symbol is almost level and the resolution of the RegressionLines is not sufficient (#199 and estimate-tilt.jpg)
|
|
603
|
+
// we then try the fallback method of sampling the symbol with the br corner extrapolated from the other three corners.
|
|
604
|
+
if (!brFound
|
|
605
|
+
&& ((dimension == 21 && brOffset != PointF(0, 0))
|
|
606
|
+
|| (EstimateTilt(fp) < 1.1 && !(bl2.isHighRes() && bl3.isHighRes() && tr2.isHighRes() && tr3.isHighRes()))))
|
|
607
|
+
{
|
|
608
|
+
mod2Pix = Mod2Pix(dimension, PointF(0, 0), {fp.tl, fp.tr, fp.tr - fp.tl + fp.bl, fp.bl});
|
|
609
|
+
co_yield SampleGrid(image, dimension, dimension, mod2Pix);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* This method detects a code in a "pure" image -- that is, pure monochrome image
|
|
615
|
+
* which contains only an unrotated, unskewed, image of a code, with some white border
|
|
616
|
+
* around it. This is a specialized method that works exceptionally fast in this special
|
|
617
|
+
* case.
|
|
618
|
+
*/
|
|
619
|
+
DetectorResult DetectPureQR(const BitMatrix& image)
|
|
620
|
+
{
|
|
621
|
+
using Pattern = std::array<PatternView::value_type, PATTERN.size()>;
|
|
622
|
+
|
|
623
|
+
#ifdef PRINT_DEBUG
|
|
624
|
+
SaveAsPBM(image, "weg.pbm");
|
|
625
|
+
#endif
|
|
626
|
+
|
|
627
|
+
constexpr int MIN_MODULES = Version::SymbolSize(1, Type::Model2).x;
|
|
628
|
+
|
|
629
|
+
int left, top, width, height;
|
|
630
|
+
if (!image.findBoundingBox(left, top, width, height, MIN_MODULES) || std::abs(width - height) > 1)
|
|
631
|
+
return {};
|
|
632
|
+
auto pos = Rectangle<PointI>(left, top, width, height);
|
|
633
|
+
|
|
634
|
+
const PointI &tl = pos.topLeft(), &tr = pos.topRight(), &bl = pos.bottomLeft();
|
|
635
|
+
Pattern diagonal;
|
|
636
|
+
// allow corners be moved one pixel inside to accommodate for possible aliasing artifacts
|
|
637
|
+
for (auto [p, d] : {std::pair(tl, PointI{1, 1}), {tr, {-1, 1}}, {bl, {1, -1}}}) {
|
|
638
|
+
diagonal = BitMatrixCursorI(image, p, d).readPatternFromBlack<Pattern>(1, width / 3 + 1);
|
|
639
|
+
if (!IsPattern(diagonal, PATTERN))
|
|
640
|
+
return {};
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
PointF::value_t fpWidth = Reduce(diagonal);
|
|
644
|
+
auto dimension =
|
|
645
|
+
EstimateDimension(image, {tl + fpWidth / 2 * PointF(1, 1), fpWidth}, {tr + fpWidth / 2 * PointF(-1, 1), fpWidth}).dim;
|
|
646
|
+
|
|
647
|
+
float moduleSize = float(width) / dimension;
|
|
648
|
+
if (!Version::IsValidSize({dimension, dimension}, Type::Model2) ||
|
|
649
|
+
!image.isIn(PointF{left + moduleSize / 2 + (dimension - 1) * moduleSize,
|
|
650
|
+
top + moduleSize / 2 + (dimension - 1) * moduleSize}))
|
|
651
|
+
return {};
|
|
652
|
+
|
|
653
|
+
#ifdef PRINT_DEBUG
|
|
654
|
+
LogMatrix log;
|
|
655
|
+
LogMatrixWriter lmw(log, image, 5, "grid2.pnm");
|
|
656
|
+
for (int y = 0; y < dimension; y++)
|
|
657
|
+
for (int x = 0; x < dimension; x++)
|
|
658
|
+
log(PointF(left + (x + .5f) * moduleSize, top + (y + .5f) * moduleSize));
|
|
659
|
+
#endif
|
|
660
|
+
|
|
661
|
+
// Now just read off the bits (this is a crop + subsample)
|
|
662
|
+
return {Deflate(image, dimension, dimension, top + moduleSize / 2, left + moduleSize / 2, moduleSize), std::move(pos)};
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
DetectorResult DetectPureMQR(const BitMatrix& image)
|
|
666
|
+
{
|
|
667
|
+
using Pattern = std::array<PatternView::value_type, PATTERN.size()>;
|
|
668
|
+
|
|
669
|
+
constexpr int MIN_MODULES = Version::SymbolSize(1, Type::Micro).x;
|
|
670
|
+
|
|
671
|
+
int left, top, width, height;
|
|
672
|
+
if (!image.findBoundingBox(left, top, width, height, MIN_MODULES) || std::abs(width - height) > 1)
|
|
673
|
+
return {};
|
|
674
|
+
|
|
675
|
+
// allow corners be moved one pixel inside to accommodate for possible aliasing artifacts
|
|
676
|
+
auto diagonal = BitMatrixCursorI(image, {left, top}, {1, 1}).readPatternFromBlack<Pattern>(1);
|
|
677
|
+
if (!IsPattern(diagonal, PATTERN))
|
|
678
|
+
return {};
|
|
679
|
+
|
|
680
|
+
auto fpWidth = Reduce(diagonal);
|
|
681
|
+
float moduleSize = float(fpWidth) / 7;
|
|
682
|
+
int dimension = narrow_cast<int>(std::lround(width / moduleSize));
|
|
683
|
+
|
|
684
|
+
if (!Version::IsValidSize({dimension, dimension}, Type::Micro) ||
|
|
685
|
+
!image.isIn(PointF{left + moduleSize / 2 + (dimension - 1) * moduleSize,
|
|
686
|
+
top + moduleSize / 2 + (dimension - 1) * moduleSize}))
|
|
687
|
+
return {};
|
|
688
|
+
|
|
689
|
+
#ifdef PRINT_DEBUG
|
|
690
|
+
LogMatrix log;
|
|
691
|
+
LogMatrixWriter lmw(log, image, 5, "grid2.pnm");
|
|
692
|
+
for (int y = 0; y < dimension; y++)
|
|
693
|
+
for (int x = 0; x < dimension; x++)
|
|
694
|
+
log(PointF(left + (x + .5f) * moduleSize, top + (y + .5f) * moduleSize));
|
|
695
|
+
#endif
|
|
696
|
+
|
|
697
|
+
// Now just read off the bits (this is a crop + subsample)
|
|
698
|
+
return {Deflate(image, dimension, dimension, top + moduleSize / 2, left + moduleSize / 2, moduleSize),
|
|
699
|
+
Rectangle<PointI>(left, top, width, height)};
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
DetectorResult DetectPureRMQR(const BitMatrix& image)
|
|
703
|
+
{
|
|
704
|
+
constexpr auto SUBPATTERN = FixedPattern<4, 4>{1, 1, 1, 1};
|
|
705
|
+
constexpr auto TIMINGPATTERN = FixedPattern<10, 10>{1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
|
|
706
|
+
|
|
707
|
+
using Pattern = std::array<PatternView::value_type, PATTERN.size()>;
|
|
708
|
+
using SubPattern = std::array<PatternView::value_type, SUBPATTERN.size()>;
|
|
709
|
+
using TimingPattern = std::array<PatternView::value_type, TIMINGPATTERN.size()>;
|
|
710
|
+
|
|
711
|
+
#ifdef PRINT_DEBUG
|
|
712
|
+
SaveAsPBM(image, "weg.pbm");
|
|
713
|
+
#endif
|
|
714
|
+
|
|
715
|
+
constexpr int MIN_MODULES = Version::SymbolSize(1, Type::rMQR).y;
|
|
716
|
+
|
|
717
|
+
int left, top, width, height;
|
|
718
|
+
if (!image.findBoundingBox(left, top, width, height, MIN_MODULES) || height >= width)
|
|
719
|
+
return {};
|
|
720
|
+
auto pos = Rectangle<PointI>(left, top, width, height);
|
|
721
|
+
|
|
722
|
+
const PointI &tl = pos.topLeft(), &tr = pos.topRight(), &bl = pos.bottomLeft(), &br = pos.bottomRight();
|
|
723
|
+
|
|
724
|
+
// allow corners be moved one pixel inside to accommodate for possible aliasing artifacts
|
|
725
|
+
auto diagonal = BitMatrixCursorI(image, tl, {1, 1}).readPatternFromBlack<Pattern>(1);
|
|
726
|
+
if (!IsPattern(diagonal, PATTERN))
|
|
727
|
+
return {};
|
|
728
|
+
|
|
729
|
+
// Finder sub pattern
|
|
730
|
+
auto subdiagonal = BitMatrixCursorI(image, br, {-1, -1}).readPatternFromBlack<SubPattern>(1);
|
|
731
|
+
if (!IsPattern(subdiagonal, SUBPATTERN))
|
|
732
|
+
return {};
|
|
733
|
+
|
|
734
|
+
float moduleSize = Reduce(diagonal) + Reduce(subdiagonal);
|
|
735
|
+
|
|
736
|
+
// Horizontal timing patterns
|
|
737
|
+
for (auto [p, d] : {std::pair(tr, PointI{-1, 0}), {bl, {1, 0}}, {tl, {1, 0}}, {br, {-1, 0}}}) {
|
|
738
|
+
auto cur = BitMatrixCursorI(image, p, d);
|
|
739
|
+
// skip corner / finder / sub pattern edge
|
|
740
|
+
cur.stepToEdge(2 + cur.isWhite());
|
|
741
|
+
auto timing = cur.readPattern<TimingPattern>();
|
|
742
|
+
if (!IsPattern(timing, TIMINGPATTERN))
|
|
743
|
+
return {};
|
|
744
|
+
moduleSize += Reduce(timing);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
moduleSize /= 7 + 4 + 4 * 10; // fp + sub + 4 x timing
|
|
748
|
+
int dimW = narrow_cast<int>(std::lround(width / moduleSize));
|
|
749
|
+
int dimH = narrow_cast<int>(std::lround(height / moduleSize));
|
|
750
|
+
|
|
751
|
+
if (!Version::IsValidSize(PointI{dimW, dimH}, Type::rMQR))
|
|
752
|
+
return {};
|
|
753
|
+
|
|
754
|
+
#ifdef PRINT_DEBUG
|
|
755
|
+
LogMatrix log;
|
|
756
|
+
LogMatrixWriter lmw(log, image, 5, "grid2.pnm");
|
|
757
|
+
for (int y = 0; y < dimH; y++)
|
|
758
|
+
for (int x = 0; x < dimW; x++)
|
|
759
|
+
log(pos.topLeft() + moduleSize * PointF(x + .5f, y + .5f));
|
|
760
|
+
#endif
|
|
761
|
+
|
|
762
|
+
// Now just read off the bits (this is a crop + subsample)
|
|
763
|
+
return {Deflate(image, dimW, dimH, top + moduleSize / 2, left + moduleSize / 2, moduleSize), std::move(pos)};
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
DetectorResult SampleMQR(const BitMatrix& image, const ConcentricPattern& fp)
|
|
767
|
+
{
|
|
768
|
+
auto fpQuad = FindConcentricPatternCorners(image, fp, fp.size, 2);
|
|
769
|
+
if (!fpQuad)
|
|
770
|
+
return {};
|
|
771
|
+
|
|
772
|
+
auto srcQuad = Rectangle(7, 7, 0.5);
|
|
773
|
+
|
|
774
|
+
#if defined(_MSVC_LANG) && !(_MSC_VER >= 1940) // VS2022 17.10 and later work
|
|
775
|
+
// see MSVC issue https://developercommunity.visualstudio.com/t/constexpr-object-is-unable-to-be-used-as/10035065
|
|
776
|
+
static
|
|
777
|
+
#else
|
|
778
|
+
constexpr
|
|
779
|
+
#endif
|
|
780
|
+
const PointI FORMAT_INFO_COORDS[] = {{0, 8}, {1, 8}, {2, 8}, {3, 8}, {4, 8}, {5, 8}, {6, 8}, {7, 8}, {8, 8},
|
|
781
|
+
{8, 7}, {8, 6}, {8, 5}, {8, 4}, {8, 3}, {8, 2}, {8, 1}, {8, 0}};
|
|
782
|
+
|
|
783
|
+
FormatInformation bestFI;
|
|
784
|
+
PerspectiveTransform bestPT;
|
|
785
|
+
BitMatrixCursorF cur(image, {}, {});
|
|
786
|
+
|
|
787
|
+
for (int i = 0; i < 4; ++i) {
|
|
788
|
+
auto mod2Pix = PerspectiveTransform(srcQuad, RotatedCorners(*fpQuad, i));
|
|
789
|
+
|
|
790
|
+
auto check = [&](int i, bool checkOne) {
|
|
791
|
+
auto p = mod2Pix(centered(FORMAT_INFO_COORDS[i]));
|
|
792
|
+
return image.isIn(p) && (!checkOne || image.get(p));
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
// check that we see both innermost timing pattern modules
|
|
796
|
+
if (!check(0, true) || !check(8, false) || !check(16, true))
|
|
797
|
+
continue;
|
|
798
|
+
|
|
799
|
+
int formatInfoBits = 0;
|
|
800
|
+
for (int i = 1; i <= 15; ++i)
|
|
801
|
+
AppendBit(formatInfoBits, cur.blackAt(mod2Pix(centered(FORMAT_INFO_COORDS[i]))));
|
|
802
|
+
|
|
803
|
+
auto fi = FormatInformation::DecodeMQR(formatInfoBits);
|
|
804
|
+
if (fi.hammingDistance < bestFI.hammingDistance) {
|
|
805
|
+
bestFI = fi;
|
|
806
|
+
bestPT = mod2Pix;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
if (!bestFI.isValid())
|
|
811
|
+
return {};
|
|
812
|
+
|
|
813
|
+
const int dim = Version::SymbolSize(bestFI.microVersion, Type::Micro).x;
|
|
814
|
+
|
|
815
|
+
// check that we are in fact not looking at a corner of a non-micro QRCode symbol
|
|
816
|
+
// we accept at most 1/3rd black pixels in the quite zone (in a QRCode symbol we expect about 1/2).
|
|
817
|
+
int blackPixels = 0;
|
|
818
|
+
for (int i = 0; i < dim; ++i) {
|
|
819
|
+
auto px = bestPT(centered(PointI{i, dim}));
|
|
820
|
+
auto py = bestPT(centered(PointI{dim, i}));
|
|
821
|
+
blackPixels += cur.blackAt(px) && cur.blackAt(py);
|
|
822
|
+
}
|
|
823
|
+
if (blackPixels > 2 * dim / 3)
|
|
824
|
+
return {};
|
|
825
|
+
|
|
826
|
+
return SampleGrid(image, dim, dim, bestPT);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
DetectorResult SampleRMQR(const BitMatrix& image, const ConcentricPattern& fp)
|
|
830
|
+
{
|
|
831
|
+
auto fpQuad = FindConcentricPatternCorners(image, fp, fp.size, 2);
|
|
832
|
+
if (!fpQuad)
|
|
833
|
+
return {};
|
|
834
|
+
|
|
835
|
+
auto srcQuad = Rectangle(7, 7, 0.5);
|
|
836
|
+
|
|
837
|
+
static const PointI FORMAT_INFO_EDGE_COORDS[] = {{8, 0}, {9, 0}, {10, 0}, {11, 0}};
|
|
838
|
+
static const PointI FORMAT_INFO_COORDS[] = {
|
|
839
|
+
{11, 3}, {11, 2}, {11, 1},
|
|
840
|
+
{10, 5}, {10, 4}, {10, 3}, {10, 2}, {10, 1},
|
|
841
|
+
{ 9, 5}, { 9, 4}, { 9, 3}, { 9, 2}, { 9, 1},
|
|
842
|
+
{ 8, 5}, { 8, 4}, { 8, 3}, { 8, 2}, { 8, 1},
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
FormatInformation bestFI;
|
|
846
|
+
PerspectiveTransform bestPT;
|
|
847
|
+
BitMatrixCursorF cur(image, {}, {});
|
|
848
|
+
|
|
849
|
+
for (int i = 0; i < 4; ++i) {
|
|
850
|
+
auto mod2Pix = PerspectiveTransform(srcQuad, RotatedCorners(*fpQuad, i));
|
|
851
|
+
|
|
852
|
+
auto check = [&](int i, bool on) {
|
|
853
|
+
return cur.testAt(mod2Pix(centered(FORMAT_INFO_EDGE_COORDS[i]))) == BitMatrixCursorF::Value(on);
|
|
854
|
+
};
|
|
855
|
+
|
|
856
|
+
// check that we see top edge timing pattern modules
|
|
857
|
+
if (!check(0, true) || !check(1, false) || !check(2, true) || !check(3, false))
|
|
858
|
+
continue;
|
|
859
|
+
|
|
860
|
+
uint32_t formatInfoBits = 0;
|
|
861
|
+
for (auto c : FORMAT_INFO_COORDS)
|
|
862
|
+
AppendBit(formatInfoBits, cur.blackAt(mod2Pix(centered(c))));
|
|
863
|
+
|
|
864
|
+
auto fi = FormatInformation::DecodeRMQR(formatInfoBits, 0 /*formatInfoBits2*/);
|
|
865
|
+
if (fi.hammingDistance < bestFI.hammingDistance) {
|
|
866
|
+
bestFI = fi;
|
|
867
|
+
bestPT = mod2Pix;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
if (!bestFI.isValid())
|
|
872
|
+
return {};
|
|
873
|
+
|
|
874
|
+
const PointI dim = Version::SymbolSize(bestFI.microVersion, Type::rMQR);
|
|
875
|
+
|
|
876
|
+
// TODO: this is a WIP
|
|
877
|
+
auto intersectQuads = [](QuadrilateralF& a, QuadrilateralF& b) {
|
|
878
|
+
auto tl = Center(a);
|
|
879
|
+
auto br = Center(b);
|
|
880
|
+
// rotate points such that topLeft of a is furthest away from b and topLeft of b is closest to a
|
|
881
|
+
auto dist2B = [c = br](auto a, auto b) { return distance(a, c) < distance(b, c); };
|
|
882
|
+
auto offsetA = narrow_cast<int>(std::max_element(a.begin(), a.end(), dist2B) - a.begin());
|
|
883
|
+
auto dist2A = [c = tl](auto a, auto b) { return distance(a, c) < distance(b, c); };
|
|
884
|
+
auto offsetB = narrow_cast<int>(std::min_element(b.begin(), b.end(), dist2A) - b.begin());
|
|
885
|
+
|
|
886
|
+
a = RotatedCorners(a, offsetA);
|
|
887
|
+
b = RotatedCorners(b, offsetB);
|
|
888
|
+
|
|
889
|
+
auto tr = (intersect(RegressionLine(a[0], a[1]), RegressionLine(b[1], b[2]))
|
|
890
|
+
+ intersect(RegressionLine(a[3], a[2]), RegressionLine(b[0], b[3])))
|
|
891
|
+
/ 2;
|
|
892
|
+
auto bl = (intersect(RegressionLine(a[0], a[3]), RegressionLine(b[2], b[3]))
|
|
893
|
+
+ intersect(RegressionLine(a[1], a[2]), RegressionLine(b[0], b[1])))
|
|
894
|
+
/ 2;
|
|
895
|
+
|
|
896
|
+
log(tr, 2);
|
|
897
|
+
log(bl, 2);
|
|
898
|
+
|
|
899
|
+
return QuadrilateralF{tl, tr, br, bl};
|
|
900
|
+
};
|
|
901
|
+
|
|
902
|
+
if (auto found = LocateAlignmentPattern(image, fp.size / 7, bestPT(dim - PointF(3, 3)))) {
|
|
903
|
+
log(*found, 2);
|
|
904
|
+
if (auto spQuad = FindConcentricPatternCorners(image, *found, fp.size / 2, 1)) {
|
|
905
|
+
auto dest = intersectQuads(*fpQuad, *spQuad);
|
|
906
|
+
if (dim.y <= 9) {
|
|
907
|
+
bestPT = PerspectiveTransform({{6.5, 0.5}, {dim.x - 1.5, dim.y - 3.5}, {dim.x - 1.5, dim.y - 1.5}, {6.5, 6.5}},
|
|
908
|
+
{fpQuad->topRight(), spQuad->topRight(), spQuad->bottomRight(), fpQuad->bottomRight()});
|
|
909
|
+
} else {
|
|
910
|
+
dest[0] = fp;
|
|
911
|
+
dest[2] = *found;
|
|
912
|
+
bestPT = PerspectiveTransform({{3.5, 3.5}, {dim.x - 2.5, 3.5}, {dim.x - 2.5, dim.y - 2.5}, {3.5, dim.y - 2.5}}, dest);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
return SampleGrid(image, dim.x, dim.y, bestPT);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
} // namespace ZXing::QRCode
|