fix: correct error related to GenericMultipleBarcodeReader

This error occured in some cases during decode for an image with  many barcodes spread througout.
This commit is contained in:
Henry Schimke
2024-02-01 18:18:44 -06:00
parent 3c6c0426df
commit d73d92bd2a
7 changed files with 91 additions and 61 deletions

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "rxing" name = "rxing"
version = "0.5.5" version = "0.5.6"
description="A rust port of the zxing barcode library." description="A rust port of the zxing barcode library."
license="Apache-2.0" license="Apache-2.0"
repository="https://github.com/rxing-core/rxing" repository="https://github.com/rxing-core/rxing"

View File

@@ -113,7 +113,13 @@ impl<B: Binarizer> BinaryBitmap<B> {
// 1D Reader finds a barcode before the 2D Readers run. // 1D Reader finds a barcode before the 2D Readers run.
// 2. This work will only be done once even if the caller installs multiple 2D Readers. // 2. This work will only be done once even if the caller installs multiple 2D Readers.
if self.matrix.is_none() { if self.matrix.is_none() {
self.matrix = Some(self.binarizer.get_black_matrix().unwrap().clone()) self.matrix = Some(match self.binarizer.get_black_matrix() {
Ok(a) => a.clone(),
Err(_) => {
BitMatrix::new(self.get_width() as u32, self.get_height() as u32).unwrap()
}
})
// self.binarizer.get_black_matrix().unwrap_or_else( |_| BitMatrix::new(self.get_width() as u32, self.get_height() as u32).unwrap()).clone())
} }
self.matrix.as_ref().unwrap() self.matrix.as_ref().unwrap()
} }

View File

