update for shared state and improved performance

This commit is contained in:
Henry Schimke
2023-01-02 16:38:05 -06:00
parent 72f69dd6a0
commit 65f7c4d01b
46 changed files with 505 additions and 359 deletions

View File

@@ -27,8 +27,6 @@
// */
// public final class BitMatrixTestCase extends Assert {
use crate::common::BitArray;
use super::BitMatrix;
static BIT_MATRIX_POINTS: [u32; 6] = [1, 2, 2, 0, 3, 1];
@@ -151,17 +149,17 @@ fn test_get_row() {
}
// Should allocate
let array = matrix.getRow(2, BitArray::new());
let array = matrix.getRow(2);
assert_eq!(102, array.getSize());
// Should reallocate
let mut array2 = BitArray::with_size(60);
array2 = matrix.getRow(2, array2);
// let mut array2 = BitArray::with_size(60);
let array2 = matrix.getRow(2);
assert_eq!(102, array2.getSize());
// Should use provided object, with original BitArray size
let mut array3 = BitArray::with_size(200);
array3 = matrix.getRow(2, array3);
// let mut array3 = BitArray::with_size(200);
let array3 = matrix.getRow(2);
assert_eq!(200, array3.getSize());
for x in 0..102 {

View File

@@ -81,7 +81,7 @@ impl BitArray {
* @return true iff bit i is set
*/
pub fn get(&self, i: usize) -> bool {
return (self.bits[i / 32] & (1 << (i & 0x1F))) != 0;
(self.bits[i / 32] & (1 << (i & 0x1F))) != 0
}
/**

View File

@@ -267,11 +267,11 @@ impl BitMatrix {
"input matrix dimensions do not match".to_owned(),
));
}
let mut rowArray = BitArray::with_size(self.width as usize);
// let mut rowArray = BitArray::with_size(self.width as usize);
for y in 0..self.height {
//for (int y = 0; y < height; y++) {
let offset = y as usize * self.row_size;
rowArray = mask.getRow(y, rowArray);
let rowArray = mask.getRow(y);
let row = rowArray.getBitArray();
for x in 0..self.row_size {
//for (int x = 0; x < rowSize; x++) {
@@ -344,16 +344,17 @@ impl BitMatrix {
* @return The resulting BitArray - this reference should always be used even when passing
* your own row
*/
pub fn getRow(&self, y: u32, row: BitArray) -> BitArray {
let mut rw: BitArray = if row.getSize() < self.width as usize {
BitArray::with_size(self.width as usize)
} else {
let mut z = row; //row.clone();
z.clear();
z
// row.clear();
// row.clone()
};
pub fn getRow(&self, y: u32) -> BitArray {
// let mut rw: BitArray = if row.getSize() < self.width as usize {
// BitArray::with_size(self.width as usize)
// } else {
// let mut z = row; //row.clone();
// z.clear();
// z
// // row.clear();
// // row.clone()
// };
let mut rw = BitArray::with_size(self.width as usize);
let offset = y as usize * self.row_size;
for x in 0..self.row_size {
@@ -404,14 +405,14 @@ impl BitMatrix {
* Modifies this {@code BitMatrix} to represent the same but rotated 180 degrees
*/
pub fn rotate180(&mut self) {
let mut topRow = BitArray::with_size(self.width as usize);
let mut bottomRow = BitArray::with_size(self.width as usize);
// let mut topRow = BitArray::with_size(self.width as usize);
// let mut bottomRow = BitArray::with_size(self.width as usize);
let maxHeight = (self.height + 1) / 2;
for i in 0..maxHeight {
//for (int i = 0; i < maxHeight; i++) {
topRow = self.getRow(i, topRow);
let mut topRow = self.getRow(i);
let bottomRowIndex = self.height - 1 - i;
bottomRow = self.getRow(bottomRowIndex, bottomRow);
let mut bottomRow = self.getRow(bottomRowIndex);
topRow.reverse();
bottomRow.reverse();
self.setRow(i, &bottomRow);
@@ -632,6 +633,25 @@ impl BitMatrix {
// public BitMatrix clone() {
// return new BitMatrix(width, height, rowSize, bits.clone());
// }
// pub fn crop(&self, top:usize, left:usize, height: usize, width: usize) -> BitMatrix {
// let area = self.bits.iter().skip(self.row_size * top).take(self.row_size * height)
// .copied().collect::<Vec<u32>>();
// let new_bits = area.chunks(self.row_size)
// .skip(left).take(width).flatten().copied().collect::<Vec<u32>>();
// Self { width: width, height: height, row_size: width, bits: () }
// }
pub fn crop(&self, top: usize, left: usize, height: usize, width: usize) -> BitMatrix {
let mut new_bm = BitMatrix::new(width as u32, height as u32).expect("create empty");
for y in top..top + height {
// let row = self.getRow(y as u32);
for x in left..left + width {
if self.get(x as u32, y as u32) {
new_bm.set(x as u32, y as u32)
}
}
}
new_bm
}
}
impl fmt::Display for BitMatrix {

View File

@@ -20,7 +20,7 @@
// import com.google.zxing.LuminanceSource;
// import com.google.zxing.NotFoundException;
use std::rc::Rc;
use std::{cell::RefCell, rc::Rc};
use crate::{Binarizer, Exceptions, LuminanceSource};
@@ -38,11 +38,12 @@ use super::{BitArray, BitMatrix};
* @author Sean Owen
*/
pub struct GlobalHistogramBinarizer {
luminances: Vec<u8>,
buckets: Vec<u32>,
_luminances: Vec<u8>,
width: usize,
height: usize,
source: Box<dyn LuminanceSource>,
black_matrix: Option<BitMatrix>,
black_row_cache: Vec<Option<BitArray>>,
}
impl Binarizer for GlobalHistogramBinarizer {
@@ -51,26 +52,23 @@ impl Binarizer for GlobalHistogramBinarizer {
}
// Applies simple sharpening to the row data to improve performance of the 1D Readers.
fn getBlackRow(&self, y: usize, row: &mut BitArray) -> Result<BitArray, Exceptions> {
fn getBlackRow(&mut self, y: usize) -> Result<BitArray, Exceptions> {
if let Some(black_row) = &self.black_row_cache[y] {
return Ok(black_row.clone());
}
let source = self.getLuminanceSource();
let width = source.getWidth();
let mut row = if row.getSize() < width {
BitArray::with_size(width)
} else {
let mut z = row.clone();
z.clear();
z
};
let mut row = BitArray::with_size(width);
// self.initArrays(width);
let localLuminances = source.getRow(y, &self.luminances);
let mut localBuckets = self.buckets.clone();
let localLuminances = source.getRow(y);
let mut localBuckets = [0; GlobalHistogramBinarizer::LUMINANCE_BUCKETS]; //self.buckets.clone();
for x in 0..width {
// for (int x = 0; x < width; x++) {
localBuckets
[((localLuminances[x]) >> GlobalHistogramBinarizer::LUMINANCE_SHIFT) as usize] += 1;
}
let blackPoint = self.estimateBlackPoint(&localBuckets)?;
let blackPoint = Self::estimateBlackPoint(&localBuckets)?;
if width < 3 {
// Special case for very small images
@@ -94,12 +92,54 @@ impl Binarizer for GlobalHistogramBinarizer {
center = right;
}
}
self.black_row_cache[y] = Some(row.clone());
Ok(row)
}
// Does not sharpen the data, as this call is intended to only be used by 2D Readers.
fn getBlackMatrix(&self) -> Result<BitMatrix, Exceptions> {
let source = self.getLuminanceSource();
fn getBlackMatrix(&mut self) -> Result<&BitMatrix, Exceptions> {
if self.black_matrix.is_none() {
self.black_matrix =
Some(Self::build_black_matrix(&self.source).expect("matrix must generate"))
}
Ok(self.black_matrix.as_ref().unwrap())
}
fn createBinarizer(
&self,
source: Box<dyn crate::LuminanceSource>,
) -> Rc<RefCell<dyn Binarizer>> {
return Rc::new(RefCell::new(GlobalHistogramBinarizer::new(source)));
}
fn getWidth(&self) -> usize {
self.width
}
fn getHeight(&self) -> usize {
self.height
}
}
impl GlobalHistogramBinarizer {
const LUMINANCE_BITS: usize = 5;
const LUMINANCE_SHIFT: usize = 8 - GlobalHistogramBinarizer::LUMINANCE_BITS;
const LUMINANCE_BUCKETS: usize = 1 << GlobalHistogramBinarizer::LUMINANCE_BITS;
// const EMPTY: [u8; 0] = [0; 0];
pub fn new(source: Box<dyn LuminanceSource>) -> Self {
Self {
_luminances: vec![0; source.getWidth()],
width: source.getWidth(),
height: source.getHeight(),
black_matrix: None,
black_row_cache: vec![None; source.getHeight()],
source: source,
}
}
fn build_black_matrix(source: &Box<dyn LuminanceSource>) -> Result<BitMatrix, Exceptions> {
// let source = source.getLuminanceSource();
let width = source.getWidth();
let height = source.getHeight();
let mut matrix = BitMatrix::new(width as u32, height as u32)?;
@@ -107,11 +147,11 @@ impl Binarizer for GlobalHistogramBinarizer {
// Quickly calculates the histogram by sampling four rows from the image. This proved to be
// more robust on the blackbox tests than sampling a diagonal as we used to do.
// self.initArrays(width);
let mut localBuckets = self.buckets.clone();
let mut localBuckets = [0; GlobalHistogramBinarizer::LUMINANCE_BUCKETS]; //self.buckets.clone();
for y in 1..5 {
// for (int y = 1; y < 5; y++) {
let row = height * y / 5;
let localLuminances = source.getRow(row, &self.luminances);
let localLuminances = source.getRow(row);
let right = (width * 4) / 5;
let mut x = width / 5;
while x < right {
@@ -121,7 +161,7 @@ impl Binarizer for GlobalHistogramBinarizer {
x += 1;
}
}
let blackPoint = self.estimateBlackPoint(&localBuckets)?;
let blackPoint = Self::estimateBlackPoint(&localBuckets)?;
// We delay reading the entire image luminance until the black point estimation succeeds.
// Although we end up reading four rows twice, it is consistent with our motto of
@@ -142,35 +182,6 @@ impl Binarizer for GlobalHistogramBinarizer {
Ok(matrix)
}
fn createBinarizer(&self, source: Box<dyn crate::LuminanceSource>) -> Rc<dyn Binarizer> {
return Rc::new(GlobalHistogramBinarizer::new(source));
}
fn getWidth(&self) -> usize {
self.width
}
fn getHeight(&self) -> usize {
self.height
}
}
impl GlobalHistogramBinarizer {
const LUMINANCE_BITS: usize = 5;
const LUMINANCE_SHIFT: usize = 8 - GlobalHistogramBinarizer::LUMINANCE_BITS;
const LUMINANCE_BUCKETS: usize = 1 << GlobalHistogramBinarizer::LUMINANCE_BITS;
// const EMPTY: [u8; 0] = [0; 0];
pub fn new(source: Box<dyn LuminanceSource>) -> Self {
Self {
luminances: vec![0; source.getWidth()],
buckets: vec![0; GlobalHistogramBinarizer::LUMINANCE_BUCKETS],
width: source.getWidth(),
height: source.getHeight(),
source: source,
}
}
// fn initArrays(&mut self, luminanceSize: usize) {
// // if self.luminances.len() < luminanceSize {
// // self.luminances = ;
@@ -181,7 +192,7 @@ impl GlobalHistogramBinarizer {
// // }
// }
fn estimateBlackPoint(&self, buckets: &[u32]) -> Result<u32, Exceptions> {
fn estimateBlackPoint(buckets: &[u32]) -> Result<u32, Exceptions> {
// Find the tallest peak in the histogram.
let numBuckets = buckets.len();
let mut maxBucketCount = 0;

View File

@@ -20,7 +20,7 @@
// import com.google.zxing.LuminanceSource;
// import com.google.zxing.NotFoundException;
use std::rc::Rc;
use std::{cell::RefCell, rc::Rc};
use crate::{Binarizer, Exceptions, LuminanceSource};
@@ -48,15 +48,15 @@ pub struct HybridBinarizer {
//height: usize,
//source: Box<dyn LuminanceSource>,
ghb: GlobalHistogramBinarizer,
// matrix :Option<BitMatrix>,
black_matrix: Option<BitMatrix>,
}
impl Binarizer for HybridBinarizer {
fn getLuminanceSource(&self) -> &Box<dyn LuminanceSource> {
self.ghb.getLuminanceSource()
}
fn getBlackRow(&self, y: usize, row: &mut BitArray) -> Result<BitArray, Exceptions> {
self.ghb.getBlackRow(y, row)
fn getBlackRow(&mut self, y: usize) -> Result<BitArray, Exceptions> {
self.ghb.getBlackRow(y)
}
/**
@@ -64,12 +64,48 @@ impl Binarizer for HybridBinarizer {
* constructor instead, but there are some advantages to doing it lazily, such as making
* profiling easier, and not doing heavy lifting when callers don't expect it.
*/
fn getBlackMatrix(&self) -> Result<BitMatrix, Exceptions> {
// if self.matrix.is_some() {
// return Ok(self.matrix.clone().unwrap())
// }
fn getBlackMatrix(&mut self) -> Result<&BitMatrix, Exceptions> {
if self.black_matrix.is_none() {
self.black_matrix = Some(
Self::calculateBlackMatrix(&mut self.ghb)
.expect("generate black matrix must complete"),
)
}
Ok(self.black_matrix.as_ref().unwrap())
}
fn createBinarizer(&self, source: Box<dyn LuminanceSource>) -> Rc<RefCell<dyn Binarizer>> {
Rc::new(RefCell::new(HybridBinarizer::new(source)))
}
fn getWidth(&self) -> usize {
self.ghb.getWidth()
}
fn getHeight(&self) -> usize {
self.ghb.getHeight()
}
}
impl HybridBinarizer {
// This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels.
// So this is the smallest dimension in each axis we can accept.
const BLOCK_SIZE_POWER: usize = 3;
const BLOCK_SIZE: usize = 1 << HybridBinarizer::BLOCK_SIZE_POWER; // ...0100...00
const BLOCK_SIZE_MASK: usize = HybridBinarizer::BLOCK_SIZE - 1; // ...0011...11
const MINIMUM_DIMENSION: usize = HybridBinarizer::BLOCK_SIZE * 5;
const MIN_DYNAMIC_RANGE: usize = 24;
pub fn new(source: Box<dyn LuminanceSource>) -> Self {
let ghb = GlobalHistogramBinarizer::new(source);
Self {
black_matrix: None,
ghb: ghb,
}
}
fn calculateBlackMatrix(ghb: &mut GlobalHistogramBinarizer) -> Result<BitMatrix, Exceptions> {
let matrix;
let source = self.getLuminanceSource();
let source = ghb.getLuminanceSource();
let width = source.getWidth();
let height = source.getHeight();
if width >= HybridBinarizer::MINIMUM_DIMENSION
@@ -102,41 +138,14 @@ impl Binarizer for HybridBinarizer {
&black_points,
&mut new_matrix,
);
matrix = new_matrix;
matrix = Ok(new_matrix);
} else {
// If the image is too small, fall back to the global histogram approach.
matrix = self.ghb.getBlackMatrix()?;
let m = ghb.getBlackMatrix()?;
matrix = Ok(m.clone());
}
// dbg!(matrix.to_string());
Ok(matrix)
}
fn createBinarizer(&self, source: Box<dyn LuminanceSource>) -> Rc<dyn Binarizer> {
Rc::new(HybridBinarizer::new(source))
}
fn getWidth(&self) -> usize {
self.ghb.getWidth()
}
fn getHeight(&self) -> usize {
self.ghb.getHeight()
}
}
impl HybridBinarizer {
// This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels.
// So this is the smallest dimension in each axis we can accept.
const BLOCK_SIZE_POWER: usize = 3;
const BLOCK_SIZE: usize = 1 << HybridBinarizer::BLOCK_SIZE_POWER; // ...0100...00
const BLOCK_SIZE_MASK: usize = HybridBinarizer::BLOCK_SIZE - 1; // ...0011...11
const MINIMUM_DIMENSION: usize = HybridBinarizer::BLOCK_SIZE * 5;
const MIN_DYNAMIC_RANGE: usize = 24;
pub fn new(source: Box<dyn LuminanceSource>) -> Self {
Self {
ghb: GlobalHistogramBinarizer::new(source),
// matrix: None,
}
matrix
}
/**