From 1ef262e967357956dd6b0eb101435668920a8e37 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Mon, 3 Apr 2023 12:18:35 -0500 Subject: [PATCH] initial port complete, untested, builds --- src/barcode_format.rs | 6 + src/multi_format_reader.rs | 15 +- src/qrcode/cpp_port/detector.rs | 6 +- src/qrcode/cpp_port/qr_cpp_reader.rs | 312 +++++++++++++++++---------- src/qrcode/mod.rs | 2 +- 5 files changed, 219 insertions(+), 122 deletions(-) diff --git a/src/barcode_format.rs b/src/barcode_format.rs index 0f1f45f..43b0464 100644 --- a/src/barcode_format.rs +++ b/src/barcode_format.rs @@ -65,6 +65,8 @@ pub enum BarcodeFormat { /** QR Code 2D barcode format. */ QR_CODE, + MICRO_QR_CODE, + /** RSS 14 */ RSS_14, @@ -102,6 +104,7 @@ impl Display for BarcodeFormat { BarcodeFormat::MAXICODE => "maxicode", BarcodeFormat::PDF_417 => "pdf 417", BarcodeFormat::QR_CODE => "qrcode", + BarcodeFormat::MICRO_QR_CODE => "mqr", BarcodeFormat::RSS_14 => "rss 14", BarcodeFormat::RSS_EXPANDED => "rss expanded", BarcodeFormat::UPC_A => "upc a", @@ -140,6 +143,9 @@ impl From<&str> for BarcodeFormat { "maxicode" | "maxi_code" => BarcodeFormat::MAXICODE, "pdf 417" | "pdf_417" | "pdf417" | "iso 15438" | "iso_15438" => BarcodeFormat::PDF_417, "qrcode" | "qr_code" | "qr code" => BarcodeFormat::QR_CODE, + "mqr" | "microqr" | "micro_qr" | "micro_qrcode" | "micro_qr_code" | "mqr_code" => { + BarcodeFormat::MICRO_QR_CODE + } "rss 14" | "rss_14" | "rss14" | "gs1 databar" | "gs1 databar coupon" | "gs1_databar_coupon" => BarcodeFormat::RSS_14, "rss expanded" | "expanded rss" | "rss_expanded" => BarcodeFormat::RSS_EXPANDED, diff --git a/src/multi_format_reader.rs b/src/multi_format_reader.rs index 4d6fb5c..b6cb286 100644 --- a/src/multi_format_reader.rs +++ b/src/multi_format_reader.rs @@ -17,6 +17,7 @@ use std::collections::{HashMap, HashSet}; use crate::common::Result; +use crate::qrcode::cpp_port::QrReader; use crate::{ aztec::AztecReader, datamatrix::DataMatrixReader, maxicode::MaxiCodeReader, oned::MultiFormatOneDReader, pdf417::PDF417Reader, qrcode::QRCodeReader, BarcodeFormat, @@ -164,7 +165,16 @@ impl MultiFormatReader { for possible_format in self.possible_formats.iter() { let res = match possible_format { BarcodeFormat::QR_CODE => { - QRCodeReader::default().decode_with_hints(image, &self.hints) + let default_qr = + QRCodeReader::default().decode_with_hints(image, &self.hints); + if default_qr.is_ok() { + default_qr + } else { + QrReader::default().decode_with_hints(image, &self.hints) + } + } + BarcodeFormat::MICRO_QR_CODE => { + QrReader::default().decode_with_hints(image, &self.hints) } BarcodeFormat::DATA_MATRIX => { DataMatrixReader::default().decode_with_hints(image, &self.hints) @@ -199,6 +209,9 @@ impl MultiFormatReader { if let Ok(res) = QRCodeReader::default().decode_with_hints(image, &self.hints) { return Ok(res); } + if let Ok(res) = QrReader::default().decode_with_hints(image, &self.hints) { + return Ok(res); + } if let Ok(res) = DataMatrixReader::default().decode_with_hints(image, &self.hints) { return Ok(res); } diff --git a/src/qrcode/cpp_port/detector.rs b/src/qrcode/cpp_port/detector.rs index ac34c16..2afd886 100644 --- a/src/qrcode/cpp_port/detector.rs +++ b/src/qrcode/cpp_port/detector.rs @@ -27,9 +27,9 @@ use crate::{ #[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] pub struct FinderPatternSet { - bl: ConcentricPattern, - tl: ConcentricPattern, - tr: ConcentricPattern, + pub bl: ConcentricPattern, + pub tl: ConcentricPattern, + pub tr: ConcentricPattern, } pub type FinderPatterns = Vec; diff --git a/src/qrcode/cpp_port/qr_cpp_reader.rs b/src/qrcode/cpp_port/qr_cpp_reader.rs index f912693..1993fb9 100644 --- a/src/qrcode/cpp_port/qr_cpp_reader.rs +++ b/src/qrcode/cpp_port/qr_cpp_reader.rs @@ -5,22 +5,6 @@ */ // SPDX-License-Identifier: Apache-2.0 -// #include "QRReader.h" - -// #include "BinaryBitmap.h" -// #include "ConcentricFinder.h" -// #include "DecodeHints.h" -// #include "DecoderResult.h" -// #include "DetectorResult.h" -// #include "LogMatrix.h" -// #include "QRDecoder.h" -// #include "QRDetector.h" -// #include "Result.h" - -// #include - -// namespace ZXing::QRCode { - // Result Reader::decode(const BinaryBitmap& image) const // { // #if 1 @@ -133,13 +117,31 @@ // } // namespace ZXing::QRCode +use crate::{ + common::{cpp_essentials::ConcentricPattern, DetectorRXingResult, HybridBinarizer}, + multi::MultipleBarcodeReader, + BarcodeFormat, BinaryBitmap, DecodeHintType, DecodeHintValue, DecodingHintDictionary, + Exceptions, RXingResult, Reader, +}; -use crate::{Reader, DecodingHintDictionary, multi::MultipleBarcodeReader}; +use super::{ + decoder::Decode, + detector::{ + DetectPureMQR, DetectPureQR, FindFinderPatterns, GenerateFinderPatternSets, SampleMQR, + SampleQR, + }, +}; -pub struct QrReader{} +use crate::qrcode::detector::QRCodeDetectorResult as DetectorResult; + +#[derive(Default)] +pub struct QrReader; impl Reader for QrReader { - fn decode(&mut self, image: &mut crate::BinaryBitmap) -> crate::common::Result { + fn decode( + &mut self, + image: &mut crate::BinaryBitmap, + ) -> crate::common::Result { self.decode_with_hints(image, &DecodingHintDictionary::new()) } @@ -147,47 +149,67 @@ impl Reader for QrReader { &mut self, image: &mut crate::BinaryBitmap, hints: &crate::DecodingHintDictionary, - ) -> crate::common::Result { - todo!() + ) -> crate::common::Result { // #if 1 -// if (!_hints.isPure()) -// return FirstOrDefault(decode(image, 1)); -// #endif + if !matches!( + hints.get(&DecodeHintType::PURE_BARCODE), + Some(DecodeHintValue::PureBarcode(true)) + ) + // if !matches!(Some(hints.get(&DecodeHintType::PURE_BARCODE))) + // if (!_hints.isPure()) + { + return Ok(self + .decode_set_number_with_hints(image, hints, 1)? + .first() + .ok_or(Exceptions::NOT_FOUND)? + .clone()); + // return FirstOrDefault(decode(image, 1)); + } + // #endif -// auto binImg = image.getBitMatrix(); -// if (binImg == nullptr) -// return {}; + let binImg = image.get_black_matrix(); //image.getBitMatrix(); + // if (binImg == nullptr) + // {return {};} -// DetectorResult detectorResult; -// if (_hints.hasFormat(BarcodeFormat::QRCode)) -// detectorResult = DetectPureQR(*binImg); -// if (_hints.hasFormat(BarcodeFormat::MicroQRCode) && !detectorResult.isValid()) -// detectorResult = DetectPureMQR(*binImg); + let mut detectorResult = Err(Exceptions::NOT_FOUND); + if let Some(DecodeHintValue::PossibleFormats(formats)) = + hints.get(&DecodeHintType::POSSIBLE_FORMATS) + { + if formats.contains(&BarcodeFormat::QR_CODE) { + detectorResult = DetectPureQR(binImg); + } + if formats.contains(&BarcodeFormat::MICRO_QR_CODE) && detectorResult.is_err() { + detectorResult = DetectPureMQR(binImg); + } + } -// if (!detectorResult.isValid()) -// return {}; + let detectorResult = detectorResult?; -// auto decoderResult = Decode(detectorResult.bits()); -// auto position = detectorResult.position(); + // let detectorResult: DetectorResult; + // if (_hints.hasFormat(BarcodeFormat::QR_CODE)) + // {detectorResult = DetectPureQR(binImg);} -// return Result(std::move(decoderResult), std::move(position), -// detectorResult.bits().width() < 21 ? BarcodeFormat::MicroQRCode : BarcodeFormat::QRCode); -// } + // if (_hints.hasFormat(BarcodeFormat::MICRO_QR_CODE) && !detectorResult.isValid()) + // {detectorResult = DetectPureMQR(binImg);} -// void logFPSet(const FinderPatternSet& fps [[maybe_unused]]) -// { -// #ifdef PRINT_DEBUG -// auto drawLine = [](PointF a, PointF b) { -// int steps = maxAbsComponent(b - a); -// PointF dir = bresenhamDirection(PointF(b - a)); -// for (int i = 0; i < steps; ++i) -// log(centered(a + i * dir), 2); -// }; + // if (!detectorResult.isValid()) + // {return {};} -// drawLine(fps.bl, fps.tl); -// drawLine(fps.tl, fps.tr); -// drawLine(fps.tr, fps.bl); -// #endif + let decoderResult = Decode(detectorResult.getBits())?; + let position = detectorResult.getPoints(); + + Ok(RXingResult::new( + &decoderResult.content().to_string(), + decoderResult.content().bytes().to_vec(), + position.to_vec(), + if detectorResult.getBits().width() < 21 { + BarcodeFormat::MICRO_QR_CODE + } else { + BarcodeFormat::QR_CODE + }, + )) + // return Result(std::move(decoderResult), std::move(position), + // detectorResult.bits().width() < 21 ? BarcodeFormat::MICRO_QR_CODE : BarcodeFormat::QR_CODE); } } @@ -204,69 +226,125 @@ impl MultipleBarcodeReader for QrReader { image: &mut crate::BinaryBitmap, hints: &DecodingHintDictionary, ) -> crate::common::Result> { - todo!() - // auto binImg = image.getBitMatrix(); -// if (binImg == nullptr) -// return {}; - -// #ifdef PRINT_DEBUG -// LogMatrixWriter lmw(log, *binImg, 5, "qr-log.pnm"); -// #endif - -// auto allFPs = FindFinderPatterns(*binImg, _hints.tryHarder()); - -// #ifdef PRINT_DEBUG -// printf("allFPs: %d\n", Size(allFPs)); -// #endif - -// std::vector usedFPs; -// Results results; - -// if (_hints.hasFormat(BarcodeFormat::QRCode)) { -// auto allFPSets = GenerateFinderPatternSets(allFPs); -// for (const auto& fpSet : allFPSets) { -// if (Contains(usedFPs, fpSet.bl) || Contains(usedFPs, fpSet.tl) || Contains(usedFPs, fpSet.tr)) -// continue; - -// logFPSet(fpSet); - -// auto detectorResult = SampleQR(*binImg, fpSet); -// if (detectorResult.isValid()) { -// auto decoderResult = Decode(detectorResult.bits()); -// auto position = detectorResult.position(); -// if (decoderResult.isValid()) { -// usedFPs.push_back(fpSet.bl); -// usedFPs.push_back(fpSet.tl); -// usedFPs.push_back(fpSet.tr); -// } -// if (decoderResult.isValid(_hints.returnErrors())) { -// results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::QRCode); -// if (maxSymbols && Size(results) == maxSymbols) -// break; -// } -// } -// } -// } - -// if (_hints.hasFormat(BarcodeFormat::MicroQRCode) && !(maxSymbols && Size(results) == maxSymbols)) { -// for (const auto& fp : allFPs) { -// if (Contains(usedFPs, fp)) -// continue; - -// auto detectorResult = SampleMQR(*binImg, fp); -// if (detectorResult.isValid()) { -// auto decoderResult = Decode(detectorResult.bits()); -// auto position = detectorResult.position(); -// if (decoderResult.isValid(_hints.returnErrors())) { -// results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::MicroQRCode); -// if (maxSymbols && Size(results) == maxSymbols) -// break; -// } - -// } -// } -// } - -// return results; + self.decode_set_number_with_hints(image, hints, u32::MAX) } -} \ No newline at end of file +} + +impl QrReader { + fn decode_set_number_with_hints( + &mut self, + image: &mut crate::BinaryBitmap, + hints: &DecodingHintDictionary, + count: u32, + ) -> crate::common::Result> { + let binImg = image.get_black_matrix(); //image.getBitMatrix(); + let maxSymbols = count; + // if (binImg == nullptr) + // {return {};} + + // #ifdef PRINT_DEBUG + // LogMatrixWriter lmw(log, *binImg, 5, "qr-log.pnm"); + // #endif + let try_harder = matches!( + hints.get(&DecodeHintType::TRY_HARDER), + Some(DecodeHintValue::TryHarder(true)) + ); + + let mut allFPs = FindFinderPatterns(binImg, try_harder); + + // #ifdef PRINT_DEBUG + // printf("allFPs: %d\n", Size(allFPs)); + // #endif + + let mut usedFPs: Vec = Vec::new(); + let mut results: Vec = Vec::new(); + + let (check_qr, check_mqr) = if let Some(DecodeHintValue::PossibleFormats(formats)) = + hints.get(&DecodeHintType::POSSIBLE_FORMATS) + { + ( + formats.contains(&BarcodeFormat::QR_CODE), + formats.contains(&BarcodeFormat::MICRO_QR_CODE), + ) + } else { + (true, true) + }; + + if check_qr { + // if (_hints.hasFormat(BarcodeFormat::QRCode)) { + let allFPSets = GenerateFinderPatternSets(&mut allFPs); + for fpSet in allFPSets { + // for (const auto& fpSet : allFPSets) { + if (usedFPs.contains(&fpSet.bl) + || usedFPs.contains(&fpSet.tl) + || usedFPs.contains(&fpSet.tr)) + { + continue; + } + + // logFPSet(fpSet); + + let detectorResult = SampleQR(binImg, &fpSet); + if let Ok(detectorResult) = detectorResult { + // if (detectorResult.is_ok()) { + let decoderResult = Decode(detectorResult.getBits()); + let position = detectorResult.getPoints(); + if let Ok(decoderResult) = decoderResult { + if (decoderResult.isValid()) { + usedFPs.push(fpSet.bl); + usedFPs.push(fpSet.tl); + usedFPs.push(fpSet.tr); + } + + if (decoderResult.isValid()) { + results.push(RXingResult::new( + &decoderResult.content().to_string(), + decoderResult.content().bytes().to_vec(), + position.to_vec(), + BarcodeFormat::QR_CODE, + )); + // results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::QR_CODE); + + if (maxSymbols != 0 && (results.len() as u32) == maxSymbols) { + break; + } + } + } + } + } + } + if (check_mqr && !(maxSymbols != 0 && (results.len() as u32) == maxSymbols)) { + // if (_hints.hasFormat(BarcodeFormat::MicroQRCode) && !(maxSymbols && Size(results) == maxSymbols)) { + for fp in allFPs { + // for (const auto& fp : allFPs) { + if (usedFPs.contains(&fp)) { + continue; + } + + let detectorResult = SampleMQR(binImg, fp); + if let Ok(detectorResult) = detectorResult { + // if (detectorResult.is_ok()) { + let decoderResult = Decode(detectorResult.getBits()); + let position = detectorResult.getPoints(); + if let Ok(decoderResult) = decoderResult { + if (decoderResult.isValid()) { + results.push(RXingResult::new( + &decoderResult.content().to_string(), + decoderResult.content().bytes().to_vec(), + position.to_vec(), + BarcodeFormat::MICRO_QR_CODE, + )); + // results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::MICRO_QR_CODE); + + if (maxSymbols != 0 && (results.len() as u32) == maxSymbols) { + break; + } + } + } + } + } + } + + return Ok(results); + } +} diff --git a/src/qrcode/mod.rs b/src/qrcode/mod.rs index 77ee545..ae308d6 100644 --- a/src/qrcode/mod.rs +++ b/src/qrcode/mod.rs @@ -8,7 +8,7 @@ pub use qr_code_reader::*; mod qr_code_writer; pub use qr_code_writer::*; -mod cpp_port; +pub mod cpp_port; #[cfg(test)] #[cfg(feature = "image")]