@@ -227,14 +227,16 @@ impl<LS: LuminanceSource> GlobalHistogramBinarizer<LS> {
} }
// Find a valley between them that is low and closer to the white peak. // Find a valley between them that is low and closer to the white peak.
let mut bestValley = secondPeak - 1; let mut bestValley = secondPeak as isize - 1;
let mut bestValleyScore = -1; let mut bestValleyScore = -1;
let mut x = secondPeak; let mut x = secondPeak as isize;
while x > firstPeak { while x > firstPeak as isize {
// for (int x = secondPeak - 1; x > firstPeak; x--) { // for (int x = secondPeak - 1; x > firstPeak; x--) {
let fromFirst = x - firstPeak; let fromFirst = x - firstPeak as isize;
let score = let score = fromFirst
fromFirst * fromFirst * (secondPeak - x) * (maxBucketCount - buckets[x]) as usize; * fromFirst
* (secondPeak as isize - x)
* (maxBucketCount - buckets[x as usize]) as isize;
if score as i32 > bestValleyScore { if score as i32 > bestValleyScore {
bestValley = x; bestValley = x;
bestValleyScore = score as i32; bestValleyScore = score as i32;

View File

@@ -57,14 +57,60 @@ impl<T: Reader> MultipleBarcodeReader for GenericMultipleBarcodeReader<T> {
let mut results = Vec::new(); let mut results = Vec::new();
self.do_decode_multiple(image, hints, &mut results, 0, 0, 0); self.do_decode_multiple(image, hints, &mut results, 0, 0, 0);
if results.is_empty() { let unique_results: Vec<RXingResult> = results
.iter()
.enumerate()
.filter(|(i, r)| {
let already_found = if r.getPoints().len() >= 4 {
let q1 = Quadrilateral::new(
r.getPoints()[0],
r.getPoints()[1],
r.getPoints()[2],
r.getPoints()[3],
);
results.iter().skip(*i + 1).any(|e| {
if e.getPoints().len() >= 4 {
let q2 = Quadrilateral::new(
e.getPoints()[0],
e.getPoints()[1],
e.getPoints()[2],
e.getPoints()[3],
);
Quadrilateral::have_intersecting_bounding_boxes(&q1, &q2)
} else {
e.getPoints().iter().any(|p| q1.is_inside(*p))
}
})
} else {
results.iter().skip(*i + 1).any(|e| {
if e.getPoints().len() >= 4 {
let q2 = Quadrilateral::new(
e.getPoints()[0],
e.getPoints()[1],
e.getPoints()[2],
e.getPoints()[3],
);
e.getPoints().iter().any(|p| q2.is_inside(*p))
} else {
e.getText() == r.getText()
&& e.getBarcodeFormat() == r.getBarcodeFormat()
}
})
};
!already_found
})
.map(|(_, r)| r)
.cloned()
.collect();
if unique_results.is_empty() {
return Err(Exceptions::NOT_FOUND); return Err(Exceptions::NOT_FOUND);
} }
Ok(results) Ok(unique_results)
} }
} }
impl<T: Reader> GenericMultipleBarcodeReader<T> { impl<T: Reader> GenericMultipleBarcodeReader<T> {
const MIN_DIMENSION_TO_RECUR: f32 = 10.0; const MIN_DIMENSION_TO_RECUR: f32 = 100.0;
const MAX_DEPTH: u32 = 4; const MAX_DEPTH: u32 = 4;
pub fn new(delegate: T) -> Self { pub fn new(delegate: T) -> Self {
@@ -89,52 +135,11 @@ impl<T: Reader> GenericMultipleBarcodeReader<T> {
return; return;
}; };
// let alreadyFound = results.iter().any(|r| r.getText() == result.getText() && r.getBarcodeFormat() == result.getBarcodeFormat());
let resultPoints = result.getPoints().clone(); let resultPoints = result.getPoints().clone();
let possible_new_result = Self::translatePoints(result, xOffset, yOffset); let possible_new_result = Self::translatePoints(result, xOffset, yOffset);
let already_found = if possible_new_result.getPoints().len() >= 4 { results.push(possible_new_result);
let q1 = Quadrilateral::new(
possible_new_result.getPoints()[0],
possible_new_result.getPoints()[1],
possible_new_result.getPoints()[2],
possible_new_result.getPoints()[3],
);
results.iter().any(|e| {
if e.getPoints().len() >= 4 {
let q2 = Quadrilateral::new(
e.getPoints()[0],
e.getPoints()[1],
e.getPoints()[2],
e.getPoints()[3],
);
Quadrilateral::have_intersecting_bounding_boxes(&q1, &q2)
} else {
e.getPoints().iter().any(|p| q1.is_inside(*p))
}
})
} else {
results.iter().any(|e| {
if e.getPoints().len() >= 4 {
let q2 = Quadrilateral::new(
e.getPoints()[0],
e.getPoints()[1],
e.getPoints()[2],
e.getPoints()[3],
);
e.getPoints().iter().any(|p| q2.is_inside(*p))
} else {
e.getText() == possible_new_result.getText()
&& e.getBarcodeFormat() == possible_new_result.getBarcodeFormat()
}
})
};
if !already_found {
results.push(possible_new_result);
}
if resultPoints.is_empty() { if resultPoints.is_empty() {
return; return;
@@ -213,13 +218,6 @@ impl<T: Reader> GenericMultipleBarcodeReader<T> {
.map(|oldPoint| point_f(oldPoint.x + xOffset as f32, oldPoint.y + yOffset as f32)) .map(|oldPoint| point_f(oldPoint.x + xOffset as f32, oldPoint.y + yOffset as f32))
.collect(); .collect();
// let mut newPoints = Vec::with_capacity(oldPoints.len());
// for oldPoint in oldPoints {
// newPoints.push(point(
// oldPoint.getX() + xOffset as f32,
// oldPoint.getY() + yOffset as f32,
// ));
// }
let mut newRXingResult = RXingResult::new_complex( let mut newRXingResult = RXingResult::new_complex(
result.getText(), result.getText(),
result.getRawBytes().clone(), result.getRawBytes().clone(),

View File

@@ -30,7 +30,7 @@ use serde::{Deserialize, Serialize};
* @author Sean Owen * @author Sean Owen
*/ */
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone)] #[derive(Clone, Debug)]
pub struct RXingResult { pub struct RXingResult {
text: String, text: String,
rawBytes: Vec<u8>, rawBytes: Vec<u8>,

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 KiB

View File

@@ -23,3 +23,27 @@ fn issue_28() {
); );
rxing::helpers::detect_multiple_in_file_with_hints("test_resources/blackbox/github_issue_cases/226611447-be6041dc-5b21-42fe-827b-068ccc59082c.png", &mut hints).unwrap_or_default(); rxing::helpers::detect_multiple_in_file_with_hints("test_resources/blackbox/github_issue_cases/226611447-be6041dc-5b21-42fe-827b-068ccc59082c.png", &mut hints).unwrap_or_default();
} }
#[cfg(feature = "image")]
#[test]
fn dynamsoft_all_supported_formats_image_fault() {
use rxing::DecodingHintDictionary;
let mut hints: DecodingHintDictionary = DecodingHintDictionary::new();
hints.insert(
rxing::DecodeHintType::TRY_HARDER,
rxing::DecodeHintValue::TryHarder(true),
);
let results = rxing::helpers::detect_multiple_in_file_with_hints(
"test_resources/blackbox/multi-1/AllSupportedBarcodeTypes.png",
&mut hints,
)
.expect("must not fault during read");
assert!(
results.len() >= 11,
"regression detection, base count of 11 codes"
);
// ToDo: This test is incomplete. Some that should be detected aren't, and some that are detected shouldn't be.
}