wip: incomplete port checking

This commit is contained in:
Henry Schimke
2024-01-26 11:11:50 -06:00
parent e01e392a4a
commit 347bcf8fe3
13 changed files with 236 additions and 99 deletions

View File

@@ -19,7 +19,7 @@
use std::borrow::Cow; use std::borrow::Cow;
use crate::{ use crate::{
common::{BitArray, BitMatrix, Result}, common::{BitArray, BitMatrix, LineOrientation, Result},
LuminanceSource, LuminanceSource,
}; };
@@ -66,6 +66,9 @@ pub trait Binarizer {
*/ */
fn get_black_matrix(&self) -> Result<&BitMatrix>; fn get_black_matrix(&self) -> Result<&BitMatrix>;
/// Get a row or column of the image
fn get_black_line(&self, l: usize, lt: LineOrientation) -> Result<Cow<BitArray>>;
/** /**
* Creates a new object with the same type as this Binarizer implementation, but with pristine * Creates a new object with the same type as this Binarizer implementation, but with pristine
* state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache * state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache

View File

@@ -19,7 +19,7 @@
use std::{borrow::Cow, fmt}; use std::{borrow::Cow, fmt};
use crate::{ use crate::{
common::{BitArray, BitMatrix, Result}, common::{BitArray, BitMatrix, LineOrientation, Result},
Binarizer, LuminanceSource, Binarizer, LuminanceSource,
}; };
@@ -72,6 +72,11 @@ impl<B: Binarizer> BinaryBitmap<B> {
self.binarizer.get_black_row(y) self.binarizer.get_black_row(y)
} }
/// Get a row or column of the image
pub fn get_black_line(&self, l: usize, lt: LineOrientation) -> Result<Cow<BitArray>> {
self.binarizer.get_black_line(l, lt)
}
/** /**
* Converts a 2D array of luminance data to 1 bit. As above, assume this method is expensive * Converts a 2D array of luminance data to 1 bit. As above, assume this method is expensive
* and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or * and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or

View File

@@ -18,6 +18,7 @@
// import java.util.Arrays; // import java.util.Arrays;
use std::ops::Index;
use std::{cmp, fmt}; use std::{cmp, fmt};
use crate::common::Result; use crate::common::Result;
@@ -420,6 +421,19 @@ impl From<BitArray> for Vec<u8> {
impl From<BitArray> for Vec<bool> { impl From<BitArray> for Vec<bool> {
fn from(value: BitArray) -> Self { fn from(value: BitArray) -> Self {
// let mut array = vec![false; value.size];
// for (pixel, element) in array.iter_mut().enumerate().take(value.size) {
// *element = value.get(pixel);
// }
// array
Self::from(&value)
}
}
impl From<&BitArray> for Vec<bool> {
fn from(value: &BitArray) -> Self {
let mut array = vec![false; value.size]; let mut array = vec![false; value.size];
for (pixel, element) in array.iter_mut().enumerate().take(value.size) { for (pixel, element) in array.iter_mut().enumerate().take(value.size) {
@@ -429,3 +443,11 @@ impl From<BitArray> for Vec<bool> {
array array
} }
} }
impl Index<usize> for BitArray {
type Output = bool;
fn index(&self, index: usize) -> &Self::Output {
&self.get(index)
}
}

View File

@@ -1,3 +1,4 @@
use crate::common::cpp_essentials::PatternRow;
use crate::common::BitMatrix; use crate::common::BitMatrix;
use crate::common::Result; use crate::common::Result;
use crate::point_f; use crate::point_f;

View File

@@ -51,6 +51,10 @@ impl PatternRow {
pub fn sum(&self) -> PatternType { pub fn sum(&self) -> PatternType {
self.0.iter().sum() self.0.iter().sum()
} }
pub fn rev(&mut self) {
self.0.reverse()
}
} }
impl IntoIterator for PatternRow { impl IntoIterator for PatternRow {

View File

@@ -51,6 +51,7 @@ pub struct GlobalHistogramBinarizer<LS: LuminanceSource> {
source: LS, source: LS,
black_matrix: OnceCell<BitMatrix>, black_matrix: OnceCell<BitMatrix>,
black_row_cache: Vec<OnceCell<BitArray>>, black_row_cache: Vec<OnceCell<BitArray>>,
black_column_cache: Vec<OnceCell<BitArray>>,
} }
impl<LS: LuminanceSource> Binarizer for GlobalHistogramBinarizer<LS> { impl<LS: LuminanceSource> Binarizer for GlobalHistogramBinarizer<LS> {
@@ -106,6 +107,10 @@ impl<LS: LuminanceSource> Binarizer for GlobalHistogramBinarizer<LS> {
Ok(Cow::Borrowed(row)) Ok(Cow::Borrowed(row))
} }
fn get_black_line(&self, l: usize, lt: super::LineOrientation) -> Result<Cow<BitArray>> {
unimplemented!()
}
// Does not sharpen the data, as this call is intended to only be used by 2D Readers. // Does not sharpen the data, as this call is intended to only be used by 2D Readers.
fn get_black_matrix(&self) -> Result<&BitMatrix> { fn get_black_matrix(&self) -> Result<&BitMatrix> {
let matrix = self let matrix = self
@@ -137,6 +142,7 @@ impl<LS: LuminanceSource> GlobalHistogramBinarizer<LS> {
height: source.get_height(), height: source.get_height(),
black_matrix: OnceCell::new(), black_matrix: OnceCell::new(),
black_row_cache: vec![OnceCell::default(); source.get_height()], black_row_cache: vec![OnceCell::default(); source.get_height()],
black_column_cache: vec![OnceCell::default(); source.get_width()],
source, source,
} }
} }

View File

@@ -64,6 +64,10 @@ impl<LS: LuminanceSource> Binarizer for HybridBinarizer<LS> {
self.ghb.get_black_row(y) self.ghb.get_black_row(y)
} }
fn get_black_line(&self, l: usize, lt: super::LineOrientation) -> Result<Cow<BitArray>> {
self.ghb.get_black_line(l, lt)
}
/** /**
* Calculates the final BitMatrix once for all requests. This could be called once from the * Calculates the final BitMatrix once for all requests. This could be called once from the
* constructor instead, but there are some advantages to doing it lazily, such as making * constructor instead, but there are some advantages to doing it lazily, such as making

View File

@@ -0,0 +1,5 @@
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum LineOrientation {
Row,
Column,
}

View File

@@ -114,6 +114,9 @@ pub use quad::*;
pub mod cpp_essentials; pub mod cpp_essentials;
mod line_orientation;
pub use line_orientation::LineOrientation;
#[cfg(feature = "otsu_level")] #[cfg(feature = "otsu_level")]
mod otsu_level_binarizer; mod otsu_level_binarizer;
#[cfg(feature = "otsu_level")] #[cfg(feature = "otsu_level")]

View File

@@ -1,4 +1,4 @@
use crate::{point_f, Point}; use crate::{point_f, Exceptions, Point};
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct Quadrilateral(pub [Point; 4]); pub struct Quadrilateral(pub [Point; 4]);
@@ -272,3 +272,19 @@ impl From<[Point; 4]> for Quadrilateral {
Self(value) Self(value)
} }
} }
impl TryFrom<&Vec<Point>> for Quadrilateral {
type Error = Exceptions;
fn try_from(value: &Vec<Point>) -> Result<Self, Self::Error> {
if value.len() == 4 {
Ok(
Self(
[value[0], value[1], value[2], value[3]]
)
)
}else{
Err(Exceptions::INDEX_OUT_OF_BOUNDS)
}
}
}

View File

@@ -5,8 +5,11 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::{ use crate::{
common::cpp_essentials::{ common::{
FindLeftGuardBy, FixedPattern, IsRightGuard, PatternView, ToInt, ToIntPos, cpp_essentials::{
FindLeftGuardBy, FixedPattern, IsRightGuard, PatternView, ToInt, ToIntPos,
},
BitArray,
}, },
point, point_i, BarcodeFormat, DecodeHintValue, DecodingHintDictionary, Exceptions, PointI, point, point_i, BarcodeFormat, DecodeHintValue, DecodingHintDictionary, Exceptions, PointI,
RXingResult, RXingResult,
@@ -251,13 +254,20 @@ impl<'a> RowReader for DXFilmEdgeReader<'_> {
next.skipSymbol(); next.skipSymbol();
// Read the data bits // Read the data bits
let mut dataBits: Vec<u8> = Vec::default(); let mut dataBits = BitArray::default();
while (next.isValidWithN(1) && dataBits.len() < clock.dataLength() as usize) { while (next.isValidWithN(1) && dataBits.get_size() < clock.dataLength() as usize) {
let modules = (next[0] as f32 / clock.moduleSize() + 0.5) as u32; let modules = (next[0] as f32 / clock.moduleSize() + 0.5) as u32;
// even index means we are at a bar, otherwise at a space // even index means we are at a bar, otherwise at a space
// dataBits.appendBits(if next.index() % 2 == 0 {0xFFFFFFFF} else {0x0}, modules); // dataBits.appendBits(if next.index() % 2 == 0 {0xFFFFFFFF} else {0x0}, modules);
for i in 0..modules { for i in 0..modules {
dataBits.push((if next.index() % 2 == 0 { 0xFF } else { 0x0 } >> (i - 1)) & 1); dataBits.appendBits(
if next.index() % 2 == 0 {
0xFFFFFFFF
} else {
0x0
},
modules as usize,
);
// should it be 0xFFFFFFFF // should it be 0xFFFFFFFF
} }
@@ -265,7 +275,7 @@ impl<'a> RowReader for DXFilmEdgeReader<'_> {
} }
// Check the data track length // Check the data track length
if (dataBits.len() != clock.dataLength() as usize) { if (dataBits.get_size() != clock.dataLength() as usize) {
return Err(Exceptions::NOT_FOUND); return Err(Exceptions::NOT_FOUND);
} }
@@ -277,31 +287,36 @@ impl<'a> RowReader for DXFilmEdgeReader<'_> {
} }
// The following bits are always white (=false), they are separators. // The following bits are always white (=false), they are separators.
if (dataBits[0] != 0 if (dataBits[0] != false //0
|| dataBits[8] != 0 || dataBits[8] != false //0
|| (if clock.hasFrameNr { || (if clock.hasFrameNr {
(dataBits[20] != 0 || dataBits[22] != 0) (dataBits[20] != false/*0*/ || dataBits[22] != false/*0*/)
} else { } else {
dataBits[14] != 0 dataBits[14] != false//0
})) }))
{ {
return Err(Exceptions::NOT_FOUND); return Err(Exceptions::NOT_FOUND);
} }
// Check the parity bit // Check the parity bit
let signalSum = dataBits.iter().rev().skip(2).sum::<u8>(); //Reduce(dataBits.begin(), dataBits.end() - 2, 0); let db_hld = Into::<Vec<bool>>::into(dataBits); //.iter().rev().skip(2).fold(0, |acc, e| acc + u8::from(*e));
let parityBit = *(dataBits.last().unwrap_or(&0)); let signalSum = db_hld
.iter()
.rev()
.skip(2)
.fold(0, |acc, e| acc + u8::from(*e)); //dataBits.iter().rev().skip(2).sum::<u8>(); //Reduce(dataBits.begin(), dataBits.end() - 2, 0);
let parityBit = u8::from(*(db_hld.last().unwrap_or(&false)));
if (signalSum % 2 != parityBit) { if (signalSum % 2 != parityBit) {
return Err(Exceptions::NOT_FOUND); return Err(Exceptions::NOT_FOUND);
} }
// Compute the DX 1 number (product number) // Compute the DX 1 number (product number)
let Some(productNumber) = ToIntPos(&dataBits, 1, 7) else { let Some(productNumber) = ToIntPos(&Into::<Vec<u8>>::into(dataBits), 1, 7) else {
return Err(Exceptions::NOT_FOUND); return Err(Exceptions::NOT_FOUND);
}; };
// Compute the DX 2 number (generation number) // Compute the DX 2 number (generation number)
let Some(generationNumber) = ToIntPos(&dataBits, 9, 4) else { let Some(generationNumber) = ToIntPos(&Into::<Vec<u8>>::into(dataBits), 9, 4) else {
return Err(Exceptions::NOT_FOUND); return Err(Exceptions::NOT_FOUND);
}; };
@@ -311,9 +326,9 @@ impl<'a> RowReader for DXFilmEdgeReader<'_> {
// txt.reserve(10); // txt.reserve(10);
txt = (productNumber.to_string()) + "-" + (&generationNumber.to_string()); txt = (productNumber.to_string()) + "-" + (&generationNumber.to_string());
if (clock.hasFrameNr) { if (clock.hasFrameNr) {
let frameNr = ToIntPos(&dataBits, 13, 6).unwrap_or(0); let frameNr = ToIntPos(&Into::<Vec<u8>>::into(dataBits), 13, 6).unwrap_or(0);
txt += &("/".to_owned() + &(frameNr.to_string())); txt += &("/".to_owned() + &(frameNr.to_string()));
if (dataBits[19] != 0) { if (dataBits[19] != false/*0*/) {
txt += "A"; txt += "A";
} }
} }
@@ -332,7 +347,7 @@ impl<'a> RowReader for DXFilmEdgeReader<'_> {
Ok(RXingResult::new( Ok(RXingResult::new(
&txt, &txt,
dataBits, dataBits.into(),
Vec::new(), Vec::new(),
BarcodeFormat::DXFilmEdge, BarcodeFormat::DXFilmEdge,
)) ))

View File

@@ -8,14 +8,14 @@
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use crate::common::cpp_essentials::{PatternRow, PatternView}; use crate::common::cpp_essentials::{GetPatternRow, PatternRow, PatternView};
use crate::Binarizer; use crate::Binarizer;
use crate::{multi::MultipleBarcodeReader, RXingResult, Reader}; use crate::{multi::MultipleBarcodeReader, RXingResult, Reader};
use crate::{ use crate::{
point, BarcodeFormat, BinaryBitmap, DecodingHintDictionary, Exceptions, PointT, ResultPoint, point, BarcodeFormat, BinaryBitmap, DecodingHintDictionary, Exceptions, PointT, ResultPoint,
}; };
use crate::common::Result; use crate::common::{LineOrientation, Quadrilateral, Result};
use super::dxfilm_edge_reader::DXFilmEdgeReader; use super::dxfilm_edge_reader::DXFilmEdgeReader;
use super::row_reader::RowReader; use super::row_reader::RowReader;
@@ -32,14 +32,14 @@ pub struct ODReader<'a> {
impl<'a> ODReader<'_> { impl<'a> ODReader<'_> {
/** /**
* We're going to examine rows from the middle outward, searching alternately above and below the * 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 * middle, and farther out each time. rowStep is the number of rows between each successive
* attempt above and below the middle. So we'd scan row middle, then middle - rowStep, then * attempt above and below the middle. So we'd scan row middle, then middle - rowStep, then
* middle + rowStep, then middle - (2 * rowStep), etc. * middle + rowStep, then middle - (2 * rowStep), etc.
* rowStep is bigger as the image is taller, but is always at least 1. We've somewhat arbitrarily * rowStep is bigger as the image is taller, but is always at least 1. We've somewhat arbitrarily
* decided that moving up and down by about 1/16 of the image is pretty good; we try more of the * decided that moving up and down by about 1/16 of the image is pretty good; we try more of the
* image if "trying harder". * image if "trying harder".
*/ */
pub fn DoDecode<B: Binarizer>( pub fn DoDecode<B: Binarizer>(
reader: &DXFilmEdgeReader, reader: &DXFilmEdgeReader,
image: &BinaryBitmap<B>, image: &BinaryBitmap<B>,
@@ -50,13 +50,15 @@ impl<'a> ODReader<'_> {
minLineCount: u32, minLineCount: u32,
returnErrors: bool, returnErrors: bool,
) -> Vec<RXingResult> { ) -> Vec<RXingResult> {
let res: Vec<RXingResult> = Vec::new(); let mut res: Vec<Option<RXingResult>> = Vec::new();
let decodingState = Vec::new(); let mut decodingState: Vec<&mut Option<super::row_reader::DecodingState>> = Vec::new();
// std::vector<std::unique_ptr<RowReader::DecodingState>> decodingState(readers.size()); // std::vector<std::unique_ptr<RowReader::DecodingState>> decodingState(readers.size());
let width: i32 = image.get_width() as i32; let mut minLineCount = minLineCount;
let height: i32 = image.get_height() as i32;
let mut width: i32 = image.get_width() as i32;
let mut height: i32 = image.get_height() as i32;
if (rotate) { if (rotate) {
std::mem::swap(&mut width, &mut height); std::mem::swap(&mut width, &mut height);
@@ -73,36 +75,39 @@ impl<'a> ODReader<'_> {
32 32
}), }),
); );
let maxLines: i32 = if tryHarder let maxLines: i32 = if tryHarder {
{height} else // Look at the whole image, not just the center height // Look at the whole image, not just the center
{15}; // 15 rows spaced 1/32 apart is roughly the middle half of the image } else {
15 // 15 rows spaced 1/32 apart is roughly the middle half of the image
};
if (isPure) { if (isPure) {
minLineCount = 1; minLineCount = 1;
} }
let checkRows = Vec::new(); let mut checkRows = Vec::new();
let bars: PatternRow = PatternRow::new(vec![0; 128]); // e.g. EAN-13 has 59 bars/spaces let mut bars: PatternRow = PatternRow::new(vec![0; 128]); // e.g. EAN-13 has 59 bars/spaces
// bars.reserve(128); // e.g. EAN-13 has 59 bars/spaces // bars.reserve(128); // e.g. EAN-13 has 59 bars/spaces
// #ifdef PRINT_DEBUG // #ifdef PRINT_DEBUG
// BitMatrix dbg(width, height); // BitMatrix dbg(width, height);
// #endif // #endif
'outer: for i in 0..maxLines { let mut i = 0;
'outer: while i < maxLines {
// for (int i = 0; i < maxLines; i++) { // for (int i = 0; i < maxLines; i++) {
// Scanning from the middle out. Determine which row we're looking at next: // Scanning from the middle out. Determine which row we're looking at next:
let rowStepsAboveOrBelow: i32 = (i + 1) / 2; let rowStepsAboveOrBelow: i32 = (i + 1) / 2;
let isAbove: bool = (i & 0x01) == 0; // i.e. is x even? let isAbove: bool = (i & 0x01) == 0; // i.e. is x even?
let rowNumber: i32 = middle let mut rowNumber: i32 = middle
+ rowStep + rowStep
* (if isAbove { * (if isAbove {
rowStepsAboveOrBelow rowStepsAboveOrBelow
} else { } else {
-rowStepsAboveOrBelow -rowStepsAboveOrBelow
}); });
let isCheckRow: bool = false; let mut isCheckRow: bool = false;
if (rowNumber < 0 || rowNumber >= height) { if (rowNumber < 0 || rowNumber >= height) {
// Oops, if we run off the top or bottom, stop // Oops, if we run off the top or bottom, stop
break; break;
@@ -110,18 +115,36 @@ impl<'a> ODReader<'_> {
// See if we have additional check rows (see below) to process // See if we have additional check rows (see below) to process
if (!checkRows.is_empty()) { if (!checkRows.is_empty()) {
--i; //--i;
rowNumber = checkRows.back(); i -= 1;
checkRows.pop_back(); rowNumber = *checkRows.last().unwrap_or(&0);
checkRows.pop();
isCheckRow = true; isCheckRow = true;
if (rowNumber < 0 || rowNumber >= height) { if (rowNumber < 0 || rowNumber >= height) {
continue; continue;
} }
} }
if (!image.getPatternRow(rowNumber, if rotate { 90 } else { 0 }, bars)) { let br: Vec<bool> = if let Ok(r) = image.get_black_line(
rowNumber as usize,
if rotate {
LineOrientation::Column
} else {
LineOrientation::Row
},
) {
r.as_ref().into()
} else {
continue; continue;
} };
// let img = if rotate {let a = image.rotate_counter_clockwise(); &a} else {image};
// let Ok(black_row ) = img.get_black_row(rowNumber as usize) else {continue;};
// let br : Vec<bool> = black_row.as_ref().into();
GetPatternRow(&br, &mut bars);
// if (!image.getPatternRow(rowNumber, if rotate { 90 } else { 0 }, bars)) {
// continue;
// }
// #ifdef PRINT_DEBUG // #ifdef PRINT_DEBUG
// bool val = false; // bool val = false;
@@ -145,7 +168,7 @@ impl<'a> ODReader<'_> {
if (upsideDown) { if (upsideDown) {
// reverse the row and continue // reverse the row and continue
// std::reverse(bars.begin(), bars.end()); // std::reverse(bars.begin(), bars.end());
bars.reverse(); bars.rev();
} }
let readers = vec![reader]; let readers = vec![reader];
// Look for a barcode // Look for a barcode
@@ -153,73 +176,75 @@ impl<'a> ODReader<'_> {
// for (size_t r = 0; r < readers.size(); ++r) { // for (size_t r = 0; r < readers.size(); ++r) {
// If this is a pure symbol, then checking a single non-empty line is sufficient for all but the stacked // If this is a pure symbol, then checking a single non-empty line is sufficient for all but the stacked
// DataBar codes. They are the only ones using the decodingState, which we can use as a flag here. // DataBar codes. They are the only ones using the decodingState, which we can use as a flag here.
if (isPure && i && !decodingState[r]) { if (isPure && i > 0 && decodingState[r].is_none()) {
continue; continue;
} }
let next = PatternView::from(bars); let mut next = PatternView::new(&bars);
loop { loop {
let result = readers[r] let mut result_hld = readers[r]
.decodePattern(rowNumber, &mut next, decodingState[r]) .decodePattern(rowNumber as u32, &mut next, decodingState[r])
.ok(); .ok();
if (result.isValid() || (returnErrors && result.error())) { if result_hld.is_some() /*|| (returnErrors && result.is_none())*/ {
IncrementLineCount(&result); let mut result = result_hld.as_mut().unwrap();
IncrementLineCount(&mut result);
if (upsideDown) { if (upsideDown) {
// update position (flip horizontally). // update position (flip horizontally).
let points = result.position(); let points = result.getPointsMut();
for p in points { for p in points {
// for (auto& p : points) { // for (auto& p : points) {
p = point(width - p.getX() - 1, p.getY()); *p = point(width as f32 - p.getX() - 1.0, p.getY());
} }
result.addPoints(points); // result.addPoints(points);
// result.setPosition(std::move(points)); // result.setPosition(std::move(points));
} }
if (rotate) { if (rotate) {
let points = result.position(); let points = result.getPointsMut();
for p in points { for p in points {
// for (auto& p : points) { // for (auto& p : points) {
p = point(p.getY(), width - p.getX() - 1); *p = point(p.getY(), width as f32- p.getX() - 1.0);
} }
result.addPoints(points); // result.addPoints(points);
// result.setPosition(std::move(points)); // result.setPosition(std::move(points));
} }
// check if we know this code already // check if we know this code already
for other in res { for other_hld in res.iter_mut() {
let Some(mut other) = other_hld else{ continue;};
// for (auto& other : res) { // for (auto& other : res) {
if (result == other) { if (result == &other) {
// merge the position information // merge the position information
let dTop = PointT::maxAbsComponent( let dTop = PointT::maxAbsComponent(
other.position().topLeft() - result.position().topLeft(), other.getPoints()[0] - result.getPoints()[0],
); );
let dBot = PointT::maxAbsComponent( let dBot = PointT::maxAbsComponent(
other.position().bottomLeft() - result.position().topLeft(), other.getPoints()[2] - result.getPoints()[0],
); );
let points = other.position(); let mut points = other.getPoints().clone();
if (dTop < dBot if (dTop < dBot
|| (dTop == dBot || (dTop == dBot
&& rotate && rotate
^ (PointT::sumAbsComponent(points[0]) ^ (PointT::sumAbsComponent(points[0])
> PointT::sumAbsComponent( > PointT::sumAbsComponent(
result.position()[0], result.getPoints()[0],
)))) ))))
{ {
points[0] = result.position()[0]; points[0] = result.getPoints()[0];
points[1] = result.position()[1]; points[1] = result.getPoints()[1];
} else { } else {
points[2] = result.position()[2]; points[2] = result.getPoints()[2];
points[3] = result.position()[3]; points[3] = result.getPoints()[3];
} }
other.setPosition(points); other.replace_points(points);
IncrementLineCount(&other); IncrementLineCount(&mut other);
// clear the result, so we don't insert it again below // clear the result, so we don't insert it again below
result = None; //Result(); result_hld = None; //Result();
break; break;
} }
} }
if (result.format() != BarcodeFormat::UNSUPORTED_FORMAT) { if (result.getBarcodeFormat() != &BarcodeFormat::UNSUPORTED_FORMAT) {
res.push(result); res.push(Some(result.clone()));
// res.push_back(std::move(result)); // res.push_back(std::move(result));
// if we found a valid code we have not seen before but a minLineCount > 1, // if we found a valid code we have not seen before but a minLineCount > 1,
@@ -234,10 +259,14 @@ impl<'a> ODReader<'_> {
} }
} }
if (maxSymbols if (maxSymbols > 0
&& res.iter().fold(0, |acc, e| { && res.iter().fold(0, |acc, e| {
acc + i32::from((r.lineCount() >= minLineCount)) if let Some(itm) = &res[r] {
}) == maxSymbols) acc + i32::from((itm.line_count() >= minLineCount as usize))
}else {
acc
}
}) == maxSymbols as i32)
{ {
break 'outer; break 'outer;
} }
@@ -245,43 +274,53 @@ impl<'a> ODReader<'_> {
// make sure we make progress and we start the next try on a bar // make sure we make progress and we start the next try on a bar
next.shift(2 - (next.index() % 2)); next.shift(2 - (next.index() % 2));
next.extend(); next.extend();
if !(tryHarder && next.size()) { if !(tryHarder && next.size() > 0) {
break; break;
} }
} //while (tryHarder && next.size()); } //while (tryHarder && next.size());
} }
} }
i += 1;
} }
// out: // out:
// remove all symbols with insufficient line count // remove all symbols with insufficient line count
let it = res.iter().filter(|e| e.lineCount() < minLineCount); res.retain(|e| if let Some(itm) = e {itm.line_count() < minLineCount as usize} else {false});
// let it = std::remove_if(res.begin(), res.end(), [&](auto&& r) { return r.lineCount() < minLineCount; }); // let it = std::remove_if(res.begin(), res.end(), [&](auto&& r) { return r.lineCount() < minLineCount; });
res.erase(it, res.end()); // res.erase(it, res.end());
// if symbols overlap, remove the one with a lower line count // if symbols overlap, remove the one with a lower line count
for (i, a) in res.iter().enumerate() { for (i, a_hld) in res.iter().enumerate() {
let a = a_hld.as_ref().unwrap();
// for (auto a = res.begin(); a != res.end(); ++a){ // for (auto a = res.begin(); a != res.end(); ++a){
for b in res.iter().skip(i) { for b_hld in res.iter().skip(i) {
let b = b_hld.as_ref().unwrap();
// for (auto b = std::next(a); b != res.end(); ++b){ // for (auto b = std::next(a); b != res.end(); ++b){
if (PointT::HaveIntersectingBoundingBoxes(a.position(), b.position())) { let Ok(q1) =Quadrilateral::try_from(a.getPoints()) else {continue;};
*(if a.lineCount() < b.lineCount() { a } else { b }) = None; let Ok(q2) = Quadrilateral::try_from(b.getPoints()) else {continue;};
if (Quadrilateral::have_intersecting_bounding_boxes(&q1, &q2)) {
*(if a.line_count() < b.line_count() {
a_hld
} else {
b_hld
}) = None;
} }
} }
} }
//TODO: C++20 res.erase_if() //TODO: C++20 res.erase_if()
it = res res
.iter()
.filter(|r| r.getBarcodeFormat() == BarcodeFormat::None); .retain(|r| if let Some(itm) = r {
itm.getBarcodeFormat() == &BarcodeFormat::UNSUPORTED_FORMAT } else { false });
// it = std::remove_if(res.begin(), res.end(), [](auto&& r) { return r.format() == BarcodeFormat::None; }); // it = std::remove_if(res.begin(), res.end(), [](auto&& r) { return r.format() == BarcodeFormat::None; });
res.erase(it, res.end()); // res.erase(it, res.end());
// #ifdef PRINT_DEBUG // #ifdef PRINT_DEBUG
// SaveAsPBM(dbg, rotate ? "od-log-r.pnm" : "od-log.pnm"); // SaveAsPBM(dbg, rotate ? "od-log-r.pnm" : "od-log.pnm");
// #endif // #endif
res res.iter().cloned().filter_map(|e| e).collect()
} }
} }
@@ -291,7 +330,7 @@ impl<'a> ODReader<'_> {
hints: &DecodingHintDictionary, hints: &DecodingHintDictionary,
image: &BinaryBitmap<B>, image: &BinaryBitmap<B>,
) -> Result<RXingResult> { ) -> Result<RXingResult> {
let result = Self::DoDecode( let mut result = Self::DoDecode(
&self.reader, &self.reader,
image, image,
self.try_harder, self.try_harder,
@@ -315,7 +354,7 @@ impl<'a> ODReader<'_> {
); );
} }
result.first().ok_or(Exceptions::NOT_FOUND) result.first().cloned().ok_or(Exceptions::NOT_FOUND)
// return FirstOrDefault(std::move(result)); // return FirstOrDefault(std::move(result));
} }
@@ -325,7 +364,7 @@ impl<'a> ODReader<'_> {
image: &BinaryBitmap<B>, image: &BinaryBitmap<B>,
maxSymbols: u32, maxSymbols: u32,
) -> Result<Vec<RXingResult>> { ) -> Result<Vec<RXingResult>> {
let resH = Self::DoDecode( let mut resH = Self::DoDecode(
&self.reader, &self.reader,
image, image,
self.try_harder, self.try_harder,
@@ -335,8 +374,8 @@ impl<'a> ODReader<'_> {
self.min_line_count, self.min_line_count,
self.return_errors, self.return_errors,
); );
if ((!maxSymbols || (resH) < maxSymbols) && self.try_rotate) { if ((!(maxSymbols != 0) || (resH.len()) < maxSymbols as usize) && self.try_rotate) {
let resV = Self::DoDecode( let mut resV = Self::DoDecode(
&self.reader, &self.reader,
image, image,
self.try_harder, self.try_harder,
@@ -397,7 +436,6 @@ impl<'a> ODReader<'_> {
} }
} }
fn IncrementLineCount(r: &RXingResult) { fn IncrementLineCount(r: &mut RXingResult) {
unimplemented!() r.set_line_count(r.line_count() + 1)
// ++r._lineCount;
} }

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, PartialEq, Eq)]
pub struct RXingResult { pub struct RXingResult {
text: String, text: String,
rawBytes: Vec<u8>, rawBytes: Vec<u8>,
@@ -39,6 +39,7 @@ pub struct RXingResult {
format: BarcodeFormat, format: BarcodeFormat,
resultMetadata: HashMap<RXingResultMetadataType, RXingResultMetadataValue>, resultMetadata: HashMap<RXingResultMetadataType, RXingResultMetadataValue>,
timestamp: u128, timestamp: u128,
line_count: usize,
} }
impl RXingResult { impl RXingResult {
pub fn new( pub fn new(
@@ -83,6 +84,7 @@ impl RXingResult {
format, format,
resultMetadata: HashMap::new(), resultMetadata: HashMap::new(),
timestamp, timestamp,
line_count: 0,
} }
} }
@@ -95,6 +97,7 @@ impl RXingResult {
format: prev.format, format: prev.format,
resultMetadata: prev.resultMetadata, resultMetadata: prev.resultMetadata,
timestamp: prev.timestamp, timestamp: prev.timestamp,
line_count: prev.line_count,
} }
} }
@@ -229,6 +232,18 @@ impl RXingResult {
pub fn getTimestamp(&self) -> u128 { pub fn getTimestamp(&self) -> u128 {
self.timestamp self.timestamp
} }
pub fn line_count(&self) -> usize {
self.line_count
}
pub fn set_line_count(&mut self, lc: usize) {
self.line_count = lc
}
pub fn replace_points(&mut self, points: Vec<Point>) {
self.resultPoints = points
}
} }
impl fmt::Display for RXingResult { impl fmt::Display for RXingResult {