From 66543f9098888dfd743190132936910e36e55f8f Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Thu, 2 Feb 2023 18:57:54 -0600 Subject: [PATCH 1/2] basic working otsu number binarizer --- Cargo.toml | 5 +- src/common/bit_matrix.rs | 16 ++++++ src/common/mod.rs | 5 ++ src/common/otsu_level_binarizer.rs | 85 ++++++++++++++++++++++++++++++ src/luminance_source.rs | 2 + 5 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 src/common/otsu_level_binarizer.rs diff --git a/Cargo.toml b/Cargo.toml index 9a9fd08..067fe16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,4 +59,7 @@ wasm_support = ["chrono/wasmbind"] experimental_features = [] #/// Adds support for serde Serialize and Deserialize for outward facing structs -serde = ["dep:serde"] \ No newline at end of file +serde = ["dep:serde"] + +#/// Adds otsu binarizer support using imageproc +otsu_level = ["image"] \ No newline at end of file diff --git a/src/common/bit_matrix.rs b/src/common/bit_matrix.rs index 7b32ea1..b311128 100644 --- a/src/common/bit_matrix.rs +++ b/src/common/bit_matrix.rs @@ -709,6 +709,22 @@ impl fmt::Display for BitMatrix { } } +#[cfg(feature = "image")] +/// This should only be used if you *know* that the `DynamicImage` is binary. +impl TryFrom for BitMatrix { + type Error = Exceptions; + + fn try_from(value: image::DynamicImage) -> Result { + let mut new_bitmatrix = BitMatrix::new(value.width(), value.height())?; + for (x, y, value) in value.as_luma8().unwrap().enumerate_pixels() { + if value.0[0] != u8::MAX { + new_bitmatrix.set(x, y) + } + } + Ok(new_bitmatrix) + } +} + #[cfg(feature = "image")] impl From for image::DynamicImage { fn from(value: BitMatrix) -> Self { diff --git a/src/common/mod.rs b/src/common/mod.rs index b77b76e..ae20d58 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -103,3 +103,8 @@ pub use global_histogram_binarizer::*; mod hybrid_binarizer; pub use hybrid_binarizer::*; + +#[cfg(feature = "otsu_level")] +mod otsu_level_binarizer; +#[cfg(feature = "otsu_level")] +pub use otsu_level_binarizer::*; diff --git a/src/common/otsu_level_binarizer.rs b/src/common/otsu_level_binarizer.rs new file mode 100644 index 0000000..8a08af3 --- /dev/null +++ b/src/common/otsu_level_binarizer.rs @@ -0,0 +1,85 @@ +use std::{borrow::Cow, rc::Rc}; + +use image::{DynamicImage, ImageBuffer, Luma}; +use once_cell::sync::OnceCell; + +use crate::{Binarizer, Exceptions, LuminanceSource}; + +use super::{BitArray, BitMatrix}; + +pub struct OtsuLevelBinarizer { + width: usize, + height: usize, + source: Box, + black_matrix: OnceCell, + black_row_cache: Vec>, +} + +impl OtsuLevelBinarizer { + fn generate_threshold_matrix(source: &dyn LuminanceSource) -> Result { + let image_buffer = { + let Some(buff) : Option,Vec>> = ImageBuffer::from_vec(source.getWidth() as u32, source.getHeight() as u32, source.getMatrix()) else { + return Err(Exceptions::IllegalArgumentException(None)) + }; + buff + }; + + let otsu_level = imageproc::contrast::otsu_level(&image_buffer); + + let filtered_iamge = imageproc::contrast::threshold(&image_buffer, otsu_level); + + let dynamic_filtered = DynamicImage::from(filtered_iamge); + + dynamic_filtered.try_into() + } + + pub fn new(source: Box) -> Self { + Self { + width: source.getWidth(), + height: source.getHeight(), + black_row_cache: vec![OnceCell::default(); source.getHeight() as usize], + source, + black_matrix: OnceCell::new(), + } + } +} + +impl Binarizer for OtsuLevelBinarizer { + fn getLuminanceSource(&self) -> &Box { + &self.source + } + + fn getBlackRow( + &self, + y: usize, + ) -> Result, crate::Exceptions> { + let row = self.black_row_cache[y].get_or_try_init(|| { + let matrix = self.getBlackMatrix()?; + Ok(matrix.getRow(y as u32)) + })?; + + Ok(Cow::Borrowed(row)) + } + + fn getBlackMatrix(&self) -> Result<&super::BitMatrix, crate::Exceptions> { + let matrix = self + .black_matrix + .get_or_try_init(|| Self::generate_threshold_matrix(self.source.as_ref()))?; + Ok(matrix) + } + + fn createBinarizer( + &self, + source: Box, + ) -> std::rc::Rc { + Rc::new(Self::new(source)) + } + + fn getWidth(&self) -> usize { + self.width + } + + fn getHeight(&self) -> usize { + self.height + } +} diff --git a/src/luminance_source.rs b/src/luminance_source.rs index acf1896..9ac2ffa 100644 --- a/src/luminance_source.rs +++ b/src/luminance_source.rs @@ -16,6 +16,8 @@ //package com.google.zxing; +use std::any::Any; + use crate::Exceptions; /** From 01623ead6d1a52155e8ea7cb2e8b8bceb859ce58 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Thu, 2 Feb 2023 18:59:04 -0600 Subject: [PATCH 2/2] clippy --fix --- src/common/otsu_level_binarizer.rs | 2 +- src/luminance_source.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/otsu_level_binarizer.rs b/src/common/otsu_level_binarizer.rs index 8a08af3..b6ac08c 100644 --- a/src/common/otsu_level_binarizer.rs +++ b/src/common/otsu_level_binarizer.rs @@ -37,7 +37,7 @@ impl OtsuLevelBinarizer { Self { width: source.getWidth(), height: source.getHeight(), - black_row_cache: vec![OnceCell::default(); source.getHeight() as usize], + black_row_cache: vec![OnceCell::default(); source.getHeight()], source, black_matrix: OnceCell::new(), } diff --git a/src/luminance_source.rs b/src/luminance_source.rs index 9ac2ffa..458a097 100644 --- a/src/luminance_source.rs +++ b/src/luminance_source.rs @@ -16,7 +16,7 @@ //package com.google.zxing; -use std::any::Any; + use crate::Exceptions;