use std::borrow::Cow;
use image::{DynamicImage, ImageBuffer, Luma};
use once_cell::sync::OnceCell;
use crate::{Binarizer, Exceptions, LuminanceSource};
use super::{BitArray, BitMatrix, Result};
pub struct AdaptiveThresholdBinarizer<LS: LuminanceSource> {
source: LS,
matrix: OnceCell<BitMatrix>,
radius: u32,
}
impl<LS: LuminanceSource> AdaptiveThresholdBinarizer<LS> {
pub const fn new(source: LS, radius: u32) -> Self {
Self {
source,
radius,
matrix: OnceCell::new(),
}
}
}
impl<LS: LuminanceSource> AdaptiveThresholdBinarizer<LS> {
fn buildMatrix(&self) -> Result<BitMatrix> {
let image_buffer = {
let Some(buff): Option<ImageBuffer<Luma<u8>, Vec<u8>>> = ImageBuffer::from_vec(
self.source.get_width() as u32,
self.source.get_height() as u32,
self.source.get_matrix(),
) else {
return Err(Exceptions::ILLEGAL_ARGUMENT);
};
buff
};
let filtered_iamge = imageproc::contrast::adaptive_threshold(&image_buffer, self.radius);
let dynamic_filtered = DynamicImage::from(filtered_iamge);
dynamic_filtered.try_into()
}
}
impl<LS: LuminanceSource> Binarizer for AdaptiveThresholdBinarizer<LS> {
type Source = LS;
fn get_luminance_source(&self) -> &Self::Source {
&self.source
}
fn get_black_row(&self, y: usize) -> Result<Cow<'_, BitArray>> {
let matrix = self.matrix.get_or_try_init(|| self.buildMatrix())?;
Ok(Cow::Owned(matrix.getRow(y as u32)))
}
fn get_black_matrix(&self) -> super::Result<&BitMatrix> {
self.matrix.get_or_try_init(|| self.buildMatrix())
}
fn get_black_line(&self, l: usize, lt: super::LineOrientation) -> Result<Cow<'_, BitArray>> {
match lt {
super::LineOrientation::Row => self.get_black_row(l),
super::LineOrientation::Column => {
let matrix = self.matrix.get_or_try_init(|| self.buildMatrix())?;
Ok(Cow::Owned(matrix.getCol(l as u32)))
}
}
}
fn create_binarizer(&self, source: Self::Source) -> Self
where
Self: Sized,
{
Self {
source,
matrix: OnceCell::new(),
radius: self.radius,
}
}
fn get_width(&self) -> usize {
self.source.get_width()
}
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)
}
}
}