Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

50 pure itf barcode not being decoded #52

Merged
merged 5 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/binarizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ pub trait Binarizer {
*/
fn get_black_row(&self, y: usize) -> Result<Cow<BitArray>>;

// An alternate version of get_black_row that fetches the line from the matrix if
// it has already been generated, falling back to get_black_row if it hasn't.
fn get_black_row_from_matrix(&self, y: usize) -> Result<Cow<BitArray>>;

/**
* Converts a 2D array of luminance data to 1 bit data. As above, assume this method is expensive
* and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
Expand Down
2 changes: 1 addition & 1 deletion src/client/result/VCardResultParser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ fn toTypes(lists: Option<Vec<Vec<String>>>) -> Vec<String> {
let metadatum = list.get(i).unwrap_or(&String::default()).clone();
if let Some(equals) = metadatum.find('=') {
if "TYPE" == (metadatum[0..equals]).to_uppercase() {
v_type = metadatum[equals + 1..].to_owned();
metadatum[equals + 1..].clone_into(&mut v_type);
break;
}
} else {
Expand Down
8 changes: 8 additions & 0 deletions src/common/adaptive_threshold_binarizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,12 @@ impl<LS: LuminanceSource> Binarizer for AdaptiveThresholdBinarizer<LS> {
fn get_height(&self) -> usize {
self.source.get_height()
}

fn get_black_row_from_matrix(&self, y: usize) -> Result<Cow<BitArray>> {
if let Some(matrix) = self.matrix.get() {
Ok(Cow::Owned(matrix.getRow(y as u32)))
} else {
self.get_black_row(y)
}
}
}
12 changes: 12 additions & 0 deletions src/common/bit_array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ impl BitArray {
}
}

pub fn with_capacity(size: usize) -> Self {
Self {
bits: makeArray(size),
size: 0,
read_offset: 0,
}
}

/// For testing only
#[cfg(test)]
pub fn with_initial_values(bits: Vec<BaseType>, size: usize) -> Self {
Expand Down Expand Up @@ -319,6 +327,10 @@ impl BitArray {
}

pub fn appendBitArray(&mut self, other: BitArray) {
self.appendBitArrayRef(&other)
}

pub fn appendBitArrayRef(&mut self, other: &BitArray) {
let otherSize = other.size;
self.ensure_capacity(self.size + otherSize);
for i in 0..otherSize {
Expand Down
8 changes: 8 additions & 0 deletions src/common/global_histogram_binarizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,14 @@ impl<LS: LuminanceSource> Binarizer for GlobalHistogramBinarizer<LS> {
fn get_height(&self) -> usize {
self.height
}

fn get_black_row_from_matrix(&self, y: usize) -> Result<Cow<BitArray>> {
if let Some(matrix) = self.black_matrix.get() {
Ok(Cow::Owned(matrix.getRow(y as u32)))
} else {
self.get_black_row(y)
}
}
}

impl<LS: LuminanceSource> GlobalHistogramBinarizer<LS> {
Expand Down
8 changes: 8 additions & 0 deletions src/common/hybrid_binarizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ impl<LS: LuminanceSource> Binarizer for HybridBinarizer<LS> {
fn get_height(&self) -> usize {
self.ghb.get_height()
}

fn get_black_row_from_matrix(&self, y: usize) -> Result<Cow<BitArray>> {
if let Some(matrix) = self.black_matrix.get() {
Ok(Cow::Owned(matrix.getRow(y as u32)))
} else {
self.get_black_row(y)
}
}
}

// This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels.
Expand Down
2 changes: 1 addition & 1 deletion src/multi_format_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ impl MultiFormatReader {
* @param hints The set of hints to use for subsequent calls to decode(image)
*/
pub fn set_hints(&mut self, hints: &DecodingHintDictionary) {
self.hints.clone_from(&hints);
self.hints.clone_from(hints);

self.try_harder = matches!(
self.hints.get(&DecodeHintType::TRY_HARDER),
Expand Down
2 changes: 1 addition & 1 deletion src/multi_use_multi_format_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ impl MultiUseMultiFormatReader {
* @param hints The set of hints to use for subsequent calls to decode(image)
*/
pub fn set_hints(&mut self, hints: &DecodingHintDictionary) {
self.hints.clone_from(&hints);
self.hints.clone_from(hints);

self.try_harder = matches!(
self.hints.get(&DecodeHintType::TRY_HARDER),
Expand Down
61 changes: 2 additions & 59 deletions src/oned/one_d_code_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@
* limitations under the License.
*/

use std::collections::HashMap;

use crate::{
common::{BitMatrix, Result},
BarcodeFormat, EncodeHintType, EncodeHintValue, Exceptions, Writer,
BarcodeFormat, Exceptions, Writer,
};

use once_cell::sync::Lazy;
Expand Down Expand Up @@ -141,60 +140,4 @@ pub trait OneDimensionalCodeWriter: Writer {
// This seems like a decent idea for a default for all formats.
10
}
}

struct L;
impl Writer for L {
fn encode(
&self,
contents: &str,
format: &crate::BarcodeFormat,
width: i32,
height: i32,
) -> Result<crate::common::BitMatrix> {
self.encode_with_hints(contents, format, width, height, &HashMap::new())
}

fn encode_with_hints(
&self,
contents: &str,
format: &crate::BarcodeFormat,
width: i32,
height: i32,
hints: &crate::EncodingHintDictionary,
) -> Result<crate::common::BitMatrix> {
if contents.is_empty() {
return Err(Exceptions::illegal_argument_with("Found empty contents"));
}

if width < 0 || height < 0 {
return Err(Exceptions::illegal_argument_with(format!(
"Negative size is not allowed. Input: {width}x{height}"
)));
}
if let Some(supportedFormats) = self.getSupportedWriteFormats() {
if !supportedFormats.contains(format) {
return Err(Exceptions::illegal_argument_with(format!(
"Can only encode {supportedFormats:?}, but got {format:?}"
)));
}
}

let mut sidesMargin = self.getDefaultMargin();
if let Some(EncodeHintValue::Margin(margin)) = hints.get(&EncodeHintType::MARGIN) {
sidesMargin = margin.parse::<u32>().map_err(|e| {
Exceptions::illegal_argument_with(format!("couldnt parse {margin}: {e}"))
})?;
}

let code = self.encode_oned_with_hints(contents, hints)?;

Self::renderRXingResult(&code, width, height, sidesMargin)
}
}

impl OneDimensionalCodeWriter for L {
fn encode_oned(&self, _contents: &str) -> Result<Vec<bool>> {
unimplemented!()
}
}
}
58 changes: 57 additions & 1 deletion src/oned/one_d_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
use crate::{
common::{BitArray, Result},
point_f, Binarizer, BinaryBitmap, DecodeHintType, DecodeHintValue, DecodingHintDictionary,
Exceptions, RXingResult, RXingResultMetadataType, RXingResultMetadataValue, Reader,
Exceptions, LuminanceSource, RXingResult, RXingResultMetadataType, RXingResultMetadataValue,
Reader,
};

/**
Expand All @@ -28,6 +29,7 @@ use crate::{
* @author Sean Owen
*/
pub trait OneDReader: Reader {
const QUIET_ZONE: usize = 15;
/**
* We're going to examine rows from the middle outward, searching alternately above and below the
* middle, and farther out each time. rowStep is the number of rows between each successive
Expand Down Expand Up @@ -55,6 +57,26 @@ pub trait OneDReader: Reader {
hints.get(&DecodeHintType::TRY_HARDER),
Some(DecodeHintValue::TryHarder(true))
);

let try_pure = matches!(
hints.get(&DecodeHintType::PURE_BARCODE),
Some(DecodeHintValue::PureBarcode(true))
);

// Attempt to decode the barcode as "pure". This method may be very inneficient and uses
// a very poor version of a binarizer.
// ToDo: Add a better binarizer for pure barcodes
if try_pure {
let mid_line = 1.max(image.get_height() / 2);

let rw = image.get_source().get_row(mid_line);

let decoded = self.decode_pure(mid_line as u32, &rw, &hints);
if decoded.is_ok() {
return decoded;
}
}

let row_step = 1.max(height >> (if try_harder { 8 } else { 5 }));
let max_lines = if try_harder {
height // Look at the whole image, not just the center
Expand Down Expand Up @@ -146,6 +168,40 @@ pub trait OneDReader: Reader {
row: &BitArray,
hints: &DecodingHintDictionary,
) -> Result<RXingResult>;

fn decode_pure(
&mut self,
rowNumber: u32,
row: &[u8],
hints: &DecodingHintDictionary,
) -> Result<RXingResult> {
let new_row = pad_bitarray(row, Self::QUIET_ZONE);

self.decode_row(rowNumber, &new_row, hints)
}
}

// Add a buffer on either side of the row to mimic a quiet zone. This may not exist in a "pure barcode"
fn pad_bitarray(bits: &[u8], quiet_zone: usize) -> BitArray {
const PIXEL_COLOR_SPLIT_POINT: u8 = u8::MAX / 2;

let mut new_row = BitArray::with_capacity(bits.len() + (quiet_zone * 2));

let value = bits[0] >= PIXEL_COLOR_SPLIT_POINT;

for _ in 0..quiet_zone {
new_row.appendBit(value);
}

for bit in bits {
new_row.appendBit(bit < &PIXEL_COLOR_SPLIT_POINT)
}

for _ in 0..quiet_zone {
new_row.appendBit(value);
}

new_row
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/rxing_result_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

use std::rc::Rc;

use crate::{pdf417::PDF417RXingResultMetadata, Point, PointI, PointU};
use crate::pdf417::PDF417RXingResultMetadata;

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion tests/common/multiimage_span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ impl<T: MultipleBarcodeReader + Reader> MultiImageSpanAbstractBlackBoxTestCase<T
RXingResultMetadataType::FILTERED_RESOLUTION => {
// RXingResultMetadataValue::FilteredResolution(v.parse().unwrap())
let arr: Box<[usize]> = v
.split(",")
.split(',')
.map(|str_source| str_source.parse::<usize>().unwrap_or_default())
.take(2)
.collect();
Expand Down
83 changes: 83 additions & 0 deletions tests/github_issues.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,3 +396,86 @@ fn test_issue_49() {

assert_eq!(EXPECTED_ITF_TEXT, itf_result.getText());
}

#[cfg(feature = "image")]
#[test]
fn test_issue_50() {
use rxing::{
common::HybridBinarizer, BarcodeFormat, BinaryBitmap, DecodeHintType, DecodeHintValue,
DecodingHintDictionary, Exceptions, Luma8LuminanceSource, MultiUseMultiFormatReader,
Reader,
};

const FILE_NAME : &str = "test_resources/blackbox/github_issue_cases/346304318-16acfb7a-4a41-4b15-af78-7ccf061e72bd.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 mut scanner = MultiUseMultiFormatReader::default();

hints
.entry(DecodeHintType::TRY_HARDER)
.or_insert(DecodeHintValue::TryHarder(true));

hints
.entry(DecodeHintType::PURE_BARCODE)
.or_insert(DecodeHintValue::PureBarcode(true));

let result = scanner
.decode_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");

const EXPECTED_FORMAT: &BarcodeFormat = &BarcodeFormat::ITF;
const EXPECTED_ITF_TEXT: &str = "85680000001403303242024070501202400002535294";

assert_eq!(EXPECTED_ITF_TEXT, result.getText());

assert_eq!(EXPECTED_FORMAT, result.getBarcodeFormat());
}

#[cfg(feature = "image")]
#[test]
fn test_issue_50_2() {
use rxing::{
common::AdaptiveThresholdBinarizer, BarcodeFormat, BinaryBitmap, DecodeHintType,
DecodeHintValue, DecodingHintDictionary, Exceptions, Luma8LuminanceSource,
MultiUseMultiFormatReader, Reader,
};

const FILE_NAME : &str = "test_resources/blackbox/github_issue_cases/346304318-16acfb7a-4a41-4b15-af78-7ccf061e72bd.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 mut scanner = MultiUseMultiFormatReader::default();

hints
.entry(DecodeHintType::TRY_HARDER)
.or_insert(DecodeHintValue::TryHarder(true));

let result = scanner
.decode_with_hints(
&mut BinaryBitmap::new(AdaptiveThresholdBinarizer::new(
Luma8LuminanceSource::new(img.to_luma8().into_raw(), img.width(), img.height()),
1,
)),
&hints,
)
.expect("must not fault during read");

const EXPECTED_FORMAT: &BarcodeFormat = &BarcodeFormat::ITF;
const EXPECTED_ITF_TEXT: &str = "85680000001403303242024070501202400002535294";

assert_eq!(EXPECTED_ITF_TEXT, result.getText());

assert_eq!(EXPECTED_FORMAT, result.getBarcodeFormat());
}
Loading