use crate::common::{BitMatrix, HybridBinarizer, Result};
use crate::{
Binarizer, BinaryBitmap, DecodeHints, Exceptions, Luma8LuminanceSource, LuminanceSource, Reader,
};
pub const DEFAULT_DOWNSCALE_THRESHHOLD: usize = 500;
pub const DEFAULT_DOWNSCALE_FACTOR: usize = 3;
pub struct FilteredImageReader<R: Reader>(R);
impl<R: Reader> FilteredImageReader<R> {
pub fn new(reader: R) -> Self {
Self(reader)
}
}
impl<R: Reader> Reader for FilteredImageReader<R> {
fn decode<B: crate::Binarizer>(
&mut self,
image: &mut crate::BinaryBitmap<B>,
) -> crate::common::Result<crate::RXingResult> {
self.decode_with_hints(image, &DecodeHints::default())
}
fn decode_with_hints<B: crate::Binarizer>(
&mut self,
image: &mut crate::BinaryBitmap<B>,
hints: &DecodeHints,
) -> crate::common::Result<crate::RXingResult> {
let pyramids = LumImagePyramid::new(
Luma8LuminanceSource::new(
image.get_source().get_matrix(),
image.get_source().get_width() as u32,
image.get_source().get_height() as u32,
),
DEFAULT_DOWNSCALE_THRESHHOLD,
DEFAULT_DOWNSCALE_FACTOR,
)
.ok_or(Exceptions::ILLEGAL_ARGUMENT)?;
for layer in pyramids.layers {
let mut b = BinaryBitmap::new(HybridBinarizer::new(layer));
for close in [false, true] {
if close {
let Ok(_) = b.close() else {
continue;
};
}
if let Ok(mut res) = self.0.decode_with_hints(&mut b, hints) {
res.putMetadata(
crate::RXingResultMetadataType::FILTERED_CLOSED,
crate::RXingResultMetadataValue::FilteredClosed(close),
);
let resolution = (b.get_width(), b.get_height());
res.putMetadata(
crate::RXingResultMetadataType::FILTERED_RESOLUTION,
crate::RXingResultMetadataValue::FilteredResolution(resolution),
);
return Ok(res);
} else {
continue;
}
}
}
Err(Exceptions::NOT_FOUND)
}
}
#[derive(Debug, Clone, Default)]
struct LumImagePyramid {
pub layers: Vec<Luma8LuminanceSource>,
}
impl LumImagePyramid {
pub fn new(image: Luma8LuminanceSource, threshold: usize, factor: usize) -> Option<Self> {
let mut new_self = Self::default();
new_self.layers.push(image);
while threshold > 0
&& std::cmp::max(
new_self.layers.last()?.get_width(),
new_self.layers.last()?.get_height(),
) > threshold
&& std::cmp::min(
new_self.layers.last()?.get_width(),
new_self.layers.last()?.get_height(),
) >= factor
{
new_self.add_layer_with_factor(factor).ok()?;
}
#[cfg(feature = "reverse_pyramid_layers")]
new_self.layers.reverse();
Some(new_self)
}
fn add_layer<const N: usize>(&mut self) -> Result<()> {
let siv = self.layers.last().ok_or(Exceptions::ILLEGAL_ARGUMENT)?;
let mut div =
Luma8LuminanceSource::with_empty_image(siv.get_width() / N, siv.get_height() / N);
let div_height = div.get_height();
let div_width = div.get_width();
let mut d_vec_it = div.get_matrix_mut().iter_mut();
'main: for dy in 0..div_height {
for dx in 0..div_width {
let mut sum = (N * N) / 2;
for ty in 0..N {
for tx in 0..N {
sum += siv.get_luma8_point(dx * N + tx, dy * N + ty) as usize;
}
}
if let Some(d) = d_vec_it.next() {
*d = (sum / (N * N)) as u8;
} else {
break 'main;
}
}
}
self.layers.push(div);
Ok(())
}
fn add_layer_with_factor(&mut self, factor: usize) -> Result<()> {
match factor {
2 => self.add_layer::<2>(),
3 => self.add_layer::<3>(),
4 => self.add_layer::<4>(),
_ => Err(Exceptions::illegal_argument_with(
"Invalid ReaderOptions::downscaleFactor",
)),
}
}
}
impl<B: Binarizer> BinaryBitmap<B> {
pub fn close(&mut self) -> Result<()> {
if let Some(matrix) = self.matrix.get_mut() {
let mut tmp = BitMatrix::new(matrix.width(), matrix.height())?;
SumFilter(matrix, &mut tmp, |sum| sum > 0);
SumFilter(&tmp, matrix, |sum| sum == 9);
}
Ok(())
}
}
fn SumFilter<F>(input: &BitMatrix, output: &mut BitMatrix, func: F)
where
F: Fn(u8) -> bool,
{
for row in 1..output.height() - 1 {
for col in 0..output.width() - 1 {
let mut sum = 0;
for j in 0..3 {
sum += input.try_get(col + j, row - 1).unwrap_or_default() as u8
+ input.try_get(col + j, row).unwrap_or_default() as u8
+ input.try_get(col + j, row + 1).unwrap_or_default() as u8;
}
output.set_bool(col, row, func(sum));
}
}
}