From ad9d19369582d0d10c0fe4a8047067b753ec8f1d Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Thu, 1 Dec 2022 16:11:49 -0600 Subject: [PATCH] port generic multiple barcode reader --- src/multi/GenericMultipleBarcodeReader.java | 182 ---------------- src/multi/generic_multiple_barcode_reader.rs | 213 +++++++++++++++++++ src/multi/mod.rs | 3 + src/multi/multiple_barcode_reader.rs | 4 +- src/multi/qrcode/qr_code_multi_reader.rs | 6 +- 5 files changed, 221 insertions(+), 187 deletions(-) delete mode 100644 src/multi/GenericMultipleBarcodeReader.java create mode 100644 src/multi/generic_multiple_barcode_reader.rs diff --git a/src/multi/GenericMultipleBarcodeReader.java b/src/multi/GenericMultipleBarcodeReader.java deleted file mode 100644 index 962f7a4..0000000 --- a/src/multi/GenericMultipleBarcodeReader.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2009 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.multi; - -import com.google.zxing.BinaryBitmap; -import com.google.zxing.DecodeHintType; -import com.google.zxing.NotFoundException; -import com.google.zxing.Reader; -import com.google.zxing.ReaderException; -import com.google.zxing.RXingResult; -import com.google.zxing.RXingResultPoint; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - *

Attempts to locate multiple barcodes in an image by repeatedly decoding portion of the image. - * After one barcode is found, the areas left, above, right and below the barcode's - * {@link RXingResultPoint}s are scanned, recursively.

- * - *

A caller may want to also employ {@link ByQuadrantReader} when attempting to find multiple - * 2D barcodes, like QR Codes, in an image, where the presence of multiple barcodes might prevent - * detecting any one of them.

- * - *

That is, instead of passing a {@link Reader} a caller might pass - * {@code new ByQuadrantReader(reader)}.

