use super::Prepare;
use image::{GenericImageView, GrayImage, Pixel, Rgba};
use image::imageops::grayscale;
use std::cmp::{max, min};
pub struct BlockedMean {
block_size: BlockSize,
block_mean_size: BlockSize,
}
impl BlockedMean {
pub fn new(block_size: u32, block_mean_size: u32) -> BlockedMean {
BlockedMean {
block_size: BlockSize(block_size),
block_mean_size: BlockSize(block_mean_size),
}
}
}
impl <D> Prepare<D, GrayImage> for BlockedMean where D: GenericImageView<Pixel = Rgba<u8>> {
fn prepare(&self, input: &D) -> GrayImage {
let grayscale = grayscale(input);
let dimensions = grayscale.dimensions();
let width = ImageCoord(dimensions.0);
let height = ImageCoord(dimensions.1);
let block_map = self.as_block_map(&grayscale, width, height);
let block_mean_map = self.to_block_mean_map(&block_map, width, height);
self.to_threshold(grayscale, &block_mean_map, width, height)
}
}
impl BlockedMean {
fn as_block_map(
&self,
grayscale: &GrayImage,
width: ImageCoord,
height: ImageCoord,
) -> Vec<Stats> {
let (block_width, block_height) = as_block_coords(width, height, self.block_size);
let mut blocks = vec![
Stats {
total: 0,
count: 0,
mean: 0.0
};
((block_width.0 + 1) * (block_height.0 + 1)) as usize
];
for (x, y, p) in grayscale.enumerate_pixels() {
let coords = as_block_coords(ImageCoord(x), ImageCoord(y), self.block_size);
let stats = &mut blocks[to_index(coords, block_width)];
stats.total += u64::from(p.channels()[0]);
stats.count += 1;
}
for stat in &mut blocks {
stat.mean = stat.total as f64 / stat.count as f64;
}
blocks
}
fn to_block_mean_map(
&self,
blocks: &[Stats],
width: ImageCoord,
height: ImageCoord,
) -> Vec<Stats> {
let block_stride = BlockCoord((self.block_mean_size.0 - 1) / 2);
let (block_width, block_height) = as_block_coords(width, height, self.block_size);
let mut block_means = vec![
Stats {
total: 0,
count: 0,
mean: 0.0
};
((block_width + BlockCoord(1)) * (block_height + BlockCoord(1))).0
as usize
];
for block_x in range_inc(BlockCoord(0), block_width) {
for block_y in range_inc(BlockCoord(0), block_height) {
let x_start = max(BlockCoord(0), block_x.saturating_sub(block_stride));
let x_end = min(block_width, block_x + block_stride);
let y_start = max(BlockCoord(0), block_y.saturating_sub(block_stride));
let y_end = min(block_height, block_y + block_stride);
let mut total = 0;
let mut count = 0;
for x in range(x_start, x_end) {
for y in range(y_start, y_end) {
let stats = &blocks[to_index((x, y), block_width)];
total += stats.total;
count += stats.count;
}
}
block_means[to_index((block_x, block_y), block_width)].mean =
total as f64 / count as f64;
}
}
block_means
}
fn to_threshold(
&self,
mut grayscale: GrayImage,
block_means: &[Stats],
width: ImageCoord,
height: ImageCoord,
) -> GrayImage {
for (x, y, p) in grayscale.enumerate_pixels_mut() {
let (block_width, _) = as_block_coords(width, height, self.block_size);
let coords = as_block_coords(ImageCoord(x), ImageCoord(y), self.block_size);
let mean = block_means[to_index(coords, block_width)].mean;
p.channels_mut()[0] = if mean > 250.0 {
255
} else if mean < 5.0 {
0
} else if f64::from(p.channels()[0]) > mean {
255
} else {
0
};
}
grayscale
}
}
#[derive(Debug, Copy, Clone)]
struct Stats {
total: u64,
count: u64,
mean: f64,
}
#[inline]
fn to_index(coords: (BlockCoord, BlockCoord), width: BlockCoord) -> usize {
((coords.1).0 * (width.0 + 1) + (coords.0).0) as usize
}
#[inline]
fn as_block_coords(
x: ImageCoord,
y: ImageCoord,
block_size: BlockSize,
) -> (BlockCoord, BlockCoord) {
let x = x / block_size;
let y = y / block_size;
(x, y)
}
#[derive(Copy, Clone, Debug)]
struct ImageCoord(u32);
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Debug)]
struct BlockCoord(u32);
NewtypeAdd! { () struct BlockCoord(u32); }
NewtypeMul! { () struct BlockCoord(u32); }
#[derive(Copy, Clone, Debug)]
struct BlockSize(u32);
use std::ops::{Div, Mul};
impl Mul<BlockSize> for BlockCoord {
type Output = ImageCoord;
fn mul(self, other: BlockSize) -> ImageCoord {
ImageCoord(self.0 * other.0)
}
}
impl Div<BlockSize> for ImageCoord {
type Output = BlockCoord;
fn div(self, other: BlockSize) -> BlockCoord {
BlockCoord(self.0 / other.0)
}
}
impl BlockCoord {
fn saturating_sub(self, other: BlockCoord) -> BlockCoord {
BlockCoord(self.0.saturating_sub(other.0))
}
}
struct BlockCoordRangeInclusive {
current: u32,
end: u32,
}
impl Iterator for BlockCoordRangeInclusive {
type Item = BlockCoord;
fn next(&mut self) -> Option<BlockCoord> {
if self.current > self.end {
None
} else {
let result = Some(BlockCoord(self.current));
self.current += 1;
result
}
}
}
fn range(start: BlockCoord, end: BlockCoord) -> BlockCoordRangeInclusive {
BlockCoordRangeInclusive {
current: start.0,
end: end.0 - 1,
}
}
fn range_inc(start: BlockCoord, end: BlockCoord) -> BlockCoordRangeInclusive {
BlockCoordRangeInclusive {
current: start.0,
end: end.0,
}
}