image-compare 0.5.0

Image comparison library based upon the image crate. Currently it provides SSIM and RMS for comparing grayscale and rgb images, a cool hybrid compare as well as several grayscale histogram distance metrics. All with a friendly license.
Documentation
use crate::prelude::*;
use itertools::izip;

pub(crate) fn root_mean_squared_error_simple(
    first: &GrayImage,
    second: &GrayImage,
) -> Result<(f64, GraySimilarityImage), CompareError> {
    let dimension = first.dimensions();
    let mut image = GraySimilarityImage::new(dimension.0, dimension.1);
    let iter = izip!(first.pixels(), second.pixels(), image.pixels_mut());

    iter.for_each(|(a, b, c)| {
        let diff = a[0] as i32 - b[0] as i32;
        let normalized = diff as f32 / u8::MAX as f32;
        let squared_root = 1. - normalized.abs();
        *c = Luma([squared_root]);
    });

    let score: f64 = 1.
        - (image
            .pixels()
            .map(|p| (1. - p[0] as f64).powi(2))
            .sum::<f64>()
            / (image.pixels().len() as f64))
            .sqrt();
    Ok((score, image))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn rms_simple_10() {
        let mut first = GrayImage::new(1, 1);
        let mut second = GrayImage::new(1, 1);

        first.fill(0);
        second.fill(10);
        let (_, comparison) =
            root_mean_squared_error_simple(&first, &second).expect("Do not expect error here");
        assert_eq!(comparison.get_pixel(0, 0)[0], 1. - (10. / (255.0f32)));
    }

    #[test]
    fn rms_simple_identity() {
        let mut first = GrayImage::new(1, 1);
        let mut second = GrayImage::new(1, 1);

        first.fill(0);
        second.fill(0);
        let (score, comparison) =
            root_mean_squared_error_simple(&first, &second).expect("Do not expect error here");
        assert_eq!(comparison.get_pixel(0, 0)[0], 1.);
        assert_eq!(score, 1.);
    }

    #[test]
    fn rms_simple_max() {
        let mut first = GrayImage::new(1, 1);
        let mut second = GrayImage::new(1, 1);

        first.fill(0);
        second.fill(255);
        let (score, comparison) =
            root_mean_squared_error_simple(&first, &second).expect("Do not expect error here");
        assert_eq!(comparison.get_pixel(0, 0)[0], 0.);
        assert_eq!(score, 0.);
    }

    #[test]
    fn rms_simple() {
        let width = 3;
        let height = 2;
        let mut first = GrayImage::new(width, height);
        let mut second = GrayImage::new(width, height);

        first.fill(0);
        second.fill(0);
        second.put_pixel(1, 1, Luma([127]));

        let (comparison, _) =
            root_mean_squared_error_simple(&first, &second).expect("Do not expect error here");

        let result = 1. - ((127. / 255.0f64).powi(2) / (width * height) as f64).sqrt();
        assert!((comparison - result).abs() < 1e-5);
    }
}