- * - * @author Sean Owen - */ -public final class GenericMultipleBarcodeReader implements MultipleBarcodeReader { - - private static final int MIN_DIMENSION_TO_RECUR = 100; - private static final int MAX_DEPTH = 4; - - static final RXingResult[] EMPTY_RESULT_ARRAY = new RXingResult[0]; - - private final Reader delegate; - - public GenericMultipleBarcodeReader(Reader delegate) { - this.delegate = delegate; - } - - @Override - public RXingResult[] decodeMultiple(BinaryBitmap image) throws NotFoundException { - return decodeMultiple(image, null); - } - - @Override - public RXingResult[] decodeMultiple(BinaryBitmap image, Map hints) - throws NotFoundException { - List results = new ArrayList<>(); - doDecodeMultiple(image, hints, results, 0, 0, 0); - if (results.isEmpty()) { - throw NotFoundException.getNotFoundInstance(); - } - return results.toArray(EMPTY_RESULT_ARRAY); - } - - private void doDecodeMultiple(BinaryBitmap image, - Map hints, - List results, - int xOffset, - int yOffset, - int currentDepth) { - if (currentDepth > MAX_DEPTH) { - return; - } - - RXingResult result; - try { - result = delegate.decode(image, hints); - } catch (ReaderException ignored) { - return; - } - boolean alreadyFound = false; - for (RXingResult existingRXingResult : results) { - if (existingRXingResult.getText().equals(result.getText())) { - alreadyFound = true; - break; - } - } - if (!alreadyFound) { - results.add(translateRXingResultPoints(result, xOffset, yOffset)); - } - RXingResultPoint[] resultPoints = result.getRXingResultPoints(); - if (resultPoints == null || resultPoints.length == 0) { - return; - } - int width = image.getWidth(); - int height = image.getHeight(); - float minX = width; - float minY = height; - float maxX = 0.0f; - float maxY = 0.0f; - for (RXingResultPoint point : resultPoints) { - if (point == null) { - continue; - } - float x = point.getX(); - float y = point.getY(); - if (x < minX) { - minX = x; - } - if (y < minY) { - minY = y; - } - if (x > maxX) { - maxX = x; - } - if (y > maxY) { - maxY = y; - } - } - - // Decode left of barcode - if (minX > MIN_DIMENSION_TO_RECUR) { - doDecodeMultiple(image.crop(0, 0, (int) minX, height), - hints, results, - xOffset, yOffset, - currentDepth + 1); - } - // Decode above barcode - if (minY > MIN_DIMENSION_TO_RECUR) { - doDecodeMultiple(image.crop(0, 0, width, (int) minY), - hints, results, - xOffset, yOffset, - currentDepth + 1); - } - // Decode right of barcode - if (maxX < width - MIN_DIMENSION_TO_RECUR) { - doDecodeMultiple(image.crop((int) maxX, 0, width - (int) maxX, height), - hints, results, - xOffset + (int) maxX, yOffset, - currentDepth + 1); - } - // Decode below barcode - if (maxY < height - MIN_DIMENSION_TO_RECUR) { - doDecodeMultiple(image.crop(0, (int) maxY, width, height - (int) maxY), - hints, results, - xOffset, yOffset + (int) maxY, - currentDepth + 1); - } - } - - private static RXingResult translateRXingResultPoints(RXingResult result, int xOffset, int yOffset) { - RXingResultPoint[] oldRXingResultPoints = result.getRXingResultPoints(); - if (oldRXingResultPoints == null) { - return result; - } - RXingResultPoint[] newRXingResultPoints = new RXingResultPoint[oldRXingResultPoints.length]; - for (int i = 0; i < oldRXingResultPoints.length; i++) { - RXingResultPoint oldPoint = oldRXingResultPoints[i]; - if (oldPoint != null) { - newRXingResultPoints[i] = new RXingResultPoint(oldPoint.getX() + xOffset, oldPoint.getY() + yOffset); - } - } - RXingResult newRXingResult = new RXingResult(result.getText(), - result.getRawBytes(), - result.getNumBits(), - newRXingResultPoints, - result.getBarcodeFormat(), - result.getTimestamp()); - newRXingResult.putAllMetadata(result.getRXingResultMetadata()); - return newRXingResult; - } - -} diff --git a/src/multi/generic_multiple_barcode_reader.rs b/src/multi/generic_multiple_barcode_reader.rs new file mode 100644 index 0000000..dabdae7 --- /dev/null +++ b/src/multi/generic_multiple_barcode_reader.rs @@ -0,0 +1,213 @@ +/* + * Copyright 2009 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use std::collections::HashMap; + +use crate::{ + BinaryBitmap, DecodingHintDictionary, Exceptions, RXingResult, RXingResultPoint, Reader, + ResultPoint, +}; + +use super::MultipleBarcodeReader; + +/** + *

Attempts to locate multiple barcodes in an image by repeatedly decoding portion of the image. + * After one barcode is found, the areas left, above, right and below the barcode's + * {@link RXingResultPoint}s are scanned, recursively.

+ * + *

A caller may want to also employ {@link ByQuadrantReader} when attempting to find multiple + * 2D barcodes, like QR Codes, in an image, where the presence of multiple barcodes might prevent + * detecting any one of them.

+ * + *

That is, instead of passing a {@link Reader} a caller might pass + * {@code new ByQuadrantReader(reader)}.

+ * + * @author Sean Owen + */ +pub struct GenericMultipleBarcodeReader(T); + +impl MultipleBarcodeReader for GenericMultipleBarcodeReader { + fn decodeMultiple( + &mut self, + image: &crate::BinaryBitmap, + ) -> Result, crate::Exceptions> { + self.decodeMultipleWithHints(image, &HashMap::new()) + } + + fn decodeMultipleWithHints( + &mut self, + image: &crate::BinaryBitmap, + hints: &crate::DecodingHintDictionary, + ) -> Result, crate::Exceptions> { + let mut results = Vec::new(); + self.doDecodeMultiple(image, hints, &mut results, 0, 0, 0); + if results.is_empty() { + return Err(Exceptions::NotFoundException("".to_owned())); + } + Ok(results) + } +} +impl GenericMultipleBarcodeReader { + const MIN_DIMENSION_TO_RECUR: f32 = 100.0; + const MAX_DEPTH: u32 = 4; + + pub fn new(delegate: T) -> Self { + Self(delegate) + } + + fn doDecodeMultiple( + &mut self, + image: &BinaryBitmap, + hints: &DecodingHintDictionary, + results: &mut Vec, + xOffset: u32, + yOffset: u32, + currentDepth: u32, + ) { + if currentDepth > Self::MAX_DEPTH { + return; + } + + let result; + //try { + result = self.0.decode_with_hints(image, hints); + //} catch (ReaderException ignored) { + // return; + //} + if let Err(Exceptions::ReaderException(_)) = result { + return; + } + + let result = result.expect("must exist"); + + let mut alreadyFound = false; + for existingRXingResult in results.iter() { + if existingRXingResult.getText() == (result.getText()) { + alreadyFound = true; + break; + } + } + + let resultPoints = result.getRXingResultPoints().clone(); + + if !alreadyFound { + results.push(Self::translateRXingResultPoints(result, xOffset, yOffset)); + } + + if resultPoints.is_empty() { + return; + } + let width = image.getWidth(); + let height = image.getHeight(); + let mut minX: f32 = width as f32; + let mut minY: f32 = height as f32; + let mut maxX: f32 = 0.0; + let mut maxY: f32 = 0.0; + for point in resultPoints { + // if (point == null) { + // continue; + // } + let x = point.getX(); + let y = point.getY(); + if x < minX { + minX = x; + } + if y < minY { + minY = y; + } + if x > maxX { + maxX = x; + } + if y > maxY { + maxY = y; + } + } + + // Decode left of barcode + if minX > Self::MIN_DIMENSION_TO_RECUR { + self.doDecodeMultiple( + &image.crop(0, 0, minX as usize, height), + hints, + results, + xOffset, + yOffset, + currentDepth + 1, + ); + } + // Decode above barcode + if minY > Self::MIN_DIMENSION_TO_RECUR { + self.doDecodeMultiple( + &image.crop(0, 0, width, minY as usize), + hints, + results, + xOffset, + yOffset, + currentDepth + 1, + ); + } + // Decode right of barcode + if maxX < (width as f32) - Self::MIN_DIMENSION_TO_RECUR { + self.doDecodeMultiple( + &image.crop(maxX as usize, 0, width - maxX as usize, height), + hints, + results, + xOffset + maxX as u32, + yOffset, + currentDepth + 1, + ); + } + // Decode below barcode + if maxY < (height as f32) - Self::MIN_DIMENSION_TO_RECUR { + self.doDecodeMultiple( + &image.crop(0, maxY as usize, width, height - maxY as usize), + hints, + results, + xOffset, + yOffset + maxY as u32, + currentDepth + 1, + ); + } + } + + fn translateRXingResultPoints(result: RXingResult, xOffset: u32, yOffset: u32) -> RXingResult { + let oldRXingResultPoints = result.getRXingResultPoints(); + if oldRXingResultPoints.is_empty() { + return result; + } + let mut newRXingResultPoints = Vec::with_capacity(oldRXingResultPoints.len()); //new RXingResultPoint[oldRXingResultPoints.length]; + for oldPoint in oldRXingResultPoints { + // for (int i = 0; i < oldRXingResultPoints.length; i++) { + // RXingResultPoint oldPoint = oldRXingResultPoints[i]; + // if (oldPoint != null) { + newRXingResultPoints.push(RXingResultPoint::new( + oldPoint.getX() + xOffset as f32, + oldPoint.getY() + yOffset as f32, + )); + // } + } + let mut newRXingResult = RXingResult::new_complex( + result.getText(), + result.getRawBytes().clone(), + result.getNumBits(), + newRXingResultPoints, + *result.getBarcodeFormat(), + result.getTimestamp(), + ); + newRXingResult.putAllMetadata(result.getRXingResultMetadata().clone()); + + newRXingResult + } +} diff --git a/src/multi/mod.rs b/src/multi/mod.rs index ad05da4..c79949c 100644 --- a/src/multi/mod.rs +++ b/src/multi/mod.rs @@ -4,3 +4,6 @@ pub use multiple_barcode_reader::*; mod by_quadrant_reader; pub use by_quadrant_reader::*; + +mod generic_multiple_barcode_reader; +pub use generic_multiple_barcode_reader::*; diff --git a/src/multi/multiple_barcode_reader.rs b/src/multi/multiple_barcode_reader.rs index 441e930..7ca546f 100644 --- a/src/multi/multiple_barcode_reader.rs +++ b/src/multi/multiple_barcode_reader.rs @@ -23,10 +23,10 @@ use crate::{BinaryBitmap, DecodingHintDictionary, Exceptions, RXingResult}; * @author Sean Owen */ pub trait MultipleBarcodeReader { - fn decodeMultiple(&self, image: &BinaryBitmap) -> Result, Exceptions>; + fn decodeMultiple(&mut self, image: &BinaryBitmap) -> Result, Exceptions>; fn decodeMultipleWithHints( - &self, + &mut self, image: &BinaryBitmap, hints: &DecodingHintDictionary, ) -> Result, Exceptions>; diff --git a/src/multi/qrcode/qr_code_multi_reader.rs b/src/multi/qrcode/qr_code_multi_reader.rs index 1684d6c..5a3d4ed 100644 --- a/src/multi/qrcode/qr_code_multi_reader.rs +++ b/src/multi/qrcode/qr_code_multi_reader.rs @@ -37,14 +37,14 @@ use super::detector::MultiDetector; pub struct QRCodeMultiReader(QRCodeReader); impl MultipleBarcodeReader for QRCodeMultiReader { fn decodeMultiple( - &self, + &mut self, image: &crate::BinaryBitmap, ) -> Result, crate::Exceptions> { self.decodeMultipleWithHints(image, &HashMap::new()) } fn decodeMultipleWithHints( - &self, + &mut self, image: &crate::BinaryBitmap, hints: &crate::DecodingHintDictionary, ) -> Result, crate::Exceptions> { @@ -251,7 +251,7 @@ mod multi_qr_code_test_case { let source = BufferedImageLuminanceSource::new(image); let bitmap = BinaryBitmap::new(Rc::new(HybridBinarizer::new(Box::new(source)))); - let reader = QRCodeMultiReader::new(); + let mut reader = QRCodeMultiReader::new(); let results = reader.decodeMultiple(&bitmap).expect("must decode"); // assertNotNull(results); assert_eq!(4, results.len());