mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-26 20:32:34 +00:00
update for shared state and improved performance
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user