histogram_equalization 0.2.4

Histogram equalization
Documentation
use num_traits::FromPrimitive;

#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub(crate) enum AheImplementation {
    Ahe = 1,
    Clahe = 2,
}

impl From<u8> for AheImplementation {
    fn from(value: u8) -> Self {
        match value {
            1 => AheImplementation::Ahe,
            2 => AheImplementation::Clahe,
            _ => {
                panic!("{} not implemented in AHE", value)
            }
        }
    }
}

#[inline]
pub(crate) fn lerp(a: f32, b: f32, f: f32) -> f32 {
    a * (1f32 - f) + (b * f)
}

#[inline]
pub(crate) fn blerp(c00: f32, c10: f32, c01: f32, c11: f32, tx: f32, ty: f32) -> f32 {
    lerp(lerp(c00, c10, tx), lerp(c01, c11, tx), ty)
}

#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct ClaheGridSize {
    pub w: u32,
    pub h: u32,
}

impl ClaheGridSize {
    pub fn new(w: u32, h: u32) -> ClaheGridSize {
        ClaheGridSize { w, h }
    }
}

#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub struct ImageHistogram {
    pub bins: Vec<u64>,
}

pub(crate) fn cdf(arr: &mut [u64]) {
    let mut sum: u64 = 0u64;

    for value in arr.iter_mut() {
        sum += *value;
        *value = sum;
    }
}

pub(crate) fn clip_hist_clahe(bins: &mut [u64], level: f32, width: usize, height: usize) {
    let threshold_value: f64 = level as f64 * width as f64 * height as f64 / bins.len() as f64;
    let clip_limit = threshold_value as u64;
    let mut excess = 0u64;

    for i in 0..bins.len() {
        let value = unsafe { *bins.get_unchecked(i) };
        if value > clip_limit {
            excess += value;
            unsafe {
                *bins.get_unchecked_mut(i) = clip_limit;
            }
        }
    }

    let mean_excess = (excess as f64 / bins.len() as f64) as u64;

    for i in 0..bins.len() {
        unsafe {
            *bins.get_unchecked_mut(i) = *bins.get_unchecked(i) + mean_excess;
        }
    }
}

pub(crate) fn minmax(slice: &[u64]) -> (u64, u64) {
    let mut min_value = u64::MAX;
    let mut max_value = u64::MIN;
    let mut i = 0usize;
    let count = slice.len();

    while i < count {
        let value = unsafe { *slice.get_unchecked(i) };
        if value < min_value {
            min_value = value;
        }
        if value > max_value {
            max_value = value;
        }
        i += 1;
    }
    (min_value, max_value)
}

pub(crate) fn make_histogram_region<
    const CHANNEL: usize,
    const CHANNELS: usize,
    T: Copy + Ord + FromPrimitive + Default + Into<usize>,
>(
    in_place: &[T],
    stride: u32,
    start_x: u32,
    end_x: u32,
    start_y: u32,
    end_y: u32,
    bins_count: usize,
) -> ImageHistogram {
    let mut bins = vec![0u64; bins_count];
    let mut y_shift = (stride * start_y) as usize;

    let min_v = T::from_u16(bins_count as u16 - 1).unwrap_or_default();

    for _ in start_y as usize..end_y as usize {
        for x in start_x as usize..end_x as usize {
            let px = x * CHANNELS;
            let value: T = unsafe { *in_place.get_unchecked(y_shift + px + CHANNEL) }
                .min(min_v)
                .max(T::from_u16(0u16).unwrap_or_default());
            let iter: usize = value.into();
            unsafe {
                *bins.get_unchecked_mut(iter) += 1u64;
            }
        }
        y_shift += stride as usize;
    }

    ImageHistogram { bins }
}