diff --git a/Cargo.toml b/Cargo.toml index f7e8d75..4972267 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,8 @@ version = "0.1.0" authors = ["Thomas Forgione "] [dependencies] -image = "*" -lazy_static = "*" +image = "0.17.0" +lazy_static = "0.2.11" [[bin]] name = "psnr" diff --git a/src/lib.rs b/src/lib.rs index e26c913..04cc55f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ extern crate lazy_static; extern crate image; -use image::{Pixel, GenericImage, ImageResult, Rgba}; +use image::{Pixel, GenericImage, ImageResult, Rgba, FilterType}; lazy_static! { /// This values is 20 * log10(255) @@ -16,6 +16,94 @@ pub fn mse_color, T: GenericImage>(img: &T, color let mut mse = 0.0; + for x in 0 .. img.dimensions().0 { + for y in 0 .. img.dimensions().1 { + + let p1 = img.get_pixel(x, y).to_rgba(); + + let dx = p1.data[0] as f64 - color.data[0] as f64; + let dy = p1.data[1] as f64 - color.data[1] as f64; + let dz = p1.data[2] as f64 - color.data[2] as f64; + let da = p1.data[3] as f64 - color.data[3] as f64; + + let dx = dx * dx; + let dy = dy * dy; + let dz = dz * dz; + let da = da * da; + + mse += dx + dy + dz + da; + } + } + + mse / (img.dimensions().0 * img.dimensions().1 * 4) as f64 +} + +/// Compute a MSE between two images. +/// +/// Will panic an error if the two images don't have the same size. +pub fn mse, T1: GenericImage, P2: Pixel, T2: GenericImage>(img1: &T1, img2: &T2) -> f64 { + + assert_eq!(img1.dimensions(), img2.dimensions()); + + let mut mse = 0.0; + + for x in 0 .. img1.dimensions().0 { + for y in 0 .. img1.dimensions().1 { + + let p1 = img1.get_pixel(x, y).to_rgba(); + let p2 = img2.get_pixel(x, y).to_rgba(); + + let dx = p1.data[0] as f64 - p2.data[0] as f64; + let dy = p1.data[1] as f64 - p2.data[1] as f64; + let dz = p1.data[2] as f64 - p2.data[2] as f64; + let da = p1.data[3] as f64 - p2.data[3] as f64; + + let dx = dx * dx; + let dy = dy * dy; + let dz = dz * dz; + let da = da * da; + + mse += dx + dy + dz + da; + } + } + + mse / (img1.dimensions().0 * img1.dimensions().1 * 4) as f64 +} + +/// Resizes img1 to the size of img2 and then compute the MSE between the images. +pub fn mse_resized, T: 'static + GenericImage>(img1: &T, img2: &T, filter: FilterType) -> f64 { + + let img1 = image::imageops::resize(img1, img2.width(), img2.height(), filter); + mse(&img1, img2) + +} + +/// Compute the PSNR from the MSE. +/// Returns 10 * log10(255^2) - 10 * log10(mse) +pub fn mse_to_psnr(mse: f64) -> f64 { + *TWENTY_LOG10_MAX - 10.0 * mse.log10() + +} + +/// Computes the PSNR between two images of the same sizes. +pub fn psnr, T: GenericImage>(img1: &T, img2: &T) -> f64 { + *TWENTY_LOG10_MAX - 10.0 * mse(img1, img2).log10() +} + +/// Computes the PSNR between two images of the same size from their paths. +pub fn psnr_files(path1: &str, path2: &str) -> ImageResult { + let img1 = image::open(path1)?; + let img2 = image::open(path2)?; + Ok(psnr(&img1, &img2)) +} + +/// Compute a MSE between an image and its color. +/// +/// Will panic an error if the two images don't have the same size. +pub fn mse_color_without_alpha, T: GenericImage>(img: &T, color: &Rgba) -> f64 { + + let mut mse = 0.0; + for x in 0 .. img.dimensions().0 { for y in 0 .. img.dimensions().1 { @@ -35,10 +123,11 @@ pub fn mse_color, T: GenericImage>(img: &T, color mse / (img.dimensions().0 * img.dimensions().1 * 3) as f64 } + /// Compute a MSE between two images. /// /// Will panic an error if the two images don't have the same size. -pub fn mse, T: GenericImage>(img1: &T, img2: &T) -> f64 { +pub fn mse_without_alpha, T1: GenericImage, P2: Pixel, T2: GenericImage>(img1: &T1, img2: &T2) -> f64 { assert_eq!(img1.dimensions(), img2.dimensions()); @@ -65,19 +154,22 @@ pub fn mse, T: GenericImage>(img1: &T, img2: &T) mse / (img1.dimensions().0 * img1.dimensions().1 * 3) as f64 } -/// Compute the PSNR from the MSE. -/// Returns 10 * log10(255^2) - 10 * log10(mse) -pub fn psnr_from_mse(mse: f64) -> f64 { - *TWENTY_LOG10_MAX - 10.0 * mse.log10() +/// Resizes img1 to the size of img2 and then compute the MSE between the images. +pub fn mse_resized_without_alpha, T: 'static + GenericImage>(img1: &T, img2: &T, filter: FilterType) -> f64 { + + let img1 = image::imageops::resize(img1, img2.width(), img2.height(), filter); + mse_without_alpha(&img1, img2) } -pub fn psnr, T: GenericImage>(img1: &T, img2: &T) -> f64 { - *TWENTY_LOG10_MAX - 10.0 * mse(img1, img2).log10() +/// Computes the PSNR between two images of the same sizes. +pub fn psnr_without_alpha, T: GenericImage>(img1: &T, img2: &T) -> f64 { + *TWENTY_LOG10_MAX - 10.0 * mse_without_alpha(img1, img2).log10() } -pub fn psnr_files(path1: &str, path2: &str) -> ImageResult { +/// Computes the PSNR between two images of the same size from their paths. +pub fn psnr_files_without_alpha(path1: &str, path2: &str) -> ImageResult { let img1 = image::open(path1)?; let img2 = image::open(path2)?; - Ok(psnr(&img1, &img2)) + Ok(psnr_without_alpha(&img1, &img2)) }