diff --git a/src/buffered_image_luminance_source.rs b/src/buffered_image_luminance_source.rs index a736ec1..6689c84 100644 --- a/src/buffered_image_luminance_source.rs +++ b/src/buffered_image_luminance_source.rs @@ -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) +} diff --git a/src/luma_luma_source.rs b/src/luma_luma_source.rs index 9b525ee..9efa8d4 100644 --- a/src/luma_luma_source.rs +++ b/src/luma_luma_source.rs @@ -64,7 +64,7 @@ impl LuminanceSource for Luma8LuminanceSource { fn crop(&self, left: usize, top: usize, width: usize, height: usize) -> Result { 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!() -// } diff --git a/test_resources/blackbox/github_issue_cases/170050507-1f10f0ef-82ca-4e14-a2d2-4b288ec54809.png b/test_resources/blackbox/github_issue_cases/170050507-1f10f0ef-82ca-4e14-a2d2-4b288ec54809.png new file mode 100644 index 0000000..7b817f5 Binary files /dev/null and b/test_resources/blackbox/github_issue_cases/170050507-1f10f0ef-82ca-4e14-a2d2-4b288ec54809.png differ diff --git a/test_resources/blackbox/github_issue_cases/300908088-2b3ffe34-1067-48c9-8663-f841b5d0acf6.png b/test_resources/blackbox/github_issue_cases/300908088-2b3ffe34-1067-48c9-8663-f841b5d0acf6.png new file mode 100644 index 0000000..f931d84 Binary files /dev/null and b/test_resources/blackbox/github_issue_cases/300908088-2b3ffe34-1067-48c9-8663-f841b5d0acf6.png differ diff --git a/tests/cpp_qr_code_blackbox_tests.rs b/tests/cpp_qr_code_blackbox_tests.rs index 6908c41..a0df900 100644 --- a/tests/cpp_qr_code_blackbox_tests.rs +++ b/tests/cpp_qr_code_blackbox_tests.rs @@ -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(); diff --git a/tests/github_issues.rs b/tests/github_issues.rs index 212fcc3..e924351 100644 --- a/tests/github_issues.rs +++ b/tests/github_issues.rs @@ -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 + + */ +}