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,1230 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2016 Nu-book Inc.
|
|
3
|
+
* Copyright 2016 ZXing authors
|
|
4
|
+
* Copyright 2017 Axel Waggershauser
|
|
5
|
+
*/
|
|
6
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
7
|
+
|
|
8
|
+
#include "DMDetector.h"
|
|
9
|
+
|
|
10
|
+
#include "BitMatrix.h"
|
|
11
|
+
#include "BitMatrixCursor.h"
|
|
12
|
+
#include "Matrix.h"
|
|
13
|
+
#include "DetectorResult.h"
|
|
14
|
+
#include "DMVersion.h"
|
|
15
|
+
#include "GridSampler.h"
|
|
16
|
+
#include "LogMatrix.h"
|
|
17
|
+
#include "LocalGrid.h"
|
|
18
|
+
#include "Point.h"
|
|
19
|
+
#include "RegressionLine.h"
|
|
20
|
+
#include "ResultPoint.h"
|
|
21
|
+
#include "StdScope.h"
|
|
22
|
+
#include "WhiteRectDetector.h"
|
|
23
|
+
|
|
24
|
+
#include <algorithm>
|
|
25
|
+
#include <array>
|
|
26
|
+
#include <cassert>
|
|
27
|
+
#include <cmath>
|
|
28
|
+
#include <cstdlib>
|
|
29
|
+
#include <functional>
|
|
30
|
+
#include <map>
|
|
31
|
+
#include <utility>
|
|
32
|
+
#include <vector>
|
|
33
|
+
|
|
34
|
+
#ifndef PRINT_DEBUG
|
|
35
|
+
#define printf(...){}
|
|
36
|
+
#define printv(...){}
|
|
37
|
+
#else
|
|
38
|
+
#define printv(fmt, vec) \
|
|
39
|
+
for (auto v : vec) \
|
|
40
|
+
printf(fmt, v); \
|
|
41
|
+
printf("\n");
|
|
42
|
+
#endif
|
|
43
|
+
|
|
44
|
+
namespace ZXing::DataMatrix {
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The following code is the 'old' code by Sean Owen based on the Java upstream project.
|
|
48
|
+
* It looks for a white rectangle, then cuts the corners until it hits a black pixel, which
|
|
49
|
+
* results in 4 corner points. Then it determines the dimension by counting transitions
|
|
50
|
+
* between the upper and right corners and samples the grid.
|
|
51
|
+
* This code has several limitations compared to the new code below but has one advantage:
|
|
52
|
+
* it works on high resolution scans with noisy/rippled black/white-edges and potentially
|
|
53
|
+
* on partly occluded locator patterns (the surrounding border of modules/pixels). It is
|
|
54
|
+
* therefore kept as a fall-back.
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Simply encapsulates two points and a number of transitions between them.
|
|
59
|
+
*/
|
|
60
|
+
struct ResultPointsAndTransitions
|
|
61
|
+
{
|
|
62
|
+
const ResultPoint* from;
|
|
63
|
+
const ResultPoint* to;
|
|
64
|
+
int transitions;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Counts the number of black/white transitions between two points, using something like Bresenham's algorithm.
|
|
69
|
+
*/
|
|
70
|
+
static ResultPointsAndTransitions TransitionsBetween(const BitMatrix& image, const ResultPoint& from,
|
|
71
|
+
const ResultPoint& to)
|
|
72
|
+
{
|
|
73
|
+
// See QR Code Detector, sizeOfBlackWhiteBlackRun()
|
|
74
|
+
int fromX = static_cast<int>(from.x());
|
|
75
|
+
int fromY = static_cast<int>(from.y());
|
|
76
|
+
int toX = static_cast<int>(to.x());
|
|
77
|
+
int toY = static_cast<int>(to.y());
|
|
78
|
+
bool steep = std::abs(toY - fromY) > std::abs(toX - fromX);
|
|
79
|
+
if (steep) {
|
|
80
|
+
std::swap(fromX, fromY);
|
|
81
|
+
std::swap(toX, toY);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
int dx = std::abs(toX - fromX);
|
|
85
|
+
int dy = std::abs(toY - fromY);
|
|
86
|
+
int error = -dx / 2;
|
|
87
|
+
int ystep = fromY < toY ? 1 : -1;
|
|
88
|
+
int xstep = fromX < toX ? 1 : -1;
|
|
89
|
+
int transitions = 0;
|
|
90
|
+
bool inBlack = image.get(steep ? fromY : fromX, steep ? fromX : fromY);
|
|
91
|
+
for (int x = fromX, y = fromY; x != toX; x += xstep) {
|
|
92
|
+
bool isBlack = image.get(steep ? y : x, steep ? x : y);
|
|
93
|
+
if (isBlack != inBlack) {
|
|
94
|
+
transitions++;
|
|
95
|
+
inBlack = isBlack;
|
|
96
|
+
}
|
|
97
|
+
error += dy;
|
|
98
|
+
if (error > 0) {
|
|
99
|
+
if (y == toY) {
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
y += ystep;
|
|
103
|
+
error -= dx;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return ResultPointsAndTransitions{ &from, &to, transitions };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
static bool IsValidPoint(const ResultPoint& p, int imgWidth, int imgHeight)
|
|
110
|
+
{
|
|
111
|
+
return p.x() >= 0 && p.x() < imgWidth && p.y() > 0 && p.y() < imgHeight;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
template <typename T>
|
|
115
|
+
static float RoundToNearestF(T x)
|
|
116
|
+
{
|
|
117
|
+
return static_cast<float>(std::round(x));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Calculates the position of the white top right module using the output of the rectangle detector
|
|
122
|
+
* for a rectangular matrix
|
|
123
|
+
*/
|
|
124
|
+
static bool CorrectTopRightRectangular(const BitMatrix& image, const ResultPoint& bottomLeft,
|
|
125
|
+
const ResultPoint& bottomRight, const ResultPoint& topLeft,
|
|
126
|
+
const ResultPoint& topRight, int dimensionTop, int dimensionRight,
|
|
127
|
+
ResultPoint& result)
|
|
128
|
+
{
|
|
129
|
+
float corr = RoundToNearestF(distance(bottomLeft, bottomRight)) / static_cast<float>(dimensionTop);
|
|
130
|
+
float norm = RoundToNearestF(distance(topLeft, topRight));
|
|
131
|
+
float cos = (topRight.x() - topLeft.x()) / norm;
|
|
132
|
+
float sin = (topRight.y() - topLeft.y()) / norm;
|
|
133
|
+
|
|
134
|
+
ResultPoint c1(topRight.x() + corr*cos, topRight.y() + corr*sin);
|
|
135
|
+
|
|
136
|
+
corr = RoundToNearestF(distance(bottomLeft, topLeft)) / (float)dimensionRight;
|
|
137
|
+
norm = RoundToNearestF(distance(bottomRight, topRight));
|
|
138
|
+
cos = (topRight.x() - bottomRight.x()) / norm;
|
|
139
|
+
sin = (topRight.y() - bottomRight.y()) / norm;
|
|
140
|
+
|
|
141
|
+
ResultPoint c2(topRight.x() + corr*cos, topRight.y() + corr*sin);
|
|
142
|
+
|
|
143
|
+
if (!IsValidPoint(c1, image.width(), image.height())) {
|
|
144
|
+
if (IsValidPoint(c2, image.width(), image.height())) {
|
|
145
|
+
result = c2;
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
if (!IsValidPoint(c2, image.width(), image.height())) {
|
|
151
|
+
result = c1;
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
int l1 = std::abs(dimensionTop - TransitionsBetween(image, topLeft, c1).transitions) +
|
|
156
|
+
std::abs(dimensionRight - TransitionsBetween(image, bottomRight, c1).transitions);
|
|
157
|
+
int l2 = std::abs(dimensionTop - TransitionsBetween(image, topLeft, c2).transitions) +
|
|
158
|
+
std::abs(dimensionRight - TransitionsBetween(image, bottomRight, c2).transitions);
|
|
159
|
+
|
|
160
|
+
result = l1 <= l2 ? c1 : c2;
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Calculates the position of the white top right module using the output of the rectangle detector
|
|
166
|
+
* for a square matrix
|
|
167
|
+
*/
|
|
168
|
+
static ResultPoint CorrectTopRight(const BitMatrix& image, const ResultPoint& bottomLeft, const ResultPoint& bottomRight,
|
|
169
|
+
const ResultPoint& topLeft, const ResultPoint& topRight, int dimension)
|
|
170
|
+
{
|
|
171
|
+
float corr = RoundToNearestF(distance(bottomLeft, bottomRight)) / (float)dimension;
|
|
172
|
+
float norm = RoundToNearestF(distance(topLeft, topRight));
|
|
173
|
+
float cos = (topRight.x() - topLeft.x()) / norm;
|
|
174
|
+
float sin = (topRight.y() - topLeft.y()) / norm;
|
|
175
|
+
|
|
176
|
+
ResultPoint c1(topRight.x() + corr * cos, topRight.y() + corr * sin);
|
|
177
|
+
|
|
178
|
+
corr = RoundToNearestF(distance(bottomLeft, topLeft)) / (float)dimension;
|
|
179
|
+
norm = RoundToNearestF(distance(bottomRight, topRight));
|
|
180
|
+
cos = (topRight.x() - bottomRight.x()) / norm;
|
|
181
|
+
sin = (topRight.y() - bottomRight.y()) / norm;
|
|
182
|
+
|
|
183
|
+
ResultPoint c2(topRight.x() + corr * cos, topRight.y() + corr * sin);
|
|
184
|
+
|
|
185
|
+
if (!IsValidPoint(c1, image.width(), image.height())) {
|
|
186
|
+
if (!IsValidPoint(c2, image.width(), image.height()))
|
|
187
|
+
return topRight;
|
|
188
|
+
return c2;
|
|
189
|
+
}
|
|
190
|
+
if (!IsValidPoint(c2, image.width(), image.height()))
|
|
191
|
+
return c1;
|
|
192
|
+
|
|
193
|
+
int l1 = std::abs(TransitionsBetween(image, topLeft, c1).transitions -
|
|
194
|
+
TransitionsBetween(image, bottomRight, c1).transitions);
|
|
195
|
+
int l2 = std::abs(TransitionsBetween(image, topLeft, c2).transitions -
|
|
196
|
+
TransitionsBetween(image, bottomRight, c2).transitions);
|
|
197
|
+
return l1 <= l2 ? c1 : c2;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
static DetectorResult SampleGrid(const BitMatrix& image, const ResultPoint& topLeft, const ResultPoint& bottomLeft,
|
|
201
|
+
const ResultPoint& bottomRight, const ResultPoint& topRight, int width, int height)
|
|
202
|
+
{
|
|
203
|
+
return SampleGrid(image, width, height,
|
|
204
|
+
{Rectangle(width, height, 0), {topLeft, topRight, bottomRight, bottomLeft}});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Returns the z component of the cross product between vectors BC and BA.
|
|
209
|
+
*/
|
|
210
|
+
static float CrossProductZ(const ResultPoint& a, const ResultPoint& b, const ResultPoint& c)
|
|
211
|
+
{
|
|
212
|
+
return (c.x() - b.x())*(a.y() - b.y()) - (c.y() - b.y())*(a.x() - b.x());
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Orders an array of three ResultPoints in an order [A,B,C] such that AB is less than AC
|
|
217
|
+
* and BC is less than AC, and the angle between BC and BA is less than 180 degrees.
|
|
218
|
+
*/
|
|
219
|
+
static void OrderByBestPatterns(const ResultPoint*& p0, const ResultPoint*& p1, const ResultPoint*& p2)
|
|
220
|
+
{
|
|
221
|
+
// Find distances between pattern centers
|
|
222
|
+
auto zeroOneDistance = distance(*p0, *p1);
|
|
223
|
+
auto oneTwoDistance = distance(*p1, *p2);
|
|
224
|
+
auto zeroTwoDistance = distance(*p0, *p2);
|
|
225
|
+
|
|
226
|
+
const ResultPoint* pointA;
|
|
227
|
+
const ResultPoint* pointB;
|
|
228
|
+
const ResultPoint* pointC;
|
|
229
|
+
// Assume one closest to other two is B; A and C will just be guesses at first
|
|
230
|
+
if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance) {
|
|
231
|
+
pointB = p0;
|
|
232
|
+
pointA = p1;
|
|
233
|
+
pointC = p2;
|
|
234
|
+
}
|
|
235
|
+
else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance) {
|
|
236
|
+
pointB = p1;
|
|
237
|
+
pointA = p0;
|
|
238
|
+
pointC = p2;
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
pointB = p2;
|
|
242
|
+
pointA = p0;
|
|
243
|
+
pointC = p1;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Use cross product to figure out whether A and C are correct or flipped.
|
|
247
|
+
// This asks whether BC x BA has a positive z component, which is the arrangement
|
|
248
|
+
// we want for A, B, C. If it's negative, then we've got it flipped around and
|
|
249
|
+
// should swap A and C.
|
|
250
|
+
if (CrossProductZ(*pointA, *pointB, *pointC) < 0.0f) {
|
|
251
|
+
std::swap(pointA, pointC);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
p0 = pointA;
|
|
255
|
+
p1 = pointB;
|
|
256
|
+
p2 = pointC;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
static DetectorResult DetectOld(const BitMatrix& image)
|
|
260
|
+
{
|
|
261
|
+
ResultPoint pointA, pointB, pointC, pointD;
|
|
262
|
+
if (!DetectWhiteRect(image, pointA, pointB, pointC, pointD))
|
|
263
|
+
return {};
|
|
264
|
+
|
|
265
|
+
// Point A and D are across the diagonal from one another,
|
|
266
|
+
// as are B and C. Figure out which are the solid black lines
|
|
267
|
+
// by counting transitions
|
|
268
|
+
std::array transitions = {
|
|
269
|
+
TransitionsBetween(image, pointA, pointB),
|
|
270
|
+
TransitionsBetween(image, pointA, pointC),
|
|
271
|
+
TransitionsBetween(image, pointB, pointD),
|
|
272
|
+
TransitionsBetween(image, pointC, pointD),
|
|
273
|
+
};
|
|
274
|
+
std::sort(transitions.begin(), transitions.end(),
|
|
275
|
+
[](const auto& a, const auto& b) { return a.transitions < b.transitions; });
|
|
276
|
+
|
|
277
|
+
// Sort by number of transitions. First two will be the two solid sides; last two
|
|
278
|
+
// will be the two alternating black/white sides
|
|
279
|
+
const auto& lSideOne = transitions[0];
|
|
280
|
+
const auto& lSideTwo = transitions[1];
|
|
281
|
+
|
|
282
|
+
// We accept at most 4 transitions inside the L pattern (i.e. 2 corruptions) to reduce false positive FormatErrors
|
|
283
|
+
if (lSideTwo.transitions > 2)
|
|
284
|
+
return {};
|
|
285
|
+
|
|
286
|
+
// Figure out which point is their intersection by tallying up the number of times we see the
|
|
287
|
+
// endpoints in the four endpoints. One will show up twice.
|
|
288
|
+
std::map<const ResultPoint*, int> pointCount;
|
|
289
|
+
pointCount[lSideOne.from] += 1;
|
|
290
|
+
pointCount[lSideOne.to] += 1;
|
|
291
|
+
pointCount[lSideTwo.from] += 1;
|
|
292
|
+
pointCount[lSideTwo.to] += 1;
|
|
293
|
+
|
|
294
|
+
const ResultPoint* bottomRight = nullptr;
|
|
295
|
+
const ResultPoint* bottomLeft = nullptr;
|
|
296
|
+
const ResultPoint* topLeft = nullptr;
|
|
297
|
+
for (const auto& [point, count] : pointCount) {
|
|
298
|
+
if (count == 2) {
|
|
299
|
+
bottomLeft = point; // this is definitely the bottom left, then -- end of two L sides
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
// Otherwise it's either top left or bottom right -- just assign the two arbitrarily now
|
|
303
|
+
if (bottomRight == nullptr) {
|
|
304
|
+
bottomRight = point;
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
topLeft = point;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (bottomRight == nullptr || bottomLeft == nullptr || topLeft == nullptr)
|
|
313
|
+
return {};
|
|
314
|
+
|
|
315
|
+
// Bottom left is correct but top left and bottom right might be switched
|
|
316
|
+
// Use the dot product trick to sort them out
|
|
317
|
+
OrderByBestPatterns(bottomRight, bottomLeft, topLeft);
|
|
318
|
+
|
|
319
|
+
// Which point didn't we find in relation to the "L" sides? that's the top right corner
|
|
320
|
+
const ResultPoint* topRight;
|
|
321
|
+
if (pointCount.find(&pointA) == pointCount.end()) {
|
|
322
|
+
topRight = &pointA;
|
|
323
|
+
}
|
|
324
|
+
else if (pointCount.find(&pointB) == pointCount.end()) {
|
|
325
|
+
topRight = &pointB;
|
|
326
|
+
}
|
|
327
|
+
else if (pointCount.find(&pointC) == pointCount.end()) {
|
|
328
|
+
topRight = &pointC;
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
topRight = &pointD;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Next determine the dimension by tracing along the top or right side and counting black/white
|
|
335
|
+
// transitions. Since we start inside a black module, we should see a number of transitions
|
|
336
|
+
// equal to 1 less than the code dimension. Well, actually 2 less, because we are going to
|
|
337
|
+
// end on a black module:
|
|
338
|
+
|
|
339
|
+
// The top right point is actually the corner of a module, which is one of the two black modules
|
|
340
|
+
// adjacent to the white module at the top right. Tracing to that corner from either the top left
|
|
341
|
+
// or bottom right should work here.
|
|
342
|
+
|
|
343
|
+
int dimensionTop = TransitionsBetween(image, *topLeft, *topRight).transitions;
|
|
344
|
+
int dimensionRight = TransitionsBetween(image, *bottomRight, *topRight).transitions;
|
|
345
|
+
|
|
346
|
+
if ((dimensionTop & 0x01) == 1) {
|
|
347
|
+
// it can't be odd, so, round... up?
|
|
348
|
+
dimensionTop++;
|
|
349
|
+
}
|
|
350
|
+
dimensionTop += 2;
|
|
351
|
+
|
|
352
|
+
if ((dimensionRight & 0x01) == 1) {
|
|
353
|
+
// it can't be odd, so, round... up?
|
|
354
|
+
dimensionRight++;
|
|
355
|
+
}
|
|
356
|
+
dimensionRight += 2;
|
|
357
|
+
|
|
358
|
+
if (dimensionTop < 10 || dimensionTop > 144 || dimensionRight < 8 || dimensionRight > 144 )
|
|
359
|
+
return {};
|
|
360
|
+
|
|
361
|
+
ResultPoint correctedTopRight;
|
|
362
|
+
|
|
363
|
+
// Rectangular symbols are 6x16, 6x28, 10x24, 10x32, 14x32, or 14x44. If one dimension is more
|
|
364
|
+
// than twice the other, it's certainly rectangular, but to cut a bit more slack we accept it as
|
|
365
|
+
// rectangular if the bigger side is at least 7/4 times the other:
|
|
366
|
+
if (4 * dimensionTop >= 7 * dimensionRight || 4 * dimensionRight >= 7 * dimensionTop) {
|
|
367
|
+
// The matrix is rectangular
|
|
368
|
+
|
|
369
|
+
if (!CorrectTopRightRectangular(image, *bottomLeft, *bottomRight, *topLeft, *topRight, dimensionTop,
|
|
370
|
+
dimensionRight, correctedTopRight)) {
|
|
371
|
+
correctedTopRight = *topRight;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
dimensionTop = TransitionsBetween(image, *topLeft, correctedTopRight).transitions;
|
|
375
|
+
dimensionRight = TransitionsBetween(image, *bottomRight, correctedTopRight).transitions;
|
|
376
|
+
|
|
377
|
+
if ((dimensionTop & 0x01) == 1) {
|
|
378
|
+
// it can't be odd, so, round... up?
|
|
379
|
+
dimensionTop++;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if ((dimensionRight & 0x01) == 1) {
|
|
383
|
+
// it can't be odd, so, round... up?
|
|
384
|
+
dimensionRight++;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
// The matrix is square
|
|
389
|
+
|
|
390
|
+
int dimension = std::min(dimensionRight, dimensionTop);
|
|
391
|
+
// correct top right point to match the white module
|
|
392
|
+
correctedTopRight = CorrectTopRight(image, *bottomLeft, *bottomRight, *topLeft, *topRight, dimension);
|
|
393
|
+
|
|
394
|
+
// Redetermine the dimension using the corrected top right point
|
|
395
|
+
int dimensionCorrected = std::max(TransitionsBetween(image, *topLeft, correctedTopRight).transitions,
|
|
396
|
+
TransitionsBetween(image, *bottomRight, correctedTopRight).transitions);
|
|
397
|
+
dimensionCorrected++;
|
|
398
|
+
if ((dimensionCorrected & 0x01) == 1) {
|
|
399
|
+
dimensionCorrected++;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
dimensionTop = dimensionRight = dimensionCorrected;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return SampleGrid(image, *topLeft, *bottomLeft, *bottomRight, correctedTopRight, dimensionTop, dimensionRight);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* The following code is the 'new' one implemented by Axel Waggershauser and is working completely different.
|
|
410
|
+
* It is performing something like a (back) trace search along edges through the bit matrix, first looking for
|
|
411
|
+
* the 'L'-pattern, then tracing the black/white borders at the top/right. Advantages over the old code are:
|
|
412
|
+
* * works with lower resolution scans (around 2 pixel per module), due to sub-pixel precision grid placement
|
|
413
|
+
* * works with real-world codes that have just one module wide quiet-zone (which is perfectly in spec)
|
|
414
|
+
*/
|
|
415
|
+
|
|
416
|
+
class DMRegressionLine : public RegressionLine
|
|
417
|
+
{
|
|
418
|
+
template <typename Container, typename Filter>
|
|
419
|
+
static double average(const Container& c, Filter f)
|
|
420
|
+
{
|
|
421
|
+
double sum = 0;
|
|
422
|
+
int num = 0;
|
|
423
|
+
for (const auto& v : c)
|
|
424
|
+
if (f(v)) {
|
|
425
|
+
sum += v;
|
|
426
|
+
++num;
|
|
427
|
+
}
|
|
428
|
+
return sum / num;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
public:
|
|
432
|
+
void reverse() { std::reverse(_points.begin(), _points.end()); }
|
|
433
|
+
|
|
434
|
+
double modules(PointF beg, PointF end)
|
|
435
|
+
{
|
|
436
|
+
assert(_points.size() > 3);
|
|
437
|
+
|
|
438
|
+
// re-evaluate and filter out all points too far away. required for the gapSizes calculation.
|
|
439
|
+
evaluate(1.2, true);
|
|
440
|
+
|
|
441
|
+
std::vector<double> gapSizes, modSizes;
|
|
442
|
+
gapSizes.reserve(_points.size());
|
|
443
|
+
|
|
444
|
+
// calculate the distance between the points projected onto the regression line
|
|
445
|
+
for (size_t i = 1; i < _points.size(); ++i)
|
|
446
|
+
gapSizes.push_back(distance(project(_points[i]), project(_points[i - 1])));
|
|
447
|
+
|
|
448
|
+
// calculate the (expected average) distance of two adjacent pixels
|
|
449
|
+
auto unitPixelDist = ZXing::length(bresenhamDirection(_points.back() - _points.front()));
|
|
450
|
+
|
|
451
|
+
// calculate the width of 2 modules (first black pixel to first black pixel)
|
|
452
|
+
double sumFront = distance(beg, project(_points.front())) - unitPixelDist;
|
|
453
|
+
double sumBack = 0; // (last black pixel to last black pixel)
|
|
454
|
+
for (auto dist : gapSizes) {
|
|
455
|
+
if (dist > 1.9 * unitPixelDist)
|
|
456
|
+
modSizes.push_back(std::exchange(sumBack, 0.0));
|
|
457
|
+
sumFront += dist;
|
|
458
|
+
sumBack += dist;
|
|
459
|
+
if (dist > 1.9 * unitPixelDist)
|
|
460
|
+
modSizes.push_back(std::exchange(sumFront, 0.0));
|
|
461
|
+
}
|
|
462
|
+
if (modSizes.empty())
|
|
463
|
+
return 0;
|
|
464
|
+
modSizes.push_back(sumFront + distance(end, project(_points.back())));
|
|
465
|
+
modSizes.front() = 0; // the first element is an invalid sumBack value, would be pop_front() if vector supported this
|
|
466
|
+
auto lineLength = distance(beg, end) - unitPixelDist;
|
|
467
|
+
auto [iMin, iMax] = std::minmax_element(modSizes.begin() + 1, modSizes.end());
|
|
468
|
+
auto meanModSize = average(modSizes, [](double dist){ return dist > 0; });
|
|
469
|
+
|
|
470
|
+
printf("unit pixel dist: %.1f\n", unitPixelDist);
|
|
471
|
+
printf("lineLength: %.1f, meanModSize: %.1f (min: %.1f, max: %.1f), gaps: %lu\n", lineLength, meanModSize, *iMin, *iMax,
|
|
472
|
+
modSizes.size());
|
|
473
|
+
printf("modSizes: ");
|
|
474
|
+
printv("%.1f ", modSizes);
|
|
475
|
+
|
|
476
|
+
if (*iMax > 2 * *iMin) {
|
|
477
|
+
for (int i = 1; i < Size(modSizes) - 2; ++i) {
|
|
478
|
+
if (modSizes[i] > 0 && modSizes[i] + modSizes[i + 2] < meanModSize * 1.4)
|
|
479
|
+
modSizes[i] += std::exchange(modSizes[i + 2], 0);
|
|
480
|
+
else if (modSizes[i] > meanModSize * 1.6)
|
|
481
|
+
modSizes[i] = 0;
|
|
482
|
+
}
|
|
483
|
+
printf("filtered: ");
|
|
484
|
+
printv("%.1f ", modSizes);
|
|
485
|
+
|
|
486
|
+
meanModSize = average(modSizes, [](double dist) { return dist > 0; });
|
|
487
|
+
}
|
|
488
|
+
printf("post filter meanModSize: %.1f\n", meanModSize);
|
|
489
|
+
|
|
490
|
+
return lineLength / meanModSize;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
bool truncateIfLShape()
|
|
494
|
+
{
|
|
495
|
+
auto lenThis = Size(_points);
|
|
496
|
+
auto lineAB = RegressionLine(_points.front(), _points.back());
|
|
497
|
+
if (lenThis < 16 || lineAB.distance(_points[lenThis / 2]) < 5)
|
|
498
|
+
return false;
|
|
499
|
+
|
|
500
|
+
auto maxP = _points.begin();
|
|
501
|
+
double maxD = 0.0;
|
|
502
|
+
for (auto p = _points.begin(); p != _points.end(); ++p) {
|
|
503
|
+
auto d = lineAB.distance(*p);
|
|
504
|
+
if (d > maxD) {
|
|
505
|
+
maxP = p;
|
|
506
|
+
maxD = d;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
auto lenL = distance(_points.front(), *maxP) - 1;
|
|
511
|
+
auto lenB = distance(*maxP, _points.back()) - 1;
|
|
512
|
+
if (maxD < std::min(lenL, lenB) / 2)
|
|
513
|
+
return false;
|
|
514
|
+
|
|
515
|
+
setDirectionInward(_points.back() - *maxP);
|
|
516
|
+
|
|
517
|
+
_points.resize(std::distance(_points.begin(), maxP) - 1);
|
|
518
|
+
|
|
519
|
+
return true;
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
class EdgeTracer : public BitMatrixCursorF
|
|
524
|
+
{
|
|
525
|
+
enum class StepResult { FOUND, OPEN_END, CLOSED_END };
|
|
526
|
+
|
|
527
|
+
// force this function inline to allow the compiler optimize for the maxStepSize==1 case in traceLine()
|
|
528
|
+
// this can result in a 10% speedup of the falsepositive use case when build with c++20
|
|
529
|
+
#if defined(__clang__) || defined(__GNUC__)
|
|
530
|
+
inline __attribute__((always_inline))
|
|
531
|
+
#elif defined(_MSC_VER)
|
|
532
|
+
__forceinline
|
|
533
|
+
#endif
|
|
534
|
+
StepResult traceStep(PointF dEdge, int maxStepSize, bool goodDirection)
|
|
535
|
+
{
|
|
536
|
+
dEdge = mainDirection(dEdge);
|
|
537
|
+
for (int breadth = 1; breadth <= (maxStepSize == 1 ? 2 : (goodDirection ? 1 : 3)); ++breadth)
|
|
538
|
+
for (int step = 1; step <= maxStepSize; ++step)
|
|
539
|
+
for (int i = 0; i <= 2*(step/4+1) * breadth; ++i) {
|
|
540
|
+
auto pEdge = p + step * d + (i&1 ? (i+1)/2 : -i/2) * dEdge;
|
|
541
|
+
log(pEdge);
|
|
542
|
+
|
|
543
|
+
if (!blackAt(pEdge + dEdge))
|
|
544
|
+
continue;
|
|
545
|
+
|
|
546
|
+
// found black pixel -> go 'outward' until we hit the b/w border
|
|
547
|
+
for (int j = 0; j < std::max(maxStepSize, 3) && isIn(pEdge); ++j) {
|
|
548
|
+
if (whiteAt(pEdge)) {
|
|
549
|
+
// if we are not making any progress, we still have another endless loop bug
|
|
550
|
+
assert(p != centered(pEdge));
|
|
551
|
+
p = centered(pEdge);
|
|
552
|
+
|
|
553
|
+
if (history && maxStepSize == 1) {
|
|
554
|
+
if (history->get(PointI(p)) == state)
|
|
555
|
+
return StepResult::CLOSED_END;
|
|
556
|
+
history->set(PointI(p), state);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
return StepResult::FOUND;
|
|
560
|
+
}
|
|
561
|
+
pEdge = pEdge - dEdge;
|
|
562
|
+
if (blackAt(pEdge - d))
|
|
563
|
+
pEdge = pEdge - d;
|
|
564
|
+
log(pEdge);
|
|
565
|
+
}
|
|
566
|
+
// no valid b/w border found within reasonable range
|
|
567
|
+
return StepResult::CLOSED_END;
|
|
568
|
+
}
|
|
569
|
+
return StepResult::OPEN_END;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
public:
|
|
573
|
+
using StateMatrix = Matrix<int8_t>;
|
|
574
|
+
StateMatrix* history = nullptr;
|
|
575
|
+
int state = 0;
|
|
576
|
+
|
|
577
|
+
using BitMatrixCursorF::BitMatrixCursor;
|
|
578
|
+
|
|
579
|
+
bool updateDirectionFromOrigin(PointF origin)
|
|
580
|
+
{
|
|
581
|
+
auto old_d = d;
|
|
582
|
+
setDirection(p - origin);
|
|
583
|
+
// if the new direction is pointing "backward", i.e. angle(new, old) > 90 deg -> break
|
|
584
|
+
if (dot(d, old_d) < 0)
|
|
585
|
+
return false;
|
|
586
|
+
// make sure d stays in the same quadrant to prevent an infinite loop
|
|
587
|
+
if (std::abs(d.x) == std::abs(d.y))
|
|
588
|
+
d = mainDirection(old_d) + 0.99f * (d - mainDirection(old_d));
|
|
589
|
+
else if (mainDirection(d) != mainDirection(old_d))
|
|
590
|
+
d = mainDirection(old_d) + 0.99f * mainDirection(d);
|
|
591
|
+
return true;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
bool updateDirectionFromLine(RegressionLine& line)
|
|
595
|
+
{
|
|
596
|
+
return line.evaluate(1.5) && updateDirectionFromOrigin(p - line.project(p) + line.points().front());
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
bool updateDirectionFromLineCentroid(RegressionLine& line)
|
|
600
|
+
{
|
|
601
|
+
// Basically a faster, less accurate version of the above without the line evaluation
|
|
602
|
+
return updateDirectionFromOrigin(line.centroid());
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
bool traceLine(PointF dEdge, RegressionLine& line)
|
|
606
|
+
{
|
|
607
|
+
line.setDirectionInward(dEdge);
|
|
608
|
+
do {
|
|
609
|
+
log(p);
|
|
610
|
+
line.add(p);
|
|
611
|
+
if (line.points().size() % 50 == 10 && !updateDirectionFromLineCentroid(line))
|
|
612
|
+
return false;
|
|
613
|
+
auto stepResult = traceStep(dEdge, 1, line.isValid());
|
|
614
|
+
if (stepResult != StepResult::FOUND)
|
|
615
|
+
return stepResult == StepResult::OPEN_END && line.points().size() > 1 && updateDirectionFromLineCentroid(line);
|
|
616
|
+
} while (true);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
bool traceGaps(PointF dEdge, RegressionLine& line, int maxStepSize, const RegressionLine& finishLine = {}, double minDist = 0)
|
|
620
|
+
{
|
|
621
|
+
line.setDirectionInward(dEdge);
|
|
622
|
+
int gaps = 0, steps = 0, maxStepsPerGap = maxStepSize;
|
|
623
|
+
PointF lastP;
|
|
624
|
+
do {
|
|
625
|
+
// detect an endless loop (lack of progress). if encountered, please report.
|
|
626
|
+
// this fixes a deadlock in falsepositives-1/#570.png and the regression in #574
|
|
627
|
+
if (p == std::exchange(lastP, p) || steps++ > (gaps == 0 ? 2 : gaps + 1) * maxStepsPerGap)
|
|
628
|
+
return false;
|
|
629
|
+
log(p);
|
|
630
|
+
|
|
631
|
+
// if we drifted too far outside of the code, break
|
|
632
|
+
if (line.isValid() && line.signedDistance(p) < -5 && (!line.evaluate() || line.signedDistance(p) < -5))
|
|
633
|
+
return false;
|
|
634
|
+
|
|
635
|
+
// if we are drifting towards the inside of the code, pull the current position back out onto the line
|
|
636
|
+
if (line.isValid() && line.signedDistance(p) > 3) {
|
|
637
|
+
// The current direction d and the line we are tracing are supposed to be roughly parallel.
|
|
638
|
+
// In case the 'go outward' step in traceStep lead us astray, we might end up with a line
|
|
639
|
+
// that is almost perpendicular to d. Then the back-projection below can result in an
|
|
640
|
+
// endless loop. Break if the angle between d and line is greater than 45 deg.
|
|
641
|
+
if (std::abs(dot(normalized(d), line.normal())) > 0.7) // thresh is approx. sin(45 deg)
|
|
642
|
+
return false;
|
|
643
|
+
|
|
644
|
+
// re-evaluate line with all the points up to here before projecting
|
|
645
|
+
if (!line.evaluate(1.5))
|
|
646
|
+
return false;
|
|
647
|
+
|
|
648
|
+
auto np = line.project(p);
|
|
649
|
+
// make sure we are making progress even when back-projecting:
|
|
650
|
+
// consider a 90deg corner, rotated 45deg. we step away perpendicular from the line and get
|
|
651
|
+
// back projected where we left off the line.
|
|
652
|
+
// The 'while' instead of 'if' was introduced to fix the issue with #245. It turns out that
|
|
653
|
+
// np can actually be behind the projection of the last line point and we need 2 steps in d
|
|
654
|
+
// to prevent a dead lock. see #245.png
|
|
655
|
+
while (distance(np, line.project(line.points().back())) < 1)
|
|
656
|
+
np = np + d;
|
|
657
|
+
p = centered(np);
|
|
658
|
+
}
|
|
659
|
+
else {
|
|
660
|
+
auto curStep = line.points().empty() ? PointF() : p - line.points().back();
|
|
661
|
+
auto stepLengthInMainDir = line.points().empty() ? 0.0 : dot(mainDirection(d), curStep);
|
|
662
|
+
line.add(p);
|
|
663
|
+
|
|
664
|
+
if (stepLengthInMainDir > 1 || maxAbsComponent(curStep) >= 2) {
|
|
665
|
+
++gaps;
|
|
666
|
+
if (gaps >= 2 || line.points().size() > 5) {
|
|
667
|
+
if (!updateDirectionFromLine(line))
|
|
668
|
+
return false;
|
|
669
|
+
// check if the first half of the top-line trace is complete.
|
|
670
|
+
// the minimum code size is 10x10 -> every code has at least 4 gaps
|
|
671
|
+
if (minDist && gaps >= 4 && distance(p, line.points().front()) > minDist) {
|
|
672
|
+
// undo the last insert, it will be inserted again after the restart
|
|
673
|
+
line.pop_back();
|
|
674
|
+
--gaps;
|
|
675
|
+
return true;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
} else if (gaps == 0 && Size(line.points()) >= 2 * maxStepSize) {
|
|
679
|
+
return false; // no point in following a line that has no gaps
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
if (finishLine.isValid())
|
|
684
|
+
UpdateMin(maxStepSize, static_cast<int>(finishLine.signedDistance(p)));
|
|
685
|
+
|
|
686
|
+
auto stepResult = traceStep(dEdge, maxStepSize, line.isValid());
|
|
687
|
+
|
|
688
|
+
if (stepResult != StepResult::FOUND)
|
|
689
|
+
// we are successful iff we found an open end across a valid finishLine
|
|
690
|
+
return stepResult == StepResult::OPEN_END && finishLine.isValid() &&
|
|
691
|
+
static_cast<int>(finishLine.signedDistance(p)) <= maxStepSize + 1;
|
|
692
|
+
} while (true);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
bool traceCorner(PointF dir, PointF& corner)
|
|
696
|
+
{
|
|
697
|
+
step();
|
|
698
|
+
log(p);
|
|
699
|
+
corner = p;
|
|
700
|
+
std::swap(d, dir);
|
|
701
|
+
traceStep(-1 * dir, 2, false);
|
|
702
|
+
printf("turn: %.0f x %.0f -> %.2f, %.2f\n", p.x, p.y, d.x, d.y);
|
|
703
|
+
|
|
704
|
+
return isIn(corner) && isIn(p);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
bool moveToNextWhiteAfterBlack()
|
|
708
|
+
{
|
|
709
|
+
assert(std::abs(d.x + d.y) == 1);
|
|
710
|
+
|
|
711
|
+
FastEdgeToEdgeCounter e2e(BitMatrixCursorI(*img, PointI(p), PointI(d)));
|
|
712
|
+
int steps = e2e.stepToNextEdge(INT_MAX);
|
|
713
|
+
if (!steps)
|
|
714
|
+
return false;
|
|
715
|
+
step(steps);
|
|
716
|
+
if(isWhite())
|
|
717
|
+
return true;
|
|
718
|
+
|
|
719
|
+
steps = e2e.stepToNextEdge(INT_MAX);
|
|
720
|
+
if (!steps)
|
|
721
|
+
return false;
|
|
722
|
+
return step(steps);
|
|
723
|
+
}
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
// Store a LUT mapping module indices to their actual center positions along the edge.
|
|
727
|
+
struct ModuleCenterLUT : std::vector<double>
|
|
728
|
+
{
|
|
729
|
+
using std::vector<double>::vector;
|
|
730
|
+
|
|
731
|
+
double operator()(int i) const { return empty() ? i + 0.5 : (*this)[i]; }
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
// Build a module-center LUT from uniform module coordinates to actual (non-uniform) ones
|
|
735
|
+
// by analyzing the gap structure in timing-pattern traced points. See #794, #1063, #1072.
|
|
736
|
+
static ModuleCenterLUT BuildModuleCenterLUT(const std::vector<PointF>& points, PointF start, PointF end, int numModules,
|
|
737
|
+
const std::function<double(double)>& edgeFracToModule)
|
|
738
|
+
{
|
|
739
|
+
if (Size(points) < 5)
|
|
740
|
+
return {};
|
|
741
|
+
|
|
742
|
+
auto edgeDir = end - start;
|
|
743
|
+
auto edgeLen = length(edgeDir);
|
|
744
|
+
auto unitDir = normalized(edgeDir);
|
|
745
|
+
auto modSize = edgeLen / numModules;
|
|
746
|
+
|
|
747
|
+
// project traced points onto edge direction
|
|
748
|
+
std::vector<double> proj;
|
|
749
|
+
proj.reserve(points.size());
|
|
750
|
+
for (const auto& p : points)
|
|
751
|
+
proj.push_back(dot(p - start, unitDir));
|
|
752
|
+
|
|
753
|
+
bool startsWithGap = false;
|
|
754
|
+
if (proj.front() > proj.back()) {
|
|
755
|
+
std::reverse(proj.begin(), proj.end());
|
|
756
|
+
startsWithGap = true;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// detect gaps (white modules) — use a lower threshold than modules() (1.9×) to catch
|
|
760
|
+
// compressed gaps near edges caused by barrel distortion on curved surfaces
|
|
761
|
+
auto unitPixelDist = length(bresenhamDirection(edgeDir));
|
|
762
|
+
double gapThreshold = 1.4 * unitPixelDist;
|
|
763
|
+
|
|
764
|
+
std::vector<double> gapMids;
|
|
765
|
+
for (int i = 1; i < Size(proj); ++i)
|
|
766
|
+
if (proj[i] - proj[i - 1] > gapThreshold) {
|
|
767
|
+
auto gapMid = (proj[i] + proj[i - 1]) / 2;
|
|
768
|
+
if (gapMids.empty() || gapMid - gapMids.back() > 0.75 * 2 * modSize)
|
|
769
|
+
gapMids.push_back(gapMid);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
if (gapMids.empty()) {
|
|
773
|
+
return {};
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// assign each gap a module index, accounting for missing gaps via spacing ratio
|
|
777
|
+
std::vector<int> modIdx(gapMids.size());
|
|
778
|
+
modIdx[0] = static_cast<int>(std::round((gapMids.front() / modSize - (1.5 + startsWithGap)) / 2)) * 2 + 1 + startsWithGap;
|
|
779
|
+
for (int i = 1; i < Size(gapMids); ++i)
|
|
780
|
+
modIdx[i] = modIdx[i - 1] + std::max(1, static_cast<int>(std::round((gapMids[i] - gapMids[i - 1]) / (2 * modSize)))) * 2;
|
|
781
|
+
|
|
782
|
+
printf("mIdx: ");
|
|
783
|
+
printv("%d ", modIdx);
|
|
784
|
+
|
|
785
|
+
struct Knot
|
|
786
|
+
{
|
|
787
|
+
double mc, corrMc;
|
|
788
|
+
};
|
|
789
|
+
std::vector<Knot> knots;
|
|
790
|
+
knots.reserve(Size(gapMids) + 2);
|
|
791
|
+
knots.push_back({0.0, 0.0});
|
|
792
|
+
for (int k = 0; k < Size(gapMids); ++k)
|
|
793
|
+
knots.push_back({modIdx[k] + 0.5, edgeFracToModule(gapMids[k] / edgeLen)});
|
|
794
|
+
knots.push_back({double(numModules), double(numModules)});
|
|
795
|
+
|
|
796
|
+
// less than 1/2 module deviation: correction not worthwhile
|
|
797
|
+
if (Reduce(knots, 0.0, [](double m, const Knot& k) { return std::max(m, std::abs(k.mc - k.corrMc)); }) < 0.5)
|
|
798
|
+
return {};
|
|
799
|
+
|
|
800
|
+
ModuleCenterLUT centerLUT(numModules);
|
|
801
|
+
|
|
802
|
+
// #define ZXING_USE_POLY_TIMING_REMAP
|
|
803
|
+
// #define ZXING_USE_POLY_TIMING_REMAP_CONSTRAINED
|
|
804
|
+
|
|
805
|
+
#ifdef ZXING_USE_POLY_TIMING_REMAP
|
|
806
|
+
{
|
|
807
|
+
// Fit degree-2 polynomial corrMc = poly[0] + poly[1]*mc + poly[2]*mc^2
|
|
808
|
+
// using least-squares on all knots (including endpoint sentinels at 0 and numModules).
|
|
809
|
+
double s0 = Size(knots), s1 = 0, s2 = 0, s3 = 0, s4 = 0;
|
|
810
|
+
double b0 = 0, b1 = 0, b2 = 0;
|
|
811
|
+
for (const auto& k : knots) {
|
|
812
|
+
double m = k.mc, c = k.corrMc, m2 = m * m;
|
|
813
|
+
s1 += m;
|
|
814
|
+
s2 += m2;
|
|
815
|
+
s3 += m2 * m;
|
|
816
|
+
s4 += m2 * m2;
|
|
817
|
+
b0 += c;
|
|
818
|
+
b1 += m * c;
|
|
819
|
+
b2 += m2 * c;
|
|
820
|
+
}
|
|
821
|
+
// Gaussian elimination on the 3x3 normal equations [A^T A | A^T b]
|
|
822
|
+
double mat[3][4] = {{s0, s1, s2, b0}, {s1, s2, s3, b1}, {s2, s3, s4, b2}};
|
|
823
|
+
for (int col = 0; col < 3; ++col)
|
|
824
|
+
for (int row = col + 1; row < 3; ++row) {
|
|
825
|
+
double f = mat[row][col] / mat[col][col];
|
|
826
|
+
for (int j = col; j <= 3; ++j)
|
|
827
|
+
mat[row][j] -= f * mat[col][j];
|
|
828
|
+
}
|
|
829
|
+
double p2 = mat[2][3] / mat[2][2];
|
|
830
|
+
double p1 = (mat[1][3] - mat[1][2] * p2) / mat[1][1];
|
|
831
|
+
double p0 = (mat[0][3] - mat[0][2] * p2 - mat[0][1] * p1) / mat[0][0];
|
|
832
|
+
for (int i = 0; i < numModules; ++i) {
|
|
833
|
+
double mc = i + 0.5;
|
|
834
|
+
centerLUT[i] = p0 + p1 * mc + p2 * mc * mc;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
#elif defined(ZXING_USE_POLY_TIMING_REMAP_CONSTRAINED)
|
|
838
|
+
{
|
|
839
|
+
// Fit a quadratic that reproduces both sentinel endpoints (0 and N) exactly:
|
|
840
|
+
// corrMc = mc + a * mc * (mc - N).
|
|
841
|
+
// This keeps corrMc(0) == 0 and corrMc(N) == N by construction,
|
|
842
|
+
// while fitting the interior gap knots in the least-squares sense.
|
|
843
|
+
double gg = 0, gb = 0;
|
|
844
|
+
for (const auto& k : knots) {
|
|
845
|
+
double g = k.mc * (k.mc - numModules);
|
|
846
|
+
gg += g * g;
|
|
847
|
+
gb += g * (k.corrMc - k.mc);
|
|
848
|
+
}
|
|
849
|
+
double a = gb / gg;
|
|
850
|
+
double p0 = 0;
|
|
851
|
+
double p1 = 1 - a * numModules;
|
|
852
|
+
double p2 = a;
|
|
853
|
+
for (int i = 0; i < numModules; ++i) {
|
|
854
|
+
double mc = i + 0.5;
|
|
855
|
+
centerLUT[i] = p0 + p1 * mc + p2 * mc * mc;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
#else
|
|
859
|
+
// Fill dense lookup table at module centers via piecewise-linear interpolation.
|
|
860
|
+
int ki = 0;
|
|
861
|
+
for (int i = 0; i < numModules; ++i) {
|
|
862
|
+
double mc = i + 0.5;
|
|
863
|
+
while (ki + 1 < Size(knots) - 1 && knots[ki + 1].mc < mc)
|
|
864
|
+
++ki;
|
|
865
|
+
double denom = knots[ki + 1].mc - knots[ki].mc;
|
|
866
|
+
assert(denom >= 0.1);
|
|
867
|
+
double t = (mc - knots[ki].mc) / denom;
|
|
868
|
+
centerLUT[i] = knots[ki].corrMc + t * (knots[ki + 1].corrMc - knots[ki].corrMc);
|
|
869
|
+
}
|
|
870
|
+
#endif
|
|
871
|
+
|
|
872
|
+
printf("corr: ");
|
|
873
|
+
for (int i = 0; i < numModules; ++i)
|
|
874
|
+
printf("%.2f ", centerLUT[i] - (i + 0.5));
|
|
875
|
+
printf("\n");
|
|
876
|
+
|
|
877
|
+
return centerLUT;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
//TODO: merge this into a generic SampleGrid function
|
|
881
|
+
static DetectorResult SampleGridCorrected(const BitMatrix& image, int width, int height, const PerspectiveTransform& mod2Pix,
|
|
882
|
+
const ModuleCenterLUT& topCenterLUT, const ModuleCenterLUT& rightCenterLUT)
|
|
883
|
+
{
|
|
884
|
+
#ifdef PRINT_DEBUG
|
|
885
|
+
LogMatrix log;
|
|
886
|
+
static int i = 0;
|
|
887
|
+
LogMatrixWriter lmw(log, image, 5, "grid_c" + std::to_string(i++) + ".pnm");
|
|
888
|
+
#endif
|
|
889
|
+
|
|
890
|
+
BitMatrix res(width, height);
|
|
891
|
+
for (int y = 0; y < height; ++y) {
|
|
892
|
+
double my = rightCenterLUT(y);
|
|
893
|
+
for (int x = 0; x < width; ++x) {
|
|
894
|
+
auto p = mod2Pix(PointF{topCenterLUT(x), my});
|
|
895
|
+
if (!image.isIn(p))
|
|
896
|
+
return {};
|
|
897
|
+
#ifdef PRINT_DEBUG
|
|
898
|
+
log(p, 3);
|
|
899
|
+
#endif
|
|
900
|
+
if (image.get(p))
|
|
901
|
+
res.set(x, y);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
auto projectCorner = [&](PointI p) { return PointI(mod2Pix(PointF(p)) + PointF(0.5, 0.5)); };
|
|
906
|
+
return {std::move(res),
|
|
907
|
+
{projectCorner({0, 0}), projectCorner({width, 0}), projectCorner({width, height}), projectCorner({0, height})}};
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
static DetectorResults Scan(EdgeTracer& startTracer, std::array<DMRegressionLine, 4>& lines)
|
|
911
|
+
{
|
|
912
|
+
while (startTracer.moveToNextWhiteAfterBlack()) {
|
|
913
|
+
log(startTracer.p);
|
|
914
|
+
|
|
915
|
+
PointF tl, bl, br, tr;
|
|
916
|
+
auto& [lineL, lineB, lineR, lineT] = lines;
|
|
917
|
+
|
|
918
|
+
for (auto& l : lines)
|
|
919
|
+
l.reset();
|
|
920
|
+
|
|
921
|
+
#ifdef PRINT_DEBUG
|
|
922
|
+
SCOPE_EXIT([&] {
|
|
923
|
+
for (auto& l : lines)
|
|
924
|
+
log(l.points());
|
|
925
|
+
});
|
|
926
|
+
# define CHECK(A) if (!(A)) { printf("broke at %d\n", __LINE__); continue; }
|
|
927
|
+
#else
|
|
928
|
+
# define CHECK(A) if(!(A)) continue
|
|
929
|
+
#endif
|
|
930
|
+
|
|
931
|
+
auto t = startTracer;
|
|
932
|
+
PointF up, right;
|
|
933
|
+
|
|
934
|
+
// follow left leg upwards
|
|
935
|
+
t.turnRight();
|
|
936
|
+
t.state = 1;
|
|
937
|
+
CHECK(t.traceLine(t.right(), lineL));
|
|
938
|
+
CHECK(t.traceCorner(t.right(), tl));
|
|
939
|
+
lineL.reverse();
|
|
940
|
+
auto tlTracer = t;
|
|
941
|
+
|
|
942
|
+
// follow left leg downwards
|
|
943
|
+
t = startTracer;
|
|
944
|
+
t.state = 1;
|
|
945
|
+
t.setDirection(tlTracer.right());
|
|
946
|
+
CHECK(t.traceLine(t.left(), lineL));
|
|
947
|
+
|
|
948
|
+
// check if lineL is L-shaped -> truncate the lower leg and set t to just before the corner
|
|
949
|
+
if (lineL.truncateIfLShape())
|
|
950
|
+
t.p = lineL.points().back();
|
|
951
|
+
t.updateDirectionFromOrigin(tl);
|
|
952
|
+
up = t.back();
|
|
953
|
+
CHECK(t.traceCorner(t.left(), bl));
|
|
954
|
+
|
|
955
|
+
// follow bottom leg right
|
|
956
|
+
t.state = 2;
|
|
957
|
+
CHECK(t.traceLine(t.left(), lineB));
|
|
958
|
+
t.updateDirectionFromOrigin(bl);
|
|
959
|
+
right = t.front();
|
|
960
|
+
CHECK(t.traceCorner(t.left(), br));
|
|
961
|
+
|
|
962
|
+
auto lenL = distance(tl, bl) - 1;
|
|
963
|
+
auto lenB = distance(bl, br) - 1;
|
|
964
|
+
CHECK(lenL >= 8 && lenB >= 10 && lenB >= lenL / 4 && lenB <= lenL * 18);
|
|
965
|
+
|
|
966
|
+
auto maxStepSize = static_cast<int>(lenB / 5 + 1); // datamatrix bottom dim is at least 10
|
|
967
|
+
|
|
968
|
+
// at this point we found a plausible L-shape and are now looking for the b/w pattern at the top and right:
|
|
969
|
+
// follow top row right 'half way' (at least 4 gaps), see traceGaps
|
|
970
|
+
tlTracer.setDirection(right);
|
|
971
|
+
CHECK(tlTracer.traceGaps(tlTracer.right(), lineT, maxStepSize, {}, lenB / 2));
|
|
972
|
+
|
|
973
|
+
maxStepSize = std::min(lineT.length() / 3, static_cast<int>(lenL / 5)) * 2;
|
|
974
|
+
|
|
975
|
+
// follow up until we reach the top line
|
|
976
|
+
t.setDirection(up);
|
|
977
|
+
t.state = 3;
|
|
978
|
+
CHECK(t.traceGaps(t.left(), lineR, maxStepSize, lineT));
|
|
979
|
+
CHECK(t.traceCorner(t.left(), tr));
|
|
980
|
+
|
|
981
|
+
auto lenT = distance(tl, tr) - 1;
|
|
982
|
+
auto lenR = distance(tr, br) - 1;
|
|
983
|
+
|
|
984
|
+
CHECK(std::abs(lenT - lenB) / lenB < 0.5 && std::abs(lenR - lenL) / lenL < 0.5 &&
|
|
985
|
+
lineT.points().size() >= 5 && lineR.points().size() >= 5);
|
|
986
|
+
|
|
987
|
+
// continue top row right until we cross the right line
|
|
988
|
+
CHECK(tlTracer.traceGaps(tlTracer.right(), lineT, maxStepSize, lineR));
|
|
989
|
+
|
|
990
|
+
printf("L: %.1f, %.1f ^ %.1f, %.1f > %.1f, %.1f (%d : %d : %d : %d)\n", bl.x, bl.y,
|
|
991
|
+
tl.x - bl.x, tl.y - bl.y, br.x - bl.x, br.y - bl.y, (int)lenL, (int)lenB, (int)lenT, (int)lenR);
|
|
992
|
+
|
|
993
|
+
for (auto* l : {&lineL, &lineB, &lineT, &lineR})
|
|
994
|
+
l->evaluate(1.0);
|
|
995
|
+
|
|
996
|
+
// find the bounding box corners of the code with sub-pixel precision by intersecting the 4 border lines
|
|
997
|
+
bl = intersect(lineB, lineL);
|
|
998
|
+
tl = intersect(lineT, lineL);
|
|
999
|
+
tr = intersect(lineT, lineR);
|
|
1000
|
+
br = intersect(lineB, lineR);
|
|
1001
|
+
|
|
1002
|
+
int dimT, dimR;
|
|
1003
|
+
double fracT, fracR;
|
|
1004
|
+
auto splitDouble = [](double d, int* i, double* f) {
|
|
1005
|
+
*i = std::isnormal(d) ? narrow_cast<int>(std::lround(d)) : 0;
|
|
1006
|
+
*f = std::isnormal(d) ? std::abs(d - *i) : INFINITY;
|
|
1007
|
+
};
|
|
1008
|
+
splitDouble(lineT.modules(tl, tr), &dimT, &fracT);
|
|
1009
|
+
splitDouble(lineR.modules(br, tr), &dimR, &fracR);
|
|
1010
|
+
|
|
1011
|
+
// the dimension is 2x the number of black/white transitions
|
|
1012
|
+
dimT *= 2;
|
|
1013
|
+
dimR *= 2;
|
|
1014
|
+
|
|
1015
|
+
printf("L: %.1f, %.1f ^ %.1f, %.1f > %.1f, %.1f ^> %.1f, %.1f\n", bl.x, bl.y,
|
|
1016
|
+
tl.x - bl.x, tl.y - bl.y, br.x - bl.x, br.y - bl.y, tr.x, tr.y);
|
|
1017
|
+
printf("dim: %d x %d (%.2f, %.2f)\n", dimT, dimR, fracT, fracR);
|
|
1018
|
+
|
|
1019
|
+
auto* version = VersionForDimensions(dimR, dimT);
|
|
1020
|
+
|
|
1021
|
+
// if we have an invalid dimension but it is almost square (all valid rectangular symbols differ in their dimension by at least 10),
|
|
1022
|
+
// we try to parse it by assuming a square. If only one leads to a valid version, we use that, otherwise we use the dimension
|
|
1023
|
+
// that is closer to an integral value.
|
|
1024
|
+
if (!version && std::abs(dimT - dimR) < 10) {
|
|
1025
|
+
auto* versionT = VersionForDimensions(dimT, dimT);
|
|
1026
|
+
auto* versionR = VersionForDimensions(dimR, dimR);
|
|
1027
|
+
if (bool(versionT) ^ bool(versionR))
|
|
1028
|
+
dimT = dimR = versionT ? dimT : dimR;
|
|
1029
|
+
else
|
|
1030
|
+
dimT = dimR = fracR < fracT ? dimR : dimT;
|
|
1031
|
+
version = VersionForDimensions(dimR, dimT);
|
|
1032
|
+
}
|
|
1033
|
+
CHECK(version);
|
|
1034
|
+
|
|
1035
|
+
auto movedTowardsBy = [](PointF a, PointF b1, PointF b2, auto d) {
|
|
1036
|
+
return a + d * normalized(normalized(b1 - a) + normalized(b2 - a));
|
|
1037
|
+
};
|
|
1038
|
+
|
|
1039
|
+
// shrink shape by half a pixel to go from center of white pixel outside of code to the edge between white and black
|
|
1040
|
+
QuadrilateralF sourcePoints = {
|
|
1041
|
+
movedTowardsBy(tl, tr, bl, 0.5f),
|
|
1042
|
+
// move the tr point a little less because the jagged top and right line tend to be statistically slightly
|
|
1043
|
+
// inclined toward the center anyway.
|
|
1044
|
+
movedTowardsBy(tr, br, tl, 0.3f),
|
|
1045
|
+
movedTowardsBy(br, bl, tr, 0.5f),
|
|
1046
|
+
movedTowardsBy(bl, tl, br, 0.5f),
|
|
1047
|
+
};
|
|
1048
|
+
|
|
1049
|
+
auto mod2Pix = PerspectiveTransform(Rectangle(dimT, dimR, 0), sourcePoints);
|
|
1050
|
+
auto pix2Mod = PerspectiveTransform(sourcePoints, Rectangle(dimT, dimR, 0));
|
|
1051
|
+
|
|
1052
|
+
#if 1
|
|
1053
|
+
std::vector<int> apX(version->dataBlocksX() + 1), apY(version->dataBlocksY() + 1);
|
|
1054
|
+
std::generate(apX.begin(), apX.end(), [i = 0, version]() mutable { return i++ * (version->dataBlockWidth + 2); });
|
|
1055
|
+
std::generate(apY.begin(), apY.end(), [i = 0, version]() mutable { return i++ * (version->dataBlockHeight + 2); });
|
|
1056
|
+
apX.back() = dimT - 1;
|
|
1057
|
+
apY.back() = dimR - 1;
|
|
1058
|
+
auto apP = Matrix<std::optional<PointF>>(Size(apX), Size(apY));
|
|
1059
|
+
|
|
1060
|
+
constexpr PointI l(-1, 0), r(1, 0), u(0, -1), d(0, 1);
|
|
1061
|
+
|
|
1062
|
+
// Find the position of the alignment pattern center by looking for the given b/w patterns around the initial estimate
|
|
1063
|
+
// based on the mod2Pix projection.
|
|
1064
|
+
auto findAP = [&](PointI p, PointI offset, int radius, PointI timingStart, LocalGrid::Directions timingDirs, PointI blackStart,
|
|
1065
|
+
LocalGrid::Directions blackDirs, PointI whiteStart,
|
|
1066
|
+
LocalGrid::Directions whiteDirs) -> std::optional<PointF> {
|
|
1067
|
+
auto lg = LocalGrid(*startTracer.img, mod2Pix, p, {dimT, dimR}, offset);
|
|
1068
|
+
if (lg.findPattern(radius, timingStart, timingDirs, blackStart, blackDirs, whiteStart, whiteDirs))
|
|
1069
|
+
return lg.getPos();
|
|
1070
|
+
return {};
|
|
1071
|
+
};
|
|
1072
|
+
|
|
1073
|
+
for (int y = 0; y < Size(apY); ++y)
|
|
1074
|
+
for (int x = 0; x < Size(apX); ++x) {
|
|
1075
|
+
std::optional<PointF> ap;
|
|
1076
|
+
PointI api{apX[x], apY[y]};
|
|
1077
|
+
int Nx = Size(apX) - 1, Ny = Size(apY) - 1;
|
|
1078
|
+
|
|
1079
|
+
if (x == 0 && y == 0) // top-left
|
|
1080
|
+
ap = findAP(api, {2, 0}, 4, {1, 0}, std::array{r}, {0, 0}, std::array{d}, {-1, -1}, std::array{d, r});
|
|
1081
|
+
else if (x == Nx && y == 0) // top-right
|
|
1082
|
+
ap = findAP(api, {-1, 1}, 4, {0, 0}, std::array{l, d}, {}, {}, {1, -1}, std::array{d, l});
|
|
1083
|
+
else if (x == Nx && y == Ny) // bottom-right
|
|
1084
|
+
ap = findAP(api, {-0, -2}, 4, {0, -1}, std::array{u}, {0, 0}, std::array{l}, {1, 1}, std::array{u, l});
|
|
1085
|
+
else if (x == 0 && y == Ny) // bottom-left
|
|
1086
|
+
ap = findAP(api, {1, -1}, 4, {}, {}, {0, 0}, std::array{u, r}, {-1, 1}, std::array{u, r});
|
|
1087
|
+
else if (x == 0) // left
|
|
1088
|
+
ap = findAP(api, {2, 0}, 3, {1, 0}, std::array{r}, {0, -1}, std::array{u, d, r}, {}, {});
|
|
1089
|
+
else if (x == Nx) // right
|
|
1090
|
+
ap = findAP(api, {-1, 0}, 3, {0, 0}, std::array{l, u, d}, {0, -1}, std::array{l}, {}, {});
|
|
1091
|
+
else if (y == 0) // top
|
|
1092
|
+
ap = findAP(api, {-1, 0}, 3, {-1, 0}, std::array{l, r, d}, {0, 0}, std::array{d}, {}, {});
|
|
1093
|
+
else if (y == Ny) // bottom
|
|
1094
|
+
ap = findAP(api, {-1, -1}, 3, {-1, -1}, std::array{u}, {0, 0}, std::array{l, r, u}, {}, {});
|
|
1095
|
+
else // center
|
|
1096
|
+
ap = findAP(api, {-1, 0}, 3, {-1, 0}, std::array{l, r, u, d}, {0, -1}, std::array{l, r, u, d}, {}, {});
|
|
1097
|
+
|
|
1098
|
+
if (ap)
|
|
1099
|
+
apP.set(x, y, *ap);
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
if (auto res = SampleGrid(*startTracer.img, dimT, dimR, mod2Pix, std::move(apP), apX, apY); res.isValid())
|
|
1103
|
+
co_yield std::move(res);
|
|
1104
|
+
#else
|
|
1105
|
+
if (auto res = SampleGrid(*startTracer.img, dimT, dimR, mod2Pix); res.isValid())
|
|
1106
|
+
co_yield std::move(res);
|
|
1107
|
+
#endif
|
|
1108
|
+
// if (auto res = SampleGrid(*startTracer.img, dimT, dimR, mod2Pix); res.isValid())
|
|
1109
|
+
// co_yield std::move(res);
|
|
1110
|
+
|
|
1111
|
+
if (dimT > 42 && dimR > 42) // symbols that are this large are unlikely to be fixable by a 'global' timing pattern correction
|
|
1112
|
+
continue;
|
|
1113
|
+
|
|
1114
|
+
// TODO: we currently only yield the corrected grid if at least one dimention is significantly deformed (has a LUT), but it
|
|
1115
|
+
// would be even better if we stopped right after having a sucessful decoding result from the uncorrected grid. The current
|
|
1116
|
+
// co-routine setup does not allow this. The runtime impact is very small though.
|
|
1117
|
+
|
|
1118
|
+
// produce timing-pattern-corrected alternatives for deformed symbols
|
|
1119
|
+
tl = sourcePoints[0], tr = sourcePoints[1], br = sourcePoints[2];
|
|
1120
|
+
|
|
1121
|
+
auto tLUT = BuildModuleCenterLUT(lineT.points(), tl, tr, dimT, [&](double t) { return pix2Mod((1 - t) * tl + t * tr).x; });
|
|
1122
|
+
auto rLUT = BuildModuleCenterLUT(lineR.points(), tr, br, dimR, [&](double t) { return pix2Mod((1 - t) * tr + t * br).y; });
|
|
1123
|
+
|
|
1124
|
+
if (!tLUT.empty() || !rLUT.empty())
|
|
1125
|
+
if (auto res = SampleGridCorrected(*startTracer.img, dimT, dimR, mod2Pix, tLUT, rLUT); res.isValid())
|
|
1126
|
+
co_yield std::move(res);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
static DetectorResults DetectNew(const BitMatrix& image, bool tryHarder, bool tryRotate)
|
|
1131
|
+
{
|
|
1132
|
+
#ifdef PRINT_DEBUG
|
|
1133
|
+
LogMatrixWriter lmw(log, image, 3, "dm-log.pnm");
|
|
1134
|
+
// tryRotate = tryHarder = false;
|
|
1135
|
+
#endif
|
|
1136
|
+
|
|
1137
|
+
// a history log to remember where the tracing already passed by to prevent a later trace from doing the same work twice
|
|
1138
|
+
EdgeTracer::StateMatrix history;
|
|
1139
|
+
if (tryHarder)
|
|
1140
|
+
history = EdgeTracer::StateMatrix(image.width(), image.height());
|
|
1141
|
+
|
|
1142
|
+
// instantiate RegressionLine objects outside of Scan function to prevent repetitive std::vector allocations
|
|
1143
|
+
std::array<DMRegressionLine, 4> lines;
|
|
1144
|
+
|
|
1145
|
+
constexpr int minSymbolSize = 8 * 2; // minimum realistic size in pixel: 8 modules x 2 pixels per module
|
|
1146
|
+
|
|
1147
|
+
for (auto dir : {PointF{-1, 0}, {1, 0}, {0, -1}, {0, 1}}) {
|
|
1148
|
+
auto center = PointI(image.width() / 2, image.height() / 2);
|
|
1149
|
+
auto startPos = centered(center - center * dir + minSymbolSize / 2 * dir);
|
|
1150
|
+
|
|
1151
|
+
history.clear();
|
|
1152
|
+
|
|
1153
|
+
for (int i = 1;; ++i) {
|
|
1154
|
+
EdgeTracer tracer(image, startPos, dir);
|
|
1155
|
+
tracer.p += i / 2 * minSymbolSize * (i & 1 ? -1 : 1) * tracer.right();
|
|
1156
|
+
if (tryHarder)
|
|
1157
|
+
tracer.history = &history;
|
|
1158
|
+
|
|
1159
|
+
if (!tracer.isIn())
|
|
1160
|
+
break;
|
|
1161
|
+
|
|
1162
|
+
for (auto&& res : Scan(tracer, lines))
|
|
1163
|
+
co_yield std::move(res);
|
|
1164
|
+
|
|
1165
|
+
if (!tryHarder)
|
|
1166
|
+
break; // only test center lines
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
if (!tryRotate)
|
|
1170
|
+
break; // only test left direction
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
/**
|
|
1175
|
+
* This method detects a code in a "pure" image -- that is, pure monochrome image
|
|
1176
|
+
* which contains only an unrotated, unskewed, image of a code, with some optional white border
|
|
1177
|
+
* around it. This is a specialized method that works exceptionally fast in this special
|
|
1178
|
+
* case.
|
|
1179
|
+
*/
|
|
1180
|
+
static DetectorResult DetectPure(const BitMatrix& image)
|
|
1181
|
+
{
|
|
1182
|
+
int left, top, width, height;
|
|
1183
|
+
if (!image.findBoundingBox(left, top, width, height, 8))
|
|
1184
|
+
return {};
|
|
1185
|
+
|
|
1186
|
+
BitMatrixCursorI cur(image, {left, top}, {0, 1});
|
|
1187
|
+
if (cur.countEdges(height - 1) != 0)
|
|
1188
|
+
return {};
|
|
1189
|
+
cur.turnLeft();
|
|
1190
|
+
if (cur.countEdges(width - 1) != 0)
|
|
1191
|
+
return {};
|
|
1192
|
+
cur.turnLeft();
|
|
1193
|
+
int dimR = cur.countEdges(height - 1) + 1;
|
|
1194
|
+
cur.turnLeft();
|
|
1195
|
+
int dimT = cur.countEdges(width - 1) + 1;
|
|
1196
|
+
|
|
1197
|
+
auto modSizeX = float(width) / dimT;
|
|
1198
|
+
auto modSizeY = float(height) / dimR;
|
|
1199
|
+
auto modSize = (modSizeX + modSizeY) / 2;
|
|
1200
|
+
|
|
1201
|
+
if (dimT % 2 != 0 || dimR % 2 != 0 || dimT < 10 || dimT > 144 || dimR < 8 || dimR > 144
|
|
1202
|
+
|| std::abs(modSizeX - modSizeY) > 1
|
|
1203
|
+
|| !image.isIn(PointF{left + modSizeX / 2 + (dimT - 1) * modSize, top + modSizeY / 2 + (dimR - 1) * modSize}))
|
|
1204
|
+
return {};
|
|
1205
|
+
|
|
1206
|
+
// Now just read off the bits (this is a crop + subsample)
|
|
1207
|
+
return {Deflate(image, dimT, dimR, top + modSizeY / 2, left + modSizeX / 2, modSize),
|
|
1208
|
+
Rectangle<PointI>(left, top, width, height)};
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
DetectorResults Detect(const BitMatrix& image, bool tryHarder, bool tryRotate, bool isPure)
|
|
1212
|
+
{
|
|
1213
|
+
// First try the very fast DetectPure() path. Also because DetectNew() generally fails with pure module size 1 symbols
|
|
1214
|
+
// TODO: implement a tryRotate version of DetectPure, see #590.
|
|
1215
|
+
if (auto r = DetectPure(image); r.isValid())
|
|
1216
|
+
co_yield std::move(r);
|
|
1217
|
+
else if (!isPure) { // If r.isValid() then there is no point in looking for more (no-pure) symbols
|
|
1218
|
+
bool found = false;
|
|
1219
|
+
for (auto&& r : DetectNew(image, tryHarder, tryRotate)) {
|
|
1220
|
+
found = true;
|
|
1221
|
+
co_yield std::move(r);
|
|
1222
|
+
}
|
|
1223
|
+
if (!found && tryHarder) {
|
|
1224
|
+
if (auto r = DetectOld(image); r.isValid())
|
|
1225
|
+
co_yield std::move(r);
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
} // namespace ZXing::DataMatrix
|