mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-26 12:22:34 +00:00
pdf 417 integration 100%
This commit is contained in:
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 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.pdf417;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.BinaryBitmap;
|
||||
import com.google.zxing.BufferedImageLuminanceSource;
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.LuminanceSource;
|
||||
import com.google.zxing.ReaderException;
|
||||
import com.google.zxing.RXingResult;
|
||||
import com.google.zxing.RXingResultMetadataType;
|
||||
import com.google.zxing.common.AbstractBlackBoxTestCase;
|
||||
import com.google.zxing.common.HybridBinarizer;
|
||||
import com.google.zxing.common.TestRXingResult;
|
||||
|
||||
import com.google.zxing.multi.MultipleBarcodeReader;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* This class tests Macro PDF417 barcode specific functionality. It ensures that information, which is split into
|
||||
* several barcodes can be properly combined again to yield the original data content.
|
||||
*
|
||||
* @author Guenther Grau
|
||||
*/
|
||||
public final class PDF417BlackBox4TestCase extends AbstractBlackBoxTestCase {
|
||||
private static final Logger log = Logger.getLogger(AbstractBlackBoxTestCase.class.getSimpleName());
|
||||
|
||||
private final MultipleBarcodeReader barcodeReader = new PDF417Reader();
|
||||
|
||||
private final List<TestRXingResult> testRXingResults = new ArrayList<>();
|
||||
|
||||
public PDF417BlackBox4TestCase() {
|
||||
super("src/test/resources/blackbox/pdf417-4", null, BarcodeFormat.PDF_417);
|
||||
testRXingResults.add(new TestRXingResult(3, 3, 0, 0, 0.0f));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testBlackBox() throws IOException {
|
||||
assertFalse(testRXingResults.isEmpty());
|
||||
|
||||
Map<String,List<Path>> imageFiles = getImageFileLists();
|
||||
int testCount = testRXingResults.size();
|
||||
|
||||
int[] passedCounts = new int[testCount];
|
||||
int[] tryHarderCounts = new int[testCount];
|
||||
|
||||
Path testBase = getTestBase();
|
||||
|
||||
for (Entry<String,List<Path>> testImageGroup : imageFiles.entrySet()) {
|
||||
log.fine(String.format("Starting Image Group %s", testImageGroup.getKey()));
|
||||
|
||||
String fileBaseName = testImageGroup.getKey();
|
||||
String expectedText;
|
||||
Path expectedTextFile = testBase.resolve(fileBaseName + ".txt");
|
||||
if (Files.exists(expectedTextFile)) {
|
||||
expectedText = readFileAsString(expectedTextFile, StandardCharsets.UTF_8);
|
||||
} else {
|
||||
expectedTextFile = testBase.resolve(fileBaseName + ".bin");
|
||||
assertTrue(Files.exists(expectedTextFile));
|
||||
expectedText = readFileAsString(expectedTextFile, StandardCharsets.ISO_8859_1);
|
||||
}
|
||||
|
||||
for (int x = 0; x < testCount; x++) {
|
||||
List<RXingResult> results = new ArrayList<>();
|
||||
for (Path imageFile : testImageGroup.getValue()) {
|
||||
BufferedImage image = ImageIO.read(imageFile.toFile());
|
||||
float rotation = testRXingResults.get(x).getRotation();
|
||||
BufferedImage rotatedImage = rotateImage(image, rotation);
|
||||
LuminanceSource source = new BufferedImageLuminanceSource(rotatedImage);
|
||||
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
|
||||
|
||||
try {
|
||||
results.addAll(Arrays.asList(decode(bitmap, false)));
|
||||
} catch (ReaderException ignored) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
results.sort(Comparator.comparingInt((RXingResult r) -> getMeta(r).getSegmentIndex()));
|
||||
StringBuilder resultText = new StringBuilder();
|
||||
String fileId = null;
|
||||
for (RXingResult result : results) {
|
||||
PDF417RXingResultMetadata resultMetadata = getMeta(result);
|
||||
assertNotNull("resultMetadata", resultMetadata);
|
||||
if (fileId == null) {
|
||||
fileId = resultMetadata.getFileId();
|
||||
}
|
||||
assertEquals("FileId", fileId, resultMetadata.getFileId());
|
||||
resultText.append(result.getText());
|
||||
}
|
||||
assertEquals("ExpectedText", expectedText, resultText.toString());
|
||||
passedCounts[x]++;
|
||||
tryHarderCounts[x]++;
|
||||
}
|
||||
}
|
||||
|
||||
// Print the results of all tests first
|
||||
int totalFound = 0;
|
||||
int totalMustPass = 0;
|
||||
|
||||
int numberOfTests = imageFiles.keySet().size();
|
||||
for (int x = 0; x < testRXingResults.size(); x++) {
|
||||
TestRXingResult testRXingResult = testRXingResults.get(x);
|
||||
log.info(String.format("Rotation %d degrees:", (int) testRXingResult.getRotation()));
|
||||
log.info(String.format(" %d of %d images passed (%d required)", passedCounts[x], numberOfTests,
|
||||
testRXingResult.getMustPassCount()));
|
||||
log.info(String.format(" %d of %d images passed with try harder (%d required)", tryHarderCounts[x],
|
||||
numberOfTests, testRXingResult.getTryHarderCount()));
|
||||
totalFound += passedCounts[x] + tryHarderCounts[x];
|
||||
totalMustPass += testRXingResult.getMustPassCount() + testRXingResult.getTryHarderCount();
|
||||
}
|
||||
|
||||
int totalTests = numberOfTests * testCount * 2;
|
||||
log.info(String.format("Decoded %d images out of %d (%d%%, %d required)", totalFound, totalTests, totalFound *
|
||||
100 /
|
||||
totalTests, totalMustPass));
|
||||
if (totalFound > totalMustPass) {
|
||||
log.warning(String.format("+++ Test too lax by %d images", totalFound - totalMustPass));
|
||||
} else if (totalFound < totalMustPass) {
|
||||
log.warning(String.format("--- Test failed by %d images", totalMustPass - totalFound));
|
||||
}
|
||||
|
||||
// Then run through again and assert if any failed
|
||||
for (int x = 0; x < testCount; x++) {
|
||||
TestRXingResult testRXingResult = testRXingResults.get(x);
|
||||
String label = "Rotation " + testRXingResult.getRotation() + " degrees: Too many images failed";
|
||||
assertTrue(label, passedCounts[x] >= testRXingResult.getMustPassCount());
|
||||
assertTrue("Try harder, " + label, tryHarderCounts[x] >= testRXingResult.getTryHarderCount());
|
||||
}
|
||||
}
|
||||
|
||||
private static PDF417RXingResultMetadata getMeta(RXingResult result) {
|
||||
return result.getRXingResultMetadata() == null ? null : (PDF417RXingResultMetadata) result.getRXingResultMetadata().get(
|
||||
RXingResultMetadataType.PDF417_EXTRA_METADATA);
|
||||
}
|
||||
|
||||
private RXingResult[] decode(BinaryBitmap source, boolean tryHarder) throws ReaderException {
|
||||
Map<DecodeHintType,Object> hints = new EnumMap<>(DecodeHintType.class);
|
||||
if (tryHarder) {
|
||||
hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
|
||||
}
|
||||
|
||||
return barcodeReader.decodeMultiple(source, hints);
|
||||
}
|
||||
|
||||
private Map<String,List<Path>> getImageFileLists() throws IOException {
|
||||
Map<String,List<Path>> result = new HashMap<>();
|
||||
for (Path file : getImageFiles()) {
|
||||
String testImageFileName = file.getFileName().toString();
|
||||
String fileBaseName = testImageFileName.substring(0, testImageFileName.indexOf('-'));
|
||||
List<Path> files = result.computeIfAbsent(fileBaseName, k -> new ArrayList<>());
|
||||
files.add(file);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
38
tests/PDF417BlackBox4TestCase.rs
Normal file
38
tests/PDF417BlackBox4TestCase.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2013 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.
|
||||
*/
|
||||
mod common;
|
||||
|
||||
use rxing::pdf417::PDF417Reader;
|
||||
|
||||
/**
|
||||
* This class tests Macro PDF417 barcode specific functionality. It ensures that information, which is split into
|
||||
* several barcodes can be properly combined again to yield the original data content.
|
||||
*
|
||||
* @author Guenther Grau
|
||||
*/
|
||||
#[test]
|
||||
fn pdf417_black_box4_test_case() {
|
||||
let mut tester = common::PDF417MultiImageSpanAbstractBlackBoxTestCase::new(
|
||||
"test_resources/blackbox/pdf417-4",
|
||||
PDF417Reader::default(),
|
||||
rxing::BarcodeFormat::PDF_417,
|
||||
);
|
||||
|
||||
// super("src/test/resources/blackbox/pdf417-4", null, BarcodeFormat.PDF_417);
|
||||
tester.add_test_complex(3, 3, 0, 0, 0.0);
|
||||
|
||||
tester.test_black_box()
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
mod abstract_black_box_test_case;
|
||||
mod pdf_417_multiimage_span;
|
||||
mod test_result;
|
||||
|
||||
pub use abstract_black_box_test_case::*;
|
||||
pub use pdf_417_multiimage_span::*;
|
||||
pub use test_result::*;
|
||||
|
||||
797
tests/common/pdf_417_multiimage_span.rs
Normal file
797
tests/common/pdf_417_multiimage_span.rs
Normal file
@@ -0,0 +1,797 @@
|
||||
/*
|
||||
* Copyright 2008 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,
|
||||
fs::{read_dir, read_to_string, File},
|
||||
io::Read,
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use encoding::Encoding;
|
||||
use rxing::{
|
||||
common::HybridBinarizer, multi::MultipleBarcodeReader, pdf417::PDF417RXingResultMetadata,
|
||||
BarcodeFormat, BinaryBitmap, BufferedImageLuminanceSource, DecodeHintType, DecodeHintValue,
|
||||
Exceptions, RXingResult, RXingResultMetadataType, RXingResultMetadataValue, Reader,
|
||||
};
|
||||
|
||||
use super::TestRXingResult;
|
||||
|
||||
/**
|
||||
* @author Sean Owen
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
pub struct PDF417MultiImageSpanAbstractBlackBoxTestCase<T: MultipleBarcodeReader + Reader> {
|
||||
test_base: Box<Path>,
|
||||
barcode_reader: T,
|
||||
expected_format: BarcodeFormat,
|
||||
test_rxing_results: Vec<TestRXingResult>,
|
||||
hints: HashMap<DecodeHintType, DecodeHintValue>,
|
||||
}
|
||||
|
||||
impl<T: MultipleBarcodeReader + Reader> PDF417MultiImageSpanAbstractBlackBoxTestCase<T> {
|
||||
pub fn build_test_base(test_base_path_suffix: &str) -> Box<Path> {
|
||||
// A little workaround to prevent aggravation in my IDE
|
||||
let test_base = Path::new(test_base_path_suffix);
|
||||
// if !testBase.exists() {
|
||||
// // try starting with 'core' since the test base is often given as the project root
|
||||
// testBase = Paths.get("core").resolve(testBasePathSuffix);
|
||||
// }
|
||||
test_base.to_owned().into()
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
test_base_path_suffix: &str,
|
||||
barcode_reader: T,
|
||||
expected_format: BarcodeFormat,
|
||||
) -> Self {
|
||||
Self {
|
||||
test_base: Self::build_test_base(test_base_path_suffix),
|
||||
barcode_reader,
|
||||
expected_format,
|
||||
test_rxing_results: Vec::new(),
|
||||
hints: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_test_base(&self) -> &Box<Path> {
|
||||
&self.test_base
|
||||
}
|
||||
|
||||
pub fn add_test(&mut self, must_pass_count: u32, try_harder_count: u32, rotation: f32) {
|
||||
self.add_test_complex(must_pass_count, try_harder_count, 0, 0, rotation);
|
||||
}
|
||||
|
||||
pub fn add_hint(&mut self, hint: DecodeHintType, value: DecodeHintValue) {
|
||||
self.hints.insert(hint, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new test for the current directory of images.
|
||||
*
|
||||
* @param mustPassCount The number of images which must decode for the test to pass.
|
||||
* @param tryHarderCount The number of images which must pass using the try harder flag.
|
||||
* @param maxMisreads Maximum number of images which can fail due to successfully reading the wrong contents
|
||||
* @param maxTryHarderMisreads Maximum number of images which can fail due to successfully
|
||||
* reading the wrong contents using the try harder flag
|
||||
* @param rotation The rotation in degrees clockwise to use for this test.
|
||||
*/
|
||||
pub fn add_test_complex(
|
||||
&mut self,
|
||||
must_pass_count: u32,
|
||||
try_harder_count: u32,
|
||||
max_misreads: u32,
|
||||
max_try_harder_misreads: u32,
|
||||
rotation: f32,
|
||||
) {
|
||||
self.test_rxing_results.push(TestRXingResult::new(
|
||||
must_pass_count,
|
||||
try_harder_count,
|
||||
max_misreads,
|
||||
max_try_harder_misreads,
|
||||
rotation,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn get_image_files(&self) -> Vec<PathBuf> {
|
||||
assert!(
|
||||
self.test_base.exists(),
|
||||
"Please download and install test images, and run from the 'core' directory"
|
||||
);
|
||||
// let paths = Vec::new();
|
||||
let path_search = read_dir(&self.test_base);
|
||||
const POSSIBLE_EXTENSIONS: &str = "jpg,jpeg,gif,png,JPG,JPEG,GIF,PNG";
|
||||
|
||||
let mut paths = path_search
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.filter(|r| r.is_ok()) // Get rid of Err variants for Result<DirEntry>
|
||||
.map(|r| r.unwrap().path()) // This is safe, since we only have the Ok variants
|
||||
.filter(|r| r.is_file()) // Filter out non-folders
|
||||
.filter(|r| POSSIBLE_EXTENSIONS.contains(r.extension().unwrap().to_str().unwrap()))
|
||||
// .map(|r| r.into_boxed_path())
|
||||
.collect::<Vec<PathBuf>>();
|
||||
|
||||
paths.sort();
|
||||
|
||||
paths
|
||||
}
|
||||
|
||||
pub fn get_reader(&self) -> &T {
|
||||
&self.barcode_reader
|
||||
}
|
||||
|
||||
pub fn test_black_box(&mut self) {
|
||||
assert!(!self.test_rxing_results.is_empty());
|
||||
|
||||
let imageFiles = self.getImageFileLists().expect("images");
|
||||
let testCount = self.test_rxing_results.len();
|
||||
|
||||
let mut passedCounts = vec![0; testCount];
|
||||
let mut tryHarderCounts = vec![0; testCount];
|
||||
|
||||
let testBase = self.get_test_base().clone();
|
||||
|
||||
for (name, files) in &imageFiles {
|
||||
// for (Entry<String,List<Path>> testImageGroup : imageFiles.entrySet()) {
|
||||
log::fine(format!("Starting Image Group {}", name));
|
||||
|
||||
let fileBaseName = name; //testImageGroup.getKey();
|
||||
// let expectedText : String;
|
||||
let mut expectedTextFile = testBase.clone().to_path_buf();
|
||||
expectedTextFile.push(fileBaseName);
|
||||
expectedTextFile.set_extension("txt");
|
||||
// let expectedTextFile = testBase.resolve(fileBaseName + ".txt");
|
||||
let expectedText = if expectedTextFile.exists() {
|
||||
Self::read_file_as_string(expectedTextFile)
|
||||
} else {
|
||||
let mut new_path = self.test_base.clone().to_path_buf();
|
||||
new_path.push(fileBaseName);
|
||||
new_path.set_extension("bin");
|
||||
//expectedTextFile = testBase.resolve(fileBaseName + ".bin");
|
||||
assert!(new_path.exists());
|
||||
Self::read_file_as_string(new_path)
|
||||
}
|
||||
.unwrap();
|
||||
|
||||
for x in 0..testCount {
|
||||
// for (int x = 0; x < testCount; x++) {
|
||||
let mut results = Vec::new();
|
||||
for imageFile in files {
|
||||
// for (Path imageFile : testImageGroup.getValue()) {
|
||||
let image = image::open(imageFile).unwrap();
|
||||
let rotation: f32 = self.test_rxing_results.get(x).expect("ok").get_rotation();
|
||||
let rotatedImage = Self::rotate_image(&image, rotation);
|
||||
let source = BufferedImageLuminanceSource::new(rotatedImage);
|
||||
let bitmap = BinaryBitmap::new(Rc::new(HybridBinarizer::new(Box::new(source))));
|
||||
|
||||
if let Ok(res) = Self::decode_pdf417(&bitmap, false, &mut self.barcode_reader) {
|
||||
for r in res {
|
||||
results.push(r);
|
||||
}
|
||||
}
|
||||
// try {
|
||||
// results.addAll(Arrays.asList(decode(bitmap, false)));
|
||||
// } catch (ReaderException ignored) {
|
||||
// // ignore
|
||||
// }
|
||||
}
|
||||
// results.sort(Comparator.comparingInt((RXingResult r) -> getMeta(r).getSegmentIndex()));
|
||||
// results.sort();
|
||||
let mut resultText = String::new(); //new StringBuilder();
|
||||
let mut fileId: Option<String> = None;
|
||||
for result in results {
|
||||
// for (RXingResult result : results) {
|
||||
let resultMetadata = Self::getMeta(&result);
|
||||
assert!(resultMetadata.is_some(), "resultMetadata");
|
||||
if fileId.is_none() {
|
||||
fileId = Some(resultMetadata.as_ref().unwrap().getFileId().to_owned());
|
||||
}
|
||||
assert_eq!(
|
||||
fileId,
|
||||
Some(resultMetadata.as_ref().unwrap().getFileId().to_owned()),
|
||||
"FileId"
|
||||
);
|
||||
resultText.push_str(result.getText());
|
||||
}
|
||||
assert_eq!(expectedText, resultText, "ExpectedText");
|
||||
passedCounts[x] += 1;
|
||||
tryHarderCounts[x] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Print the results of all tests first
|
||||
let mut totalFound = 0;
|
||||
let mut totalMustPass = 0;
|
||||
|
||||
let numberOfTests = imageFiles.len(); //imageFiles.keySet().size();
|
||||
for x in 0..self.test_rxing_results.len() {
|
||||
// for (int x = 0; x < testRXingResults.size(); x++) {
|
||||
let testRXingResult = self.test_rxing_results.get(x).expect("ok");
|
||||
log::info(format!(
|
||||
"Rotation {} degrees:",
|
||||
testRXingResult.get_rotation()
|
||||
));
|
||||
log::info(format!(
|
||||
" {} of {} images passed ({} required)",
|
||||
passedCounts[x],
|
||||
numberOfTests,
|
||||
testRXingResult.get_must_pass_count()
|
||||
));
|
||||
log::info(format!(
|
||||
" {} of {} images passed with try harder ({} required)",
|
||||
tryHarderCounts[x],
|
||||
numberOfTests,
|
||||
testRXingResult.get_try_harder_count()
|
||||
));
|
||||
totalFound += passedCounts[x] + tryHarderCounts[x];
|
||||
totalMustPass +=
|
||||
testRXingResult.get_must_pass_count() + testRXingResult.get_try_harder_count();
|
||||
}
|
||||
|
||||
let totalTests = numberOfTests * testCount * 2;
|
||||
log::info(format!(
|
||||
"Decoded {} images out of {} ({}%, {} required)",
|
||||
totalFound,
|
||||
totalTests,
|
||||
totalFound * 100 / totalTests,
|
||||
totalMustPass
|
||||
));
|
||||
if totalFound > totalMustPass as usize {
|
||||
log::warning(format!(
|
||||
"+++ Test too lax by {} images",
|
||||
totalFound - totalMustPass as usize
|
||||
));
|
||||
} else if totalFound < totalMustPass as usize {
|
||||
log::warning(format!(
|
||||
"--- Test failed by {} images",
|
||||
totalMustPass as usize - totalFound
|
||||
));
|
||||
}
|
||||
|
||||
// Then run through again and assert if any failed
|
||||
for x in 0..testCount {
|
||||
// for (int x = 0; x < testCount; x++) {
|
||||
let testRXingResult = self.test_rxing_results.get(x).expect("ok");
|
||||
// let label = "Rotation " + testRXingResult.getRotation() + " degrees: Too many images failed";
|
||||
assert!(
|
||||
passedCounts[x] >= testRXingResult.get_must_pass_count() as usize,
|
||||
"Rotation {} degrees: Too many images failed",
|
||||
testRXingResult.get_rotation()
|
||||
);
|
||||
assert!(
|
||||
tryHarderCounts[x] >= testRXingResult.get_try_harder_count() as usize,
|
||||
"Try harder, Rotation {} degrees: Too many images failed",
|
||||
testRXingResult.get_rotation()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test_black_box_old(&mut self) {
|
||||
assert!(!self.test_rxing_results.is_empty());
|
||||
|
||||
let image_files = self.get_image_files();
|
||||
let test_count = self.test_rxing_results.len();
|
||||
|
||||
let mut passed_counts = vec![0usize; test_count];
|
||||
let mut misread_counts = vec![0usize; test_count];
|
||||
let mut try_harder_counts = vec![0usize; test_count];
|
||||
let mut try_harder_misread_counts = vec![0usize; test_count];
|
||||
|
||||
for test_image in &image_files {
|
||||
// for (Path testImage : imageFiles) {
|
||||
log::info(format!("Starting {}", test_image.to_string_lossy()));
|
||||
|
||||
let image = image::open(test_image).unwrap(); //ImageIO.read(testImage.toFile());
|
||||
|
||||
//let testImageFileName = testImage.getFileName().toString();
|
||||
let file_base_name = test_image.file_stem().unwrap();
|
||||
//let expectedTextFile = self.testBase.resolve(fileBaseName + ".txt");
|
||||
let mut expected_text_file = test_image.clone();
|
||||
expected_text_file.set_extension("txt");
|
||||
let expected_text = if expected_text_file.exists() {
|
||||
Self::read_file_as_string(expected_text_file)
|
||||
} else {
|
||||
let mut new_path = self.test_base.clone().to_path_buf();
|
||||
new_path.push(file_base_name);
|
||||
new_path.set_extension("bin");
|
||||
//expectedTextFile = testBase.resolve(fileBaseName + ".bin");
|
||||
assert!(new_path.exists());
|
||||
Self::read_file_as_string(new_path)
|
||||
}
|
||||
.unwrap();
|
||||
|
||||
let mut expected_metadata_file: PathBuf = self.test_base.clone().to_path_buf();
|
||||
expected_metadata_file.push(format!("{}.metadata", file_base_name.to_str().unwrap()));
|
||||
expected_metadata_file.set_extension("txt");
|
||||
let expected_metadata_unfinished = if expected_metadata_file.exists() {
|
||||
java_properties::read(
|
||||
std::fs::File::open(expected_metadata_file)
|
||||
.expect("file exists, we already know that"),
|
||||
)
|
||||
.expect("valid java properties file")
|
||||
// try (BufferedReader reader = Files.newBufferedReader(expectedMetadataFile, StandardCharsets.UTF_8)) {
|
||||
// expectedMetadata.load(reader);
|
||||
// }
|
||||
} else {
|
||||
HashMap::new()
|
||||
};
|
||||
let expected_metadata = HashMap::new();
|
||||
for (k, v) in expected_metadata_unfinished {
|
||||
let new_k = RXingResultMetadataType::from(k);
|
||||
let _new_v = match new_k {
|
||||
RXingResultMetadataType::OTHER => RXingResultMetadataValue::OTHER(v),
|
||||
RXingResultMetadataType::ORIENTATION => {
|
||||
RXingResultMetadataValue::Orientation(v.parse().unwrap_or_default())
|
||||
}
|
||||
RXingResultMetadataType::BYTE_SEGMENTS => {
|
||||
RXingResultMetadataValue::ByteSegments(vec![v.into_bytes()])
|
||||
}
|
||||
RXingResultMetadataType::ERROR_CORRECTION_LEVEL => {
|
||||
RXingResultMetadataValue::ErrorCorrectionLevel(v)
|
||||
}
|
||||
RXingResultMetadataType::ISSUE_NUMBER => {
|
||||
RXingResultMetadataValue::IssueNumber(v.parse().unwrap_or_default())
|
||||
}
|
||||
RXingResultMetadataType::SUGGESTED_PRICE => {
|
||||
RXingResultMetadataValue::SuggestedPrice(v)
|
||||
}
|
||||
RXingResultMetadataType::POSSIBLE_COUNTRY => {
|
||||
RXingResultMetadataValue::PossibleCountry(v)
|
||||
}
|
||||
RXingResultMetadataType::UPC_EAN_EXTENSION => {
|
||||
RXingResultMetadataValue::UpcEanExtension(v)
|
||||
}
|
||||
RXingResultMetadataType::PDF417_EXTRA_METADATA => {
|
||||
RXingResultMetadataValue::Pdf417ExtraMetadata(Rc::new(
|
||||
PDF417RXingResultMetadata::default(),
|
||||
))
|
||||
}
|
||||
RXingResultMetadataType::STRUCTURED_APPEND_SEQUENCE => {
|
||||
RXingResultMetadataValue::StructuredAppendSequence(
|
||||
v.parse().unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
RXingResultMetadataType::STRUCTURED_APPEND_PARITY => {
|
||||
RXingResultMetadataValue::StructuredAppendParity(
|
||||
v.parse().unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
RXingResultMetadataType::SYMBOLOGY_IDENTIFIER => {
|
||||
RXingResultMetadataValue::SymbologyIdentifier(v)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
for x in 0..test_count {
|
||||
// for (int x = 0; x < testCount; x++) {
|
||||
let rotation = self.test_rxing_results.get(x).unwrap().get_rotation();
|
||||
let rotated_image = Self::rotate_image(&image, rotation);
|
||||
let source = BufferedImageLuminanceSource::new(rotated_image);
|
||||
let bitmap = BinaryBitmap::new(Rc::new(HybridBinarizer::new(Box::new(source))));
|
||||
|
||||
// if file_base_name == "15" {
|
||||
// let mut f = File::create("test_file_output.txt").unwrap();
|
||||
// write!(f,"{}", bitmap.getBlackMatrix().unwrap());
|
||||
// drop(f);
|
||||
// Self::rotate_image(&image, rotation).save("test_image.png").unwrap();
|
||||
// }
|
||||
|
||||
if let Ok(decoded) =
|
||||
self.decode(&bitmap, rotation, &expected_text, &expected_metadata, false)
|
||||
{
|
||||
if decoded {
|
||||
passed_counts[x] += 1;
|
||||
} else {
|
||||
misread_counts[x] += 1;
|
||||
}
|
||||
} else {
|
||||
log::fine(format!("could not read at rotation {}", rotation));
|
||||
}
|
||||
// try {
|
||||
// if (decode(bitmap, rotation, expectedText, expectedMetadata, false)) {
|
||||
// passedCounts[x]+=1;
|
||||
// } else {
|
||||
// misreadCounts[x]+=1;
|
||||
// }
|
||||
// } catch (ReaderException ignored) {
|
||||
// log::fine(format!("could not read at rotation {}", rotation));
|
||||
// }
|
||||
if let Ok(decoded) =
|
||||
self.decode(&bitmap, rotation, &expected_text, &expected_metadata, true)
|
||||
{
|
||||
if decoded {
|
||||
try_harder_counts[x] += 1;
|
||||
} else {
|
||||
try_harder_misread_counts[x] += 1;
|
||||
}
|
||||
} else {
|
||||
log::fine(format!("could not read at rotation {} w/TH", rotation));
|
||||
}
|
||||
// try {
|
||||
// if (decode(bitmap, rotation, expectedText, expectedMetadata, true)) {
|
||||
// tryHarderCounts[x]+=1;
|
||||
// } else {
|
||||
// tryHarderMisreadCounts[x]+=1;
|
||||
// }
|
||||
// } catch (ReaderException ignored) {
|
||||
// log::fine(format!("could not read at rotation {} w/TH", rotation));
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
// Print the results of all tests first
|
||||
let mut total_found = 0;
|
||||
let mut total_must_pass = 0;
|
||||
let mut total_misread = 0;
|
||||
let mut total_max_misread = 0;
|
||||
|
||||
for x in 0..self.test_rxing_results.len() {
|
||||
// for (int x = 0; x < testRXingResults.size(); x++) {
|
||||
let test_rxing_result = self.test_rxing_results.get(x).unwrap();
|
||||
log::info(format!(
|
||||
"Rotation {} degrees:",
|
||||
test_rxing_result.get_rotation()
|
||||
));
|
||||
log::info(format!(
|
||||
" {} of {} images passed ({} required)",
|
||||
passed_counts[x],
|
||||
image_files.len(),
|
||||
test_rxing_result.get_must_pass_count()
|
||||
));
|
||||
let mut failed = image_files.len() - passed_counts[x];
|
||||
log::info(format!(
|
||||
" {} failed due to misreads, {} not detected",
|
||||
misread_counts[x],
|
||||
failed - misread_counts[x]
|
||||
));
|
||||
log::info(format!(
|
||||
" {} of {} images passed with try harder ({} required)",
|
||||
try_harder_counts[x],
|
||||
image_files.len(),
|
||||
test_rxing_result.get_try_harder_count()
|
||||
));
|
||||
failed = image_files.len() - try_harder_counts[x];
|
||||
log::info(format!(
|
||||
" {} failed due to misreads, {} not detected",
|
||||
try_harder_misread_counts[x],
|
||||
failed - try_harder_misread_counts[x]
|
||||
));
|
||||
total_found += passed_counts[x] + try_harder_counts[x];
|
||||
total_must_pass +=
|
||||
test_rxing_result.get_must_pass_count() + test_rxing_result.get_try_harder_count();
|
||||
total_misread += misread_counts[x] + try_harder_misread_counts[x];
|
||||
total_max_misread += test_rxing_result.get_max_misreads()
|
||||
+ test_rxing_result.get_max_try_harder_misreads();
|
||||
}
|
||||
|
||||
let total_tests = image_files.len() * test_count * 2;
|
||||
log::info(format!(
|
||||
"Decoded {} images out of {} ({}, {} required)",
|
||||
total_found,
|
||||
total_tests,
|
||||
total_found * 100 / total_tests,
|
||||
total_must_pass
|
||||
));
|
||||
if total_found > total_must_pass as usize {
|
||||
log::warning(format!(
|
||||
"+++ Test too lax by {} images",
|
||||
total_found - total_must_pass as usize
|
||||
));
|
||||
} else if total_found < total_must_pass as usize {
|
||||
log::warning(format!(
|
||||
"--- Test failed by {} images",
|
||||
total_must_pass as usize - total_found
|
||||
));
|
||||
}
|
||||
|
||||
if total_misread < total_max_misread as usize {
|
||||
log::warning(format!(
|
||||
"+++ Test expects too many misreads by {} images",
|
||||
total_max_misread as usize - total_misread
|
||||
));
|
||||
} else if total_misread > total_max_misread as usize {
|
||||
log::warning(format!(
|
||||
"--- Test had too many misreads by {} images",
|
||||
total_misread - total_max_misread as usize
|
||||
));
|
||||
}
|
||||
|
||||
// Then run through again and assert if any failed
|
||||
for x in 0..test_count {
|
||||
// for (int x = 0; x < testCount; x++) {
|
||||
let test_rxing_result = self.test_rxing_results.get(x).unwrap();
|
||||
let label = format!(
|
||||
"Rotation {} degrees: Too many images failed",
|
||||
test_rxing_result.get_rotation()
|
||||
);
|
||||
assert!(
|
||||
passed_counts[x] >= test_rxing_result.get_must_pass_count() as usize,
|
||||
"{}",
|
||||
label
|
||||
);
|
||||
assert!(
|
||||
try_harder_counts[x] >= test_rxing_result.get_try_harder_count() as usize,
|
||||
"Try harder, {}",
|
||||
label,
|
||||
);
|
||||
let label = format!(
|
||||
"Rotation {} degrees: Too many images misread",
|
||||
test_rxing_result.get_rotation()
|
||||
);
|
||||
assert!(
|
||||
misread_counts[x] <= test_rxing_result.get_max_misreads() as usize,
|
||||
"{}",
|
||||
label
|
||||
);
|
||||
assert!(
|
||||
try_harder_misread_counts[x]
|
||||
<= test_rxing_result.get_max_try_harder_misreads() as usize,
|
||||
"Try harder, {}",
|
||||
label
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn decode(
|
||||
&mut self,
|
||||
source: &BinaryBitmap,
|
||||
rotation: f32,
|
||||
expected_text: &str,
|
||||
expected_metadata: &HashMap<RXingResultMetadataType, RXingResultMetadataValue>,
|
||||
try_harder: bool,
|
||||
) -> Result<bool, rxing::Exceptions> {
|
||||
let suffix = format!(
|
||||
" ({}rotation: {})",
|
||||
if try_harder { "try harder, " } else { "" },
|
||||
rotation
|
||||
);
|
||||
|
||||
let mut hints = self.hints.clone();
|
||||
if try_harder {
|
||||
hints.insert(DecodeHintType::TRY_HARDER, DecodeHintValue::TryHarder(true));
|
||||
// hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
|
||||
}
|
||||
|
||||
// Try in 'pure' mode mostly to exercise PURE_BARCODE code paths for exceptions;
|
||||
// not expected to pass, generally
|
||||
// let mut result = None;
|
||||
let mut pure_hints = HashMap::new();
|
||||
pure_hints.insert(
|
||||
DecodeHintType::PURE_BARCODE,
|
||||
DecodeHintValue::PureBarcode(true),
|
||||
);
|
||||
let mut result = if let Ok(res) = self.barcode_reader.decode_with_hints(source, &pure_hints)
|
||||
{
|
||||
Some(res)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if result.is_none() {
|
||||
result = Some(self.barcode_reader.decode_with_hints(source, &hints)?)
|
||||
}
|
||||
|
||||
let result = result.unwrap();
|
||||
|
||||
if &self.expected_format != result.getBarcodeFormat() {
|
||||
log::info(format!(
|
||||
"Format mismatch: expected '{:?}' but got '{:?}'{}",
|
||||
self.expected_format,
|
||||
result.getBarcodeFormat(),
|
||||
suffix
|
||||
));
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let result_text = result.getText();
|
||||
if expected_text != result_text {
|
||||
log::info(format!(
|
||||
"Content mismatch: expected '{}' but got '{}'{}",
|
||||
expected_text, result_text, suffix
|
||||
));
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let result_metadata = result.getRXingResultMetadata();
|
||||
for (key, value) in expected_metadata {
|
||||
// let key = RXingResultMetadataType.valueOf(metadatum.getKey().toString());
|
||||
// Object expectedValue = metadatum.getValue();
|
||||
let actual_value = result_metadata.get(key);
|
||||
if actual_value.is_none() || !(value == actual_value.unwrap()) {
|
||||
log::info(format!(
|
||||
"Metadata mismatch for key '{:?}': expected '{:?}' but got '{:?}'",
|
||||
key, value, actual_value
|
||||
));
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn read_file_as_string(file: PathBuf) -> Result<String, std::io::Error> {
|
||||
let string_contents = if let Some(ext) = file.extension() {
|
||||
if ext == "bin" {
|
||||
let mut buffer: Vec<u8> = Vec::new();
|
||||
File::open(&file)?.read_to_end(&mut buffer)?;
|
||||
encoding::all::ISO_8859_1
|
||||
.decode(&buffer, encoding::DecoderTrap::Replace)
|
||||
.expect("decode")
|
||||
} else {
|
||||
read_to_string(&file).expect("ok")
|
||||
}
|
||||
} else {
|
||||
"".to_owned()
|
||||
};
|
||||
// let string_contents = read_to_string(&file)?; //new String(Files.readAllBytes(file), charset);
|
||||
if string_contents.ends_with('\n') {
|
||||
log::info(format!("String contents of file {} end with a newline. This may not be intended and cause a test failure",file.to_string_lossy()));
|
||||
}
|
||||
Ok(string_contents)
|
||||
}
|
||||
|
||||
fn rotate_image(original: &image::DynamicImage, degrees: f32) -> image::DynamicImage {
|
||||
if degrees == 0.0 {
|
||||
return original.clone();
|
||||
}
|
||||
|
||||
// switch (original.getType()) {
|
||||
// case BufferedImage.TYPE_BYTE_INDEXED:
|
||||
// case BufferedImage.TYPE_BYTE_BINARY:
|
||||
// BufferedImage argb = new BufferedImage(original.getWidth(),
|
||||
// original.getHeight(),
|
||||
// BufferedImage.TYPE_INT_ARGB);
|
||||
// Graphics g = argb.createGraphics();
|
||||
// g.drawImage(original, 0, 0, null);
|
||||
// g.dispose();
|
||||
// original = argb;
|
||||
// break;
|
||||
// }
|
||||
|
||||
if degrees == 90.0 {
|
||||
original.rotate90()
|
||||
} else if degrees == 180.0 {
|
||||
original.rotate180()
|
||||
} else if degrees == 270.0 {
|
||||
original.rotate270()
|
||||
} else {
|
||||
let radians = degrees.to_radians();
|
||||
|
||||
{
|
||||
use image::DynamicImage;
|
||||
use image::Luma;
|
||||
use imageproc::geometric_transformations::*;
|
||||
|
||||
let i = rotate_about_center(
|
||||
&original.to_luma8(),
|
||||
radians,
|
||||
Interpolation::Nearest,
|
||||
Luma([u8::MAX / 2; 1]),
|
||||
);
|
||||
|
||||
DynamicImage::from(i)
|
||||
}
|
||||
}
|
||||
|
||||
// let radians = degrees.to_radians();
|
||||
|
||||
// {
|
||||
// use image::DynamicImage;
|
||||
// use image::Luma;
|
||||
// use imageproc::geometric_transformations::*;
|
||||
|
||||
// let i = rotate_about_center(
|
||||
// &original.to_luma8(),
|
||||
// radians,
|
||||
// Interpolation::Nearest,
|
||||
// Luma([u8::MAX / 2; 1]),
|
||||
// );
|
||||
|
||||
// DynamicImage::from(i)
|
||||
// }
|
||||
|
||||
// // Transform simply to find out the new bounding box (don't actually run the image through it)
|
||||
// AffineTransform at = new AffineTransform();
|
||||
// at.rotate(radians, original.getWidth() / 2.0, original.getHeight() / 2.0);
|
||||
// BufferedImageOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_BICUBIC);
|
||||
|
||||
// RectangularShape r = op.getBounds2D(original);
|
||||
// int width = (int) Math.ceil(r.getWidth());
|
||||
// int height = (int) Math.ceil(r.getHeight());
|
||||
|
||||
// // Real transform, now that we know the size of the new image and how to translate after we rotate
|
||||
// // to keep it centered
|
||||
// at = new AffineTransform();
|
||||
// at.rotate(radians, width / 2.0, height / 2.0);
|
||||
// at.translate((width - original.getWidth()) / 2.0,
|
||||
// (height - original.getHeight()) / 2.0);
|
||||
// op = new AffineTransformOp(at, AffineTransformOp.TYPE_BICUBIC);
|
||||
|
||||
// return op.filter(original, new BufferedImage(width, height, original.getType()));
|
||||
}
|
||||
|
||||
fn getMeta(result: &RXingResult) -> Option<Rc<PDF417RXingResultMetadata>> {
|
||||
if let Some(RXingResultMetadataValue::Pdf417ExtraMetadata(mtd)) = result
|
||||
.getRXingResultMetadata()
|
||||
.get(&RXingResultMetadataType::PDF417_EXTRA_METADATA)
|
||||
{
|
||||
/*(PDF417RXingResultMetadata)*/
|
||||
Some(mtd.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_pdf417(
|
||||
source: &BinaryBitmap,
|
||||
tryHarder: bool,
|
||||
barcodeReader: &mut T,
|
||||
) -> Result<Vec<RXingResult>, Exceptions> {
|
||||
let mut hints = HashMap::new(); //new EnumMap<>(DecodeHintType.class);
|
||||
if tryHarder {
|
||||
hints.insert(DecodeHintType::TRY_HARDER, DecodeHintValue::TryHarder(true));
|
||||
}
|
||||
|
||||
barcodeReader.decodeMultipleWithHints(source, &hints)
|
||||
}
|
||||
|
||||
fn getImageFileLists(&self) -> Result<HashMap<String, Vec<PathBuf>>, std::io::Error> {
|
||||
let mut result: HashMap<String, Vec<PathBuf>> = HashMap::new();
|
||||
for file in self.get_image_files() {
|
||||
let testImageFileName = file
|
||||
.file_name()
|
||||
.expect("file name exists")
|
||||
.to_str()
|
||||
.unwrap();
|
||||
let fileBaseName = if let Some(pos) = testImageFileName.find('-') {
|
||||
&testImageFileName[..pos]
|
||||
} else {
|
||||
testImageFileName
|
||||
};
|
||||
if !result.contains_key(fileBaseName) {
|
||||
result.insert(fileBaseName.to_owned(), Vec::new());
|
||||
}
|
||||
result.get_mut(fileBaseName).as_mut().unwrap().push(file);
|
||||
|
||||
// String fileBaseName = testImageFileName.substring(0, testImageFileName.indexOf('-'));
|
||||
//let files = result.computeIfAbsent(fileBaseName, k -> new ArrayList<>());
|
||||
// files.add(file);
|
||||
// result.insert(fileBaseName, vec![]);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
mod log {
|
||||
pub fn info(data: String) {
|
||||
prn("INFO", data)
|
||||
}
|
||||
|
||||
pub fn fine(data: String) {
|
||||
prn("FINE", data)
|
||||
}
|
||||
|
||||
pub fn warning(data: String) {
|
||||
prn("WARN", data)
|
||||
}
|
||||
|
||||
fn prn(level: &str, data: String) {
|
||||
println!("{} :: {}", level, data)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user