fix: resolves an issue with the Luma8LuminanceSource which prevented some codes from being found.

This commit is contained in:
Henry Schimke
2024-02-02 10:47:29 -06:00
parent d73d92bd2a
commit 6afd68edb8
6 changed files with 390 additions and 36 deletions

View File

@@ -55,30 +55,8 @@ impl BufferedImageLuminanceSource {
width: usize,
height: usize,
) -> Self {
let img = image.to_rgba8();
let mut raster: ImageBuffer<_, Vec<_>> = ImageBuffer::new(image.width(), image.height());
for (x, y, new_pixel) in raster.enumerate_pixels_mut() {
let pixel = img.get_pixel(x, y);
let [red, green, blue, alpha] = pixel.0;
if alpha == 0 {
// white, so we know its luminance is 255
*new_pixel = Luma([0xFF])
} else {
// .299R + 0.587G + 0.114B (YUV/YIQ for PAL and NTSC),
// (306*R) >> 10 is approximately equal to R*0.299, and so on.
// 0x200 >> 10 is 0.5, it implements rounding.
*new_pixel = Luma([((306 * (red as u64)
+ 601 * (green as u64)
+ 117 * (blue as u64)
+ 0x200)
>> 10) as u8])
}
}
Self {
image: Rc::new(DynamicImage::from(raster)),
image: Rc::new(build_local_grey_image(image)),
width,
height,
left,
@@ -202,3 +180,87 @@ impl LuminanceSource for BufferedImageLuminanceSource {
})
}
}
fn build_local_grey_image(source: DynamicImage) -> DynamicImage {
let raster = match source {
DynamicImage::ImageLuma8(img) => img,
DynamicImage::ImageLumaA8(img) => {
let mut raster: ImageBuffer<_, Vec<_>> = ImageBuffer::new(img.width(), img.height());
for (x, y, new_pixel) in raster.enumerate_pixels_mut() {
let pixel = img.get_pixel(x, y);
let [luma, alpha] = pixel.0;
if alpha == 0 {
// white, so we know its luminance is 255
*new_pixel = Luma([0xFF])
} else {
*new_pixel = Luma([luma.saturating_mul(alpha)])
}
}
raster
}
// DynamicImage::ImageRgb8(_) => todo!(),
// DynamicImage::ImageRgba8(_) => todo!(),
DynamicImage::ImageLuma16(img) => {
let mut raster: ImageBuffer<_, Vec<_>> = ImageBuffer::new(img.width(), img.height());
for (x, y, new_pixel) in raster.enumerate_pixels_mut() {
let pixel = img.get_pixel(x, y);
let [luma] = pixel.0;
*new_pixel = Luma([(luma / u8::max_value() as u16) as u8])
}
raster
}
DynamicImage::ImageLumaA16(img) => {
let mut raster: ImageBuffer<_, Vec<_>> = ImageBuffer::new(img.width(), img.height());
for (x, y, new_pixel) in raster.enumerate_pixels_mut() {
let pixel = img.get_pixel(x, y);
let [luma, alpha] = pixel.0;
if alpha == 0 {
// white, so we know its luminance is 255
*new_pixel = Luma([0xFF])
} else {
*new_pixel =
Luma([((luma.saturating_mul(alpha)) / u8::max_value() as u16) as u8])
}
}
raster
}
// DynamicImage::ImageRgb16(_) => todo!(),
// DynamicImage::ImageRgba16(_) => todo!(),
// DynamicImage::ImageRgb32F(_) => todo!(),
// DynamicImage::ImageRgba32F(_) => todo!(),
_ => {
let img = source.to_rgba8();
let mut raster: ImageBuffer<_, Vec<_>> =
ImageBuffer::new(source.width(), source.height());
for (x, y, new_pixel) in raster.enumerate_pixels_mut() {
let pixel = img.get_pixel(x, y);
let [red, green, blue, alpha] = pixel.0;
if alpha == 0 {
// white, so we know its luminance is 255
*new_pixel = Luma([0xFF])
} else {
// .299R + 0.587G + 0.114B (YUV/YIQ for PAL and NTSC),
// (306*R) >> 10 is approximately equal to R*0.299, and so on.
// 0x200 >> 10 is 0.5, it implements rounding.
*new_pixel = Luma([((306 * (red as u64)
+ 601 * (green as u64)
+ 117 * (blue as u64)
+ 0x200)
>> 10) as u8])
}
}
raster
}
};
DynamicImage::from(raster)
}

