convolve-image 0.4.0

A small library to perform convolution operations on images with arbitrarily-sized separable kernels
Documentation
#[cfg(feature = "image")]
use std::cmp::Ordering;
#[cfg(feature = "image")]
use image::{DynamicImage, Rgb};

#[cfg(feature = "ndarray")]
use ndarray::{Array2, Array3};
#[cfg(feature = "ndarray")]
use crate::ndarray::Aggregate;

#[derive(Copy, Clone)]
pub enum RescaleRange {
    Custom(f32, f32),
    Max,
}

impl RescaleRange {
    fn min(self) -> f32 {
        match self {
            RescaleRange::Custom(min, _) => min,
            RescaleRange::Max => 0.
        }
    }

    fn max(self) -> f32 {
        match self {
            RescaleRange::Custom(_, max) => max,
            RescaleRange::Max => 1.
        }
    }
}

pub trait Rescale {
    fn min(&self) -> f32;
    fn max(&self) -> f32;
    fn rescale(&mut self, range: RescaleRange);
    fn channel_wise_rescale(&mut self, range: RescaleRange);
    fn rescale_value(min: f32, max: f32, value: f32, range: RescaleRange) -> f32 {
        let (new_min, new_max) = (range.min(), range.max());
        let new_range = new_max - new_min;

        new_min + ((value - min) * new_range / (max - min))
    }
}

#[cfg(feature = "image")]
impl Rescale for DynamicImage {
    fn min(&self) -> f32 {
        self
            .to_rgb32f()
            .iter()
            .min_by(|x, y| {
                if x > y {
                    Ordering::Greater
                } else if y > x {
                    Ordering::Less
                } else {
                    Ordering::Equal
                }
            })
            .copied()
            .unwrap_or(0.)
            .min(0.)
    }

    fn max(&self) -> f32 {
        self
            .to_rgb32f()
            .iter()
            .max_by(|x, y| {
                if x > y {
                    Ordering::Greater
                } else if y > x {
                    Ordering::Less
                } else {
                    Ordering::Equal
                }
            })
            .copied()
            .unwrap_or(1.)
            .max(1.)
    }

    fn rescale(&mut self, range: RescaleRange) {
        let min = self.min();
        let max = self.max();

        for pixel in self.to_rgb32f().pixels_mut() {
            let [r, g, b] = pixel.0;
            *pixel = Rgb([Self::rescale_value(min, max, r, range), Self::rescale_value(min, max, g, range), Self::rescale_value(min, max, b, range)]);
        }
    }

    fn channel_wise_rescale(&mut self, range: RescaleRange) {
        let mut max_r: f32 = 1.;
        let mut max_g: f32 = 1.;
        let mut max_b: f32 = 1.;

        let mut min_r: f32 = 0.;
        let mut min_g: f32 = 0.;
        let mut min_b: f32 = 0.;

        let mut source = self.to_rgb32f();

        for pixel in source.pixels() {
            let [r, g, b] = pixel.0;
            max_r = max_r.max(r);
            max_g = max_g.max(g);
            max_b = max_b.max(b);

            min_r = min_r.min(r);
            min_g = min_g.min(g);
            min_b = min_b.min(b);
        }

        for pixel in source.pixels_mut() {
            let [r, g, b] = pixel.0;
            *pixel = Rgb([
                Self::rescale_value(min_r, max_r, r, range),
                Self::rescale_value(min_g, max_g, g, range),
                Self::rescale_value(min_b, max_b, b, range),
            ]);
        }
    }
}

#[cfg(feature = "ndarray")]
impl Rescale for Array2<f32> {
    fn min(&self) -> f32 {
        Aggregate::min(self)
    }

    fn max(&self) -> f32 {
        Aggregate::max(self)
    }

    fn rescale(&mut self, range: RescaleRange) {
        let min = Rescale::min(self);
        let max = Rescale::max(self);

        for item in self.iter_mut() {
            *item = Self::rescale_value(min, max, *item, range);
        }
    }

    fn channel_wise_rescale(&mut self, range: RescaleRange) {
        self.rescale(range)
    }
}

#[cfg(feature = "ndarray")]
impl Rescale for Array3<f32> {
    fn min(&self) -> f32 {
        Aggregate::min(self)
    }

    fn max(&self) -> f32 {
        Aggregate::max(self)
    }

    fn rescale(&mut self, range: RescaleRange) {
        let min = Rescale::min(self);
        let max = Rescale::max(self);

        for item in self.iter_mut() {
            *item = Self::rescale_value(min, max, *item, range);
        }
    }

    fn channel_wise_rescale(&mut self, range: RescaleRange) {
        let mut max_r: f32 = 1.;
        let mut max_g: f32 = 1.;
        let mut max_b: f32 = 1.;

        let mut min_r: f32 = 0.;
        let mut min_g: f32 = 0.;
        let mut min_b: f32 = 0.;

        for ((_, _, channel), item) in self.indexed_iter() {
            match channel {
                0 => {
                    min_r = min_r.min(*item);
                    max_r = max_r.max(*item);
                }
                1 => {
                    min_g = min_g.min(*item);
                    max_g = max_g.max(*item);
                }
                2 => {
                    min_b = min_b.min(*item);
                    max_b = max_b.max(*item);
                }
                _ => panic!("Unexpected number of channels")
            };
        }

        for ((_, _, channel), item) in self.indexed_iter_mut() {
            match channel {
                0 => {
                    *item = Self::rescale_value(min_r, max_r, *item, range);
                }
                1 => {
                    *item = Self::rescale_value(min_g, max_g, *item, range);
                }
                2 => {
                    *item = Self::rescale_value(min_b, max_b, *item, range);
                }
                _ => panic!("Unexpected number of channels")
            };
        }
    }
}