use std::cell::{BorrowError, Ref, RefCell};
use std::usize;
use zune_core::bit_depth::BitType;
use zune_image::errors::ImageErrors;
use zune_image::image::Image;
use zune_image::traits::OperationsTrait;
#[derive(Default)]
pub struct ChannelHistogram {
histogram: RefCell<Vec<Vec<u32>>>
}
impl ChannelHistogram {
#[must_use]
pub fn new() -> ChannelHistogram {
ChannelHistogram::default()
}
pub fn histogram(&self) -> Result<Ref<'_, Vec<Vec<u32>>>, BorrowError> {
self.histogram.try_borrow()
}
}
impl OperationsTrait for ChannelHistogram {
fn name(&self) -> &'static str {
"Channel Histogram"
}
fn execute_impl(&self, image: &mut Image) -> Result<(), ImageErrors> {
let depth = image.depth().bit_type();
self.histogram.borrow_mut().clear();
match depth {
BitType::U8 => {
for channel in image.channels_ref(false) {
let pixels = channel.reinterpret_as::<u8>()?;
let histo = histogram(pixels);
self.histogram.borrow_mut().push(histo.to_vec());
}
}
BitType::U16 => {
for channel in image.channels_ref(false) {
let pixels = channel.reinterpret_as::<u16>()?;
let histo = histogram_u16(pixels);
self.histogram.borrow_mut().push(histo.clone());
}
}
_ => {
return Err(ImageErrors::GenericStr(
"Histogram isn't implemented for f32 images"
))
}
}
Ok(())
}
fn supported_types(&self) -> &'static [BitType] {
&[BitType::U8, BitType::U16]
}
}
#[must_use]
pub fn histogram(data: &[u8]) -> [u32; 256] {
let mut start1 = [0; 256];
let mut counts = [0_u32; 256 * 3];
let (start2, counts) = counts.split_at_mut(256);
let (start3, start4) = counts.split_at_mut(256);
let chunks = data.chunks_exact(8);
let remainder = chunks.remainder();
for i in chunks {
let tmp1 = u64::from_le_bytes(i[0..8].try_into().unwrap());
start1[((tmp1 >> 56) & 255) as usize] += 1;
start2[((tmp1 >> 48) & 255) as usize] += 1;
start3[((tmp1 >> 40) & 255) as usize] += 1;
start4[((tmp1 >> 32) & 255) as usize] += 1;
start1[((tmp1 >> 24) & 255) as usize] += 1;
start2[((tmp1 >> 16) & 255) as usize] += 1;
start3[((tmp1 >> 8) & 255) as usize] += 1;
start4[(tmp1 & 255) as usize] += 1;
}
for i in remainder {
start1[usize::from(*i)] += 1;
}
for (((b, c), d), e) in start1
.iter_mut()
.zip(start2.iter())
.zip(start3.iter())
.zip(start4.iter())
{
*b += c + d + e;
}
start1
}
fn histogram_u16(data: &[u16]) -> Vec<u32> {
let mut size = vec![0; usize::from(u16::MAX) + 1];
let size_arr: &mut [u32; { u16::MAX as usize } + 1] =
size.get_mut(..).unwrap().try_into().unwrap();
let chunks = data.chunks_exact(4);
let remainder = chunks.remainder();
for i in chunks {
size_arr[usize::from(i[0])] += 1;
size_arr[usize::from(i[1])] += 1;
size_arr[usize::from(i[2])] += 1;
size_arr[usize::from(i[3])] += 1;
}
for i in remainder {
size_arr[usize::from(*i)] += 1;
}
size
}
#[test]
fn test_histogram_u8() {
use nanorand::Rng;
use zune_core::colorspace::ColorSpace;
let (w, h) = (400, 400);
let mut pixels = vec![0_u8; w * h];
nanorand::WyRand::new().fill(&mut pixels);
let mut image = Image::from_u8(&pixels, w, h, ColorSpace::Luma);
let histo = ChannelHistogram::new();
histo.execute_impl(&mut image).unwrap();
let data = histo.histogram().expect("Reference is borrowed");
assert_eq!(data.len(), 1);
assert_eq!(
data[0].iter().sum::<u32>(),
u32::try_from(pixels.len()).unwrap_or(0)
);
}
#[test]
fn test_histogram_u16() {
use nanorand::Rng;
use zune_core::colorspace::ColorSpace;
let (w, h) = (400, 400);
let mut pixels = vec![0_u16; w * h];
nanorand::WyRand::new().fill(&mut pixels);
let mut image = Image::from_u16(&pixels, w, h, ColorSpace::Luma);
let histo = ChannelHistogram::new();
histo.execute_impl(&mut image).unwrap();
let data = histo.histogram().expect("Reference is borrowed");
assert_eq!(
data[0].iter().sum::<u32>(),
u32::try_from(pixels.len()).unwrap_or(0)
);
}
#[cfg(feature = "benchmarks")]
#[cfg(test)]
mod benchmarks {
extern crate test;
use nanorand::Rng;
use crate::histogram::{histogram, histogram_u16};
#[bench]
fn bench_histogram_u8(b: &mut test::Bencher) {
let width = 800;
let height = 800;
let dimensions = width * height;
let mut in_vec = vec![255_u8; dimensions];
nanorand::WyRand::new().fill(&mut in_vec);
b.iter(|| histogram(&in_vec));
}
#[bench]
fn bench_histogram_u16(b: &mut test::Bencher) {
let width = 800;
let height = 800;
let dimensions = width * height;
let mut in_vec = vec![0_u16; dimensions];
nanorand::WyRand::new().fill(&mut in_vec);
b.iter(|| histogram_u16(&in_vec));
}
}