View File

@@ -64,7 +64,7 @@ impl LuminanceSource for Luma8LuminanceSource {
fn crop(&self, left: usize, top: usize, width: usize, height: usize) -> Result<Self> {
Ok(Self {
dimensions: (width as u32, height as u32),
origin: (left as u32, top as u32),
origin: (self.origin.0 + left as u32, self.origin.1 + top as u32),
data: self.data.clone(),
inverted: self.inverted,
original_dimension: self.original_dimension,
@@ -212,13 +212,3 @@ mod tests {
);
}
}
// fn print_matrix(matrix: &[u8], width: usize, height: usize) {
// for y in 0..height {
// for x in 0..width {
// print!("{}, ",matrix[y*width + x ]);
// }
// println!()
// }
// println!()
// }

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -211,7 +211,7 @@ fn cpp_qrcode_black_box2_test_case() {
tester.add_test(46, 48, 0.0);
tester.add_test(46, 48, 90.0);
tester.add_test(46, 48, 180.0);
tester.add_test(46, 47, 180.0);
tester.add_test(46, 48, 270.0);
tester.ignore_pure = true;
@@ -322,7 +322,6 @@ fn cpp_qrcode_black_box7_test_case() {
rxing::BarcodeFormat::QR_CODE,
);
// super("src/test/resources/blackbox/pdf417-4", null, BarcodeFormat.PDF_417);
tester.add_test_complex(1, 1, 0, 0, 0.0);
tester.test_black_box();

View File

@@ -47,3 +47,306 @@ fn dynamsoft_all_supported_formats_image_fault() {
// ToDo: This test is incomplete. Some that should be detected aren't, and some that are detected shouldn't be.
}
#[cfg(feature = "image")]
#[test]
fn zxing_bench_issue_1() {
use rxing::{BarcodeFormat, 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/github_issue_cases/170050507-1f10f0ef-82ca-4e14-a2d2-4b288ec54809.png",
&mut hints,
)
.expect("must not fault during read");
assert_eq!(
results.len(),
9,
"must detect 9 barcodes, found: {}",
results.len()
);
assert_eq!(results[0].getText(), "CODE39");
assert_eq!(results[0].getBarcodeFormat(), &BarcodeFormat::CODE_39);
assert_eq!(results[1].getText(), "012345");
assert_eq!(results[1].getBarcodeFormat(), &BarcodeFormat::CODABAR);
assert_eq!(results[2].getText(), "CODE128");
assert_eq!(results[2].getBarcodeFormat(), &BarcodeFormat::CODE_128);
assert_eq!(results[3].getText(), "00123456");
assert_eq!(results[3].getBarcodeFormat(), &BarcodeFormat::ITF);
assert_eq!(results[4].getText(), "CODE93");
assert_eq!(results[4].getBarcodeFormat(), &BarcodeFormat::CODE_93);
assert_eq!(results[5].getText(), "012345678905");
assert_eq!(results[5].getBarcodeFormat(), &BarcodeFormat::UPC_A);
assert_eq!(results[6].getText(), "01234565");
assert_eq!(results[6].getBarcodeFormat(), &BarcodeFormat::EAN_8);
assert_eq!(results[7].getText(), "01234565");
assert_eq!(results[7].getBarcodeFormat(), &BarcodeFormat::UPC_E);
assert_eq!(results[8].getText(), "1234567890128");
assert_eq!(results[8].getBarcodeFormat(), &BarcodeFormat::EAN_13);
/*
Found 9 results
Result 0:
(code 39) CODE39
Result 1:
(codabar) 012345
Result 2:
(code 128) CODE128
Result 3:
(itf) 00123456
Result 4:
(code 93) CODE93
Result 5:
(upc a) 012345678905
Result 6:
(ean 8) 01234565
Result 7:
(upc e) 01234565
Result 8:
(ean 13) 1234567890128
*/
}
#[cfg(feature = "image")]
#[test]
fn issue_48() {
use rxing::{BarcodeFormat, 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/github_issue_cases/300908088-2b3ffe34-1067-48c9-8663-f841b5d0acf6.png",
&mut hints,
)
.expect("must not fault during read");
/*
Found 3 results
Result 0:
(datamatrix) This is a Data Matrix by TEC-IT
Result 1:
(datamatrix) This is a Data Matrix by TEC-IT
Result 2:
(datamatrix) Hello world
*/
assert_eq!(
results.len(),
3,
"must detect 3 barcodes, found: {}",
results.len()
);
assert_eq!(results[0].getText(), "This is a Data Matrix by TEC-IT");
assert_eq!(results[0].getBarcodeFormat(), &BarcodeFormat::DATA_MATRIX);
assert_eq!(results[1].getText(), "This is a Data Matrix by TEC-IT");
assert_eq!(results[1].getBarcodeFormat(), &BarcodeFormat::DATA_MATRIX);
assert_eq!(results[2].getText(), "Hello world");
assert_eq!(results[2].getBarcodeFormat(), &BarcodeFormat::DATA_MATRIX);
}
#[cfg(feature = "image")]
#[test]
fn zxing_bench_grey_image_issue_luma8_image() {
use image::DynamicImage;
use rxing::{
common::HybridBinarizer,
multi::{GenericMultipleBarcodeReader, MultipleBarcodeReader},
BarcodeFormat, BinaryBitmap, BufferedImageLuminanceSource, DecodeHintType, DecodeHintValue,
DecodingHintDictionary, Exceptions, MultiUseMultiFormatReader,
};
const FILE_NAME : &'static str = "test_resources/blackbox/github_issue_cases/170050507-1f10f0ef-82ca-4e14-a2d2-4b288ec54809.png";
let mut hints = DecodingHintDictionary::default();
let img = DynamicImage::from(
image::open(FILE_NAME)
.map_err(|e| Exceptions::runtime_with(format!("couldn't read {FILE_NAME}: {e}")))
.unwrap()
.to_luma8(),
);
let multi_format_reader = MultiUseMultiFormatReader::default();
let mut scanner = GenericMultipleBarcodeReader::new(multi_format_reader);
hints
.entry(DecodeHintType::TRY_HARDER)
.or_insert(DecodeHintValue::TryHarder(true));
let results = scanner
.decode_multiple_with_hints(
&mut BinaryBitmap::new(HybridBinarizer::new(BufferedImageLuminanceSource::new(img))),
&hints,
)
.expect("must not fault during read");
assert_eq!(
results.len(),
9,
"must detect 9 barcodes, found: {}",
results.len()
);
assert_eq!(results[0].getText(), "CODE39");
assert_eq!(results[0].getBarcodeFormat(), &BarcodeFormat::CODE_39);
assert_eq!(results[1].getText(), "012345");
assert_eq!(results[1].getBarcodeFormat(), &BarcodeFormat::CODABAR);
assert_eq!(results[2].getText(), "CODE128");
assert_eq!(results[2].getBarcodeFormat(), &BarcodeFormat::CODE_128);
assert_eq!(results[3].getText(), "00123456");
assert_eq!(results[3].getBarcodeFormat(), &BarcodeFormat::ITF);
assert_eq!(results[4].getText(), "CODE93");
assert_eq!(results[4].getBarcodeFormat(), &BarcodeFormat::CODE_93);
assert_eq!(results[5].getText(), "012345678905");
assert_eq!(results[5].getBarcodeFormat(), &BarcodeFormat::UPC_A);
assert_eq!(results[6].getText(), "01234565");
assert_eq!(results[6].getBarcodeFormat(), &BarcodeFormat::EAN_8);
assert_eq!(results[7].getText(), "01234565");
assert_eq!(results[7].getBarcodeFormat(), &BarcodeFormat::UPC_E);
assert_eq!(results[8].getText(), "1234567890128");
assert_eq!(results[8].getBarcodeFormat(), &BarcodeFormat::EAN_13);
/*
Found 9 results
Result 0:
(code 39) CODE39
Result 1:
(codabar) 012345
Result 2:
(code 128) CODE128
Result 3:
(itf) 00123456
Result 4:
(code 93) CODE93
Result 5:
(upc a) 012345678905
Result 6:
(ean 8) 01234565
Result 7:
(upc e) 01234565
Result 8:
(ean 13) 1234567890128
*/
}
#[cfg(feature = "image")]
#[test]
fn zxing_bench_grey_image_issue_raw_luma8() {
use rxing::{
common::HybridBinarizer,
multi::{GenericMultipleBarcodeReader, MultipleBarcodeReader},
BarcodeFormat, BinaryBitmap, DecodeHintType, DecodeHintValue, DecodingHintDictionary,
Exceptions, Luma8LuminanceSource, MultiUseMultiFormatReader,
};
const FILE_NAME : &'static str = "test_resources/blackbox/github_issue_cases/170050507-1f10f0ef-82ca-4e14-a2d2-4b288ec54809.png";
let mut hints = DecodingHintDictionary::default();
let img = image::open(FILE_NAME)
.map_err(|e| Exceptions::runtime_with(format!("couldn't read {FILE_NAME}: {e}")))
.unwrap();
let multi_format_reader = MultiUseMultiFormatReader::default();
let mut scanner = GenericMultipleBarcodeReader::new(multi_format_reader);
hints
.entry(DecodeHintType::TRY_HARDER)
.or_insert(DecodeHintValue::TryHarder(true));
let results = scanner
.decode_multiple_with_hints(
&mut BinaryBitmap::new(HybridBinarizer::new(Luma8LuminanceSource::new(
img.to_luma8().into_raw(),
img.width(),
img.height(),
))),
&hints,
)
.expect("must not fault during read");
assert_eq!(
results.len(),
9,
"must detect 9 barcodes, found: {}",
results.len()
);
assert_eq!(results[0].getText(), "CODE39");
assert_eq!(results[0].getBarcodeFormat(), &BarcodeFormat::CODE_39);
assert_eq!(results[1].getText(), "012345");
assert_eq!(results[1].getBarcodeFormat(), &BarcodeFormat::CODABAR);
assert_eq!(results[2].getText(), "CODE128");
assert_eq!(results[2].getBarcodeFormat(), &BarcodeFormat::CODE_128);
assert_eq!(results[3].getText(), "00123456");
assert_eq!(results[3].getBarcodeFormat(), &BarcodeFormat::ITF);
assert_eq!(results[4].getText(), "CODE93");
assert_eq!(results[4].getBarcodeFormat(), &BarcodeFormat::CODE_93);
assert_eq!(results[5].getText(), "012345678905");
assert_eq!(results[5].getBarcodeFormat(), &BarcodeFormat::UPC_A);
assert_eq!(results[6].getText(), "01234565");
assert_eq!(results[6].getBarcodeFormat(), &BarcodeFormat::EAN_8);
assert_eq!(results[7].getText(), "01234565");
assert_eq!(results[7].getBarcodeFormat(), &BarcodeFormat::UPC_E);
assert_eq!(results[8].getText(), "1234567890128");
assert_eq!(results[8].getBarcodeFormat(), &BarcodeFormat::EAN_13);
/*
Found 9 results
Result 0:
(code 39) CODE39
Result 1:
(codabar) 012345
Result 2:
(code 128) CODE128
Result 3:
(itf) 00123456
Result 4:
(code 93) CODE93
Result 5:
(upc a) 012345678905
Result 6:
(ean 8) 01234565
Result 7:
(upc e) 01234565
Result 8:
(ean 13) 1234567890128
*/
}