diff --git a/Cargo.toml b/Cargo.toml index 4271a9d..6a7deb6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,14 @@ image = { version = "0.25", default-features = false, features = ["png"] } name = "compare" harness = false +[[test]] +name = "utils" +harness = false + [profile.release-with-debug] inherits = "release" debug = true + +[features] +default = [] +yuv_compare = [] diff --git a/src/hybrid.rs b/src/hybrid.rs index 7ca3381..708a99f 100644 --- a/src/hybrid.rs +++ b/src/hybrid.rs @@ -167,8 +167,8 @@ pub fn rgba_blended_hybrid_compare( /// Comparing structure via MSSIM on Y channel, comparing color-diff-vectors on U and V summing the squares /// Please mind that the RGBSimilarity-Image does _not_ contain plain RGB here /// - The red channel contains 1. - similarity(ssim, y) -/// - The green channel contains 1. - similarity(rms, u) -/// - The blue channel contains 1. - similarity(rms, v) +/// - The green channel contains 1. - similarity(rms, u) +/// - The blue channel contains 1. - similarity(rms, v) /// This leads to a nice visualization of color and structure differences - with structural differences (meaning gray mssim diffs) leading to red rectangles /// and and the u and v color diffs leading to color-deviations in green, blue and cyan /// All-black meaning no differences @@ -179,6 +179,35 @@ pub fn rgb_hybrid_compare(first: &RgbImage, second: &RgbImage) -> Result Result { + if (first_channels[0].dimensions() != second_channels[0].dimensions()) + || (first_channels[1].dimensions() != second_channels[1].dimensions()) // 5 checks needed, this ensures all channels are the same sizes. + || (first_channels[2].dimensions() != second_channels[2].dimensions()) // First 3 ensure the channels each have the same resolution across the 2 images + || (first_channels[0].dimensions() != first_channels[1].dimensions()) // Last 2 check if each channel is the same in the first image + || (first_channels[1].dimensions() != first_channels[2].dimensions()) { // If all checks pass this leaves all 6 being the same + return Err(CompareError::DimensionsDiffer); + } + internal_yuv_hybrid_compare(&first_channels, &second_channels) +} + +pub(crate) fn internal_yuv_hybrid_compare(first_channels: &[GrayImage; 3], second_channels: &[GrayImage; 3]) -> Result { let (_, mssim_result) = ssim_simple(&first_channels[0], &second_channels[0])?; let (_, u_result) = root_mean_squared_error_simple(&first_channels[1], &second_channels[1])?; let (_, v_result) = root_mean_squared_error_simple(&first_channels[2], &second_channels[2])?; diff --git a/src/lib.rs b/src/lib.rs index cd5547c..6af4309 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -77,8 +77,15 @@ mod histogram; mod hybrid; mod squared_error; mod ssim; + + +#[cfg(not(feature="yuv_compare"))] // Tests cannot be implemented to check the functionality of this feature gate, please be mindful of this. mod utils; +/// Provides some utilities to make yuv image management and conversion easier. +#[cfg(feature="yuv_compare")] // Exposes rgb/yuv conversions and split to yuv publicly, others were left to be pub crate or private +pub mod utils; // All exposed APIs have tests, and compilation will fail if they ever become non-public + #[doc(hidden)] pub mod prelude { pub use image::{GrayImage, ImageBuffer, Luma, Rgb, RgbImage}; @@ -226,6 +233,10 @@ pub use hybrid::rgba_hybrid_compare; #[doc(inline)] pub use hybrid::rgba_blended_hybrid_compare; +#[doc(inline)] +#[cfg(feature = "yuv_compare")] +pub use hybrid::yuv_hybrid_compare; + pub use hybrid::BlendInput; #[cfg(test)] diff --git a/src/utils.rs b/src/utils.rs index a96445c..76df838 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -3,7 +3,7 @@ use image::RgbaImage; use itertools::izip; /// see https://www.itu.int/rec/T-REC-T.871 -fn rgb_to_yuv(rgb: &[f32; 3]) -> [f32; 3] { +pub fn rgb_to_yuv(rgb: &[f32; 3]) -> [f32; 3] { let py = 0. + (0.299 * rgb[0]) + (0.587 * rgb[1]) + (0.114 * rgb[2]); let pu = 128. - (0.168736 * rgb[0]) - (0.331264 * rgb[1]) + (0.5 * rgb[2]); let pv = 128. + (0.5 * rgb[0]) - (0.418688 * rgb[1]) - (0.081312 * rgb[2]); @@ -12,7 +12,7 @@ fn rgb_to_yuv(rgb: &[f32; 3]) -> [f32; 3] { /// see https://www.itu.int/rec/T-REC-T.871 #[allow(dead_code)] -fn yuv_to_rgb(yuv: &[f32; 3]) -> [f32; 3] { +pub fn yuv_to_rgb(yuv: &[f32; 3]) -> [f32; 3] { let r = yuv[0] + (1.402 * (yuv[2] - 128.)); let g = yuv[0] - (0.344136 * (yuv[1] - 128.)) - (0.714136 * (yuv[2] - 128.)); let b = yuv[0] + (1.772 * (yuv[1] - 128.)); @@ -67,8 +67,11 @@ pub(crate) fn blend_alpha(image: &RgbaImage, color: Rgb) -> RgbImage { buffer } +/// Holds methods to split an RgbImage into arrays of channels pub trait Decompose { + /// Returns an array containing 3 `GrayImage`s where each channel represents [r, g, b] fn split_channels(&self) -> [GrayImage; 3]; + /// Converts an image to yuv then returns an array containing 3 `GrayImage`s where each channel represents [y, u, v] fn split_to_yuv(&self) -> [GrayImage; 3]; } @@ -113,7 +116,7 @@ impl Decompose for RgbImage { } } -pub fn merge_similarity_channels(input: &[&GraySimilarityImage; 3]) -> RGBSimilarityImage { +pub(crate) fn merge_similarity_channels(input: &[&GraySimilarityImage; 3]) -> RGBSimilarityImage { let mut output = RGBSimilarityImage::new(input[0].width(), input[0].height()); izip!( input[0].pixels(), @@ -128,12 +131,12 @@ pub fn merge_similarity_channels(input: &[&GraySimilarityImage; 3]) -> RGBSimila output } -pub struct Window { +pub(crate) struct Window { pub top_left: (u32, u32), pub bottom_right: (u32, u32), } -pub struct WindowIter<'a> { +pub(crate) struct WindowIter<'a> { current_index: u32, window: &'a Window, } @@ -189,7 +192,7 @@ impl Window { result } - pub fn iter_pixels(&self) -> WindowIter { + pub fn iter_pixels(&'_ self) -> WindowIter<'_> { WindowIter { window: self, current_index: 0, @@ -204,7 +207,7 @@ impl Window { } } -pub fn draw_window_to_image(window: &Window, image: &mut GraySimilarityImage, val: f32) { +pub(crate) fn draw_window_to_image(window: &Window, image: &mut GraySimilarityImage, val: f32) { window .iter_pixels() .for_each(|current_pixel| image.put_pixel(current_pixel.0, current_pixel.1, Luma([val]))); diff --git a/tests/compare.rs b/tests/compare.rs index 9698c86..f4012b7 100644 --- a/tests/compare.rs +++ b/tests/compare.rs @@ -12,6 +12,7 @@ pub struct CompareWorld { comparison_result: Option, comparison_result_rgb: Option, comparison_result_rgba: Option, + comparison_result_yuv: Option, } #[given(expr = "the images {string} and {string} are loaded")] @@ -129,6 +130,20 @@ fn compare_hybrid_rgb(world: &mut CompareWorld) { ); } +#[cfg(feature = "yuv_compare")] +#[when(expr = "comparing the images using the hybrid mode as yuv")] +fn compare_hybrid_yuv(world: &mut CompareWorld) { + use image_compare::utils::Decompose; + + world.comparison_result_yuv = Some( + image_compare::yuv_hybrid_compare( + &world.first.as_ref().unwrap().clone().into_rgb8().split_to_yuv(), + &world.second.as_ref().unwrap().clone().into_rgb8().split_to_yuv(), + ) + .expect("Error comparing the two images!"), + ); +} + #[then(expr = "the similarity score is {float}")] fn check_result_score(world: &mut CompareWorld, score: f64) { if world.comparison_result.is_some() { @@ -137,6 +152,8 @@ fn check_result_score(world: &mut CompareWorld, score: f64) { assert_eq!(world.comparison_result_rgb.as_ref().unwrap().score, score); } else if world.comparison_result_rgba.is_some() { assert_eq!(world.comparison_result_rgba.as_ref().unwrap().score, score); + } else if world.comparison_result_yuv.is_some() { + assert_eq!(world.comparison_result_yuv.as_ref().unwrap().score, score); } else { panic!("No result calculated yet") } @@ -182,26 +199,6 @@ fn check_result_image_rgba(world: &mut CompareWorld, reference: String) { ); } -#[then(expr = "the rgb similarity image matches {string}")] -fn check_result_image_rgb(world: &mut CompareWorld, reference: String) { - let img = world - .comparison_result_rgb - .as_ref() - .unwrap() - .image - .to_color_map() - .into_rgb8(); - let image_one = image::open(reference) - .expect("Could not find reference-image") - .into_rgb8(); - assert_eq!( - image_compare::rgb_similarity_structure(&Algorithm::RootMeanSquared, &img, &image_one) - .expect("Could not compare") - .score, - 1.0 - ); -} - #[tokio::main] async fn main() { CompareWorld::run("tests/features/structure_gray.feature").await; @@ -209,4 +206,6 @@ async fn main() { CompareWorld::run("tests/features/structure_rgb.feature").await; CompareWorld::run("tests/features/hybrid_rgb.feature").await; CompareWorld::run("tests/features/hybrid_rgba.feature").await; + #[cfg(feature = "yuv_compare")] + CompareWorld::run("tests/features/hybrid_yuv.feature").await; } diff --git a/tests/data/colored_primitives_b_channel.png b/tests/data/colored_primitives_b_channel.png new file mode 100644 index 0000000..12caae4 Binary files /dev/null and b/tests/data/colored_primitives_b_channel.png differ diff --git a/tests/data/colored_primitives_g_channel.png b/tests/data/colored_primitives_g_channel.png new file mode 100644 index 0000000..d72ed94 Binary files /dev/null and b/tests/data/colored_primitives_g_channel.png differ diff --git a/tests/data/colored_primitives_hybrid_compare_rgb_b_channel.png b/tests/data/colored_primitives_hybrid_compare_rgb_b_channel.png new file mode 100644 index 0000000..24744e6 Binary files /dev/null and b/tests/data/colored_primitives_hybrid_compare_rgb_b_channel.png differ diff --git a/tests/data/colored_primitives_hybrid_compare_rgb_g_channel.png b/tests/data/colored_primitives_hybrid_compare_rgb_g_channel.png new file mode 100644 index 0000000..fdc8ae2 Binary files /dev/null and b/tests/data/colored_primitives_hybrid_compare_rgb_g_channel.png differ diff --git a/tests/data/colored_primitives_hybrid_compare_rgb_r_channel.png b/tests/data/colored_primitives_hybrid_compare_rgb_r_channel.png new file mode 100644 index 0000000..695ad94 Binary files /dev/null and b/tests/data/colored_primitives_hybrid_compare_rgb_r_channel.png differ diff --git a/tests/data/colored_primitives_hybrid_compare_rgb_u_channel.png b/tests/data/colored_primitives_hybrid_compare_rgb_u_channel.png new file mode 100644 index 0000000..f48ad0a Binary files /dev/null and b/tests/data/colored_primitives_hybrid_compare_rgb_u_channel.png differ diff --git a/tests/data/colored_primitives_hybrid_compare_rgb_v_channel.png b/tests/data/colored_primitives_hybrid_compare_rgb_v_channel.png new file mode 100644 index 0000000..9a7c6d5 Binary files /dev/null and b/tests/data/colored_primitives_hybrid_compare_rgb_v_channel.png differ diff --git a/tests/data/colored_primitives_hybrid_compare_rgb_y_channel.png b/tests/data/colored_primitives_hybrid_compare_rgb_y_channel.png new file mode 100644 index 0000000..33becd3 Binary files /dev/null and b/tests/data/colored_primitives_hybrid_compare_rgb_y_channel.png differ diff --git a/tests/data/colored_primitives_r_channel.png b/tests/data/colored_primitives_r_channel.png new file mode 100644 index 0000000..d36a4ee Binary files /dev/null and b/tests/data/colored_primitives_r_channel.png differ diff --git a/tests/data/colored_primitives_swapped_b_channel.png b/tests/data/colored_primitives_swapped_b_channel.png new file mode 100644 index 0000000..c6744fd Binary files /dev/null and b/tests/data/colored_primitives_swapped_b_channel.png differ diff --git a/tests/data/colored_primitives_swapped_g_channel.png b/tests/data/colored_primitives_swapped_g_channel.png new file mode 100644 index 0000000..a1a104d Binary files /dev/null and b/tests/data/colored_primitives_swapped_g_channel.png differ diff --git a/tests/data/colored_primitives_swapped_r_channel.png b/tests/data/colored_primitives_swapped_r_channel.png new file mode 100644 index 0000000..de30179 Binary files /dev/null and b/tests/data/colored_primitives_swapped_r_channel.png differ diff --git a/tests/data/colored_primitives_swapped_u_channel.png b/tests/data/colored_primitives_swapped_u_channel.png new file mode 100644 index 0000000..b349c60 Binary files /dev/null and b/tests/data/colored_primitives_swapped_u_channel.png differ diff --git a/tests/data/colored_primitives_swapped_v_channel.png b/tests/data/colored_primitives_swapped_v_channel.png new file mode 100644 index 0000000..f903fcb Binary files /dev/null and b/tests/data/colored_primitives_swapped_v_channel.png differ diff --git a/tests/data/colored_primitives_swapped_y_channel.png b/tests/data/colored_primitives_swapped_y_channel.png new file mode 100644 index 0000000..e2d896c Binary files /dev/null and b/tests/data/colored_primitives_swapped_y_channel.png differ diff --git a/tests/data/colored_primitives_u_channel.png b/tests/data/colored_primitives_u_channel.png new file mode 100644 index 0000000..874e75b Binary files /dev/null and b/tests/data/colored_primitives_u_channel.png differ diff --git a/tests/data/colored_primitives_v_channel.png b/tests/data/colored_primitives_v_channel.png new file mode 100644 index 0000000..edf7c9a Binary files /dev/null and b/tests/data/colored_primitives_v_channel.png differ diff --git a/tests/data/colored_primitives_y_channel.png b/tests/data/colored_primitives_y_channel.png new file mode 100644 index 0000000..56db5be Binary files /dev/null and b/tests/data/colored_primitives_y_channel.png differ diff --git a/tests/data/pad_gaprao_b_channel.png b/tests/data/pad_gaprao_b_channel.png new file mode 100644 index 0000000..37a2e52 Binary files /dev/null and b/tests/data/pad_gaprao_b_channel.png differ diff --git a/tests/data/pad_gaprao_color_filters_b_channel.png b/tests/data/pad_gaprao_color_filters_b_channel.png new file mode 100644 index 0000000..1a344c1 Binary files /dev/null and b/tests/data/pad_gaprao_color_filters_b_channel.png differ diff --git a/tests/data/pad_gaprao_color_filters_g_channel.png b/tests/data/pad_gaprao_color_filters_g_channel.png new file mode 100644 index 0000000..5413e71 Binary files /dev/null and b/tests/data/pad_gaprao_color_filters_g_channel.png differ diff --git a/tests/data/pad_gaprao_color_filters_r_channel.png b/tests/data/pad_gaprao_color_filters_r_channel.png new file mode 100644 index 0000000..c18079b Binary files /dev/null and b/tests/data/pad_gaprao_color_filters_r_channel.png differ diff --git a/tests/data/pad_gaprao_color_filters_u_channel.png b/tests/data/pad_gaprao_color_filters_u_channel.png new file mode 100644 index 0000000..35c2a3b Binary files /dev/null and b/tests/data/pad_gaprao_color_filters_u_channel.png differ diff --git a/tests/data/pad_gaprao_color_filters_v_channel.png b/tests/data/pad_gaprao_color_filters_v_channel.png new file mode 100644 index 0000000..4f304bd Binary files /dev/null and b/tests/data/pad_gaprao_color_filters_v_channel.png differ diff --git a/tests/data/pad_gaprao_color_filters_y_channel.png b/tests/data/pad_gaprao_color_filters_y_channel.png new file mode 100644 index 0000000..e47d683 Binary files /dev/null and b/tests/data/pad_gaprao_color_filters_y_channel.png differ diff --git a/tests/data/pad_gaprao_g_channel.png b/tests/data/pad_gaprao_g_channel.png new file mode 100644 index 0000000..4160f20 Binary files /dev/null and b/tests/data/pad_gaprao_g_channel.png differ diff --git a/tests/data/pad_gaprao_gray_inverted_b_channel.png b/tests/data/pad_gaprao_gray_inverted_b_channel.png new file mode 100644 index 0000000..7a6363e Binary files /dev/null and b/tests/data/pad_gaprao_gray_inverted_b_channel.png differ diff --git a/tests/data/pad_gaprao_gray_inverted_g_channel.png b/tests/data/pad_gaprao_gray_inverted_g_channel.png new file mode 100644 index 0000000..7a6363e Binary files /dev/null and b/tests/data/pad_gaprao_gray_inverted_g_channel.png differ diff --git a/tests/data/pad_gaprao_gray_inverted_r_channel.png b/tests/data/pad_gaprao_gray_inverted_r_channel.png new file mode 100644 index 0000000..7a6363e Binary files /dev/null and b/tests/data/pad_gaprao_gray_inverted_r_channel.png differ diff --git a/tests/data/pad_gaprao_gray_inverted_u_channel.png b/tests/data/pad_gaprao_gray_inverted_u_channel.png new file mode 100644 index 0000000..5befb09 Binary files /dev/null and b/tests/data/pad_gaprao_gray_inverted_u_channel.png differ diff --git a/tests/data/pad_gaprao_gray_inverted_v_channel.png b/tests/data/pad_gaprao_gray_inverted_v_channel.png new file mode 100644 index 0000000..ddeef09 Binary files /dev/null and b/tests/data/pad_gaprao_gray_inverted_v_channel.png differ diff --git a/tests/data/pad_gaprao_gray_inverted_y_channel.png b/tests/data/pad_gaprao_gray_inverted_y_channel.png new file mode 100644 index 0000000..5dfbbd6 Binary files /dev/null and b/tests/data/pad_gaprao_gray_inverted_y_channel.png differ diff --git a/tests/data/pad_gaprao_lighter_b_channel.png b/tests/data/pad_gaprao_lighter_b_channel.png new file mode 100644 index 0000000..594a683 Binary files /dev/null and b/tests/data/pad_gaprao_lighter_b_channel.png differ diff --git a/tests/data/pad_gaprao_lighter_g_channel.png b/tests/data/pad_gaprao_lighter_g_channel.png new file mode 100644 index 0000000..b44800a Binary files /dev/null and b/tests/data/pad_gaprao_lighter_g_channel.png differ diff --git a/tests/data/pad_gaprao_lighter_r_channel.png b/tests/data/pad_gaprao_lighter_r_channel.png new file mode 100644 index 0000000..916ee16 Binary files /dev/null and b/tests/data/pad_gaprao_lighter_r_channel.png differ diff --git a/tests/data/pad_gaprao_lighter_u_channel.png b/tests/data/pad_gaprao_lighter_u_channel.png new file mode 100644 index 0000000..1efee07 Binary files /dev/null and b/tests/data/pad_gaprao_lighter_u_channel.png differ diff --git a/tests/data/pad_gaprao_lighter_v_channel.png b/tests/data/pad_gaprao_lighter_v_channel.png new file mode 100644 index 0000000..a929ccf Binary files /dev/null and b/tests/data/pad_gaprao_lighter_v_channel.png differ diff --git a/tests/data/pad_gaprao_lighter_y_channel.png b/tests/data/pad_gaprao_lighter_y_channel.png new file mode 100644 index 0000000..9c6da1d Binary files /dev/null and b/tests/data/pad_gaprao_lighter_y_channel.png differ diff --git a/tests/data/pad_gaprao_noise_b_channel.png b/tests/data/pad_gaprao_noise_b_channel.png new file mode 100644 index 0000000..6488241 Binary files /dev/null and b/tests/data/pad_gaprao_noise_b_channel.png differ diff --git a/tests/data/pad_gaprao_noise_g_channel.png b/tests/data/pad_gaprao_noise_g_channel.png new file mode 100644 index 0000000..83d14ea Binary files /dev/null and b/tests/data/pad_gaprao_noise_g_channel.png differ diff --git a/tests/data/pad_gaprao_noise_r_channel.png b/tests/data/pad_gaprao_noise_r_channel.png new file mode 100644 index 0000000..5140dcf Binary files /dev/null and b/tests/data/pad_gaprao_noise_r_channel.png differ diff --git a/tests/data/pad_gaprao_noise_u_channel.png b/tests/data/pad_gaprao_noise_u_channel.png new file mode 100644 index 0000000..aac1dee Binary files /dev/null and b/tests/data/pad_gaprao_noise_u_channel.png differ diff --git a/tests/data/pad_gaprao_noise_v_channel.png b/tests/data/pad_gaprao_noise_v_channel.png new file mode 100644 index 0000000..e7028de Binary files /dev/null and b/tests/data/pad_gaprao_noise_v_channel.png differ diff --git a/tests/data/pad_gaprao_noise_y_channel.png b/tests/data/pad_gaprao_noise_y_channel.png new file mode 100644 index 0000000..722614d Binary files /dev/null and b/tests/data/pad_gaprao_noise_y_channel.png differ diff --git a/tests/data/pad_gaprao_r_channel.png b/tests/data/pad_gaprao_r_channel.png new file mode 100644 index 0000000..a6f3aa5 Binary files /dev/null and b/tests/data/pad_gaprao_r_channel.png differ diff --git a/tests/data/pad_gaprao_u_channel.png b/tests/data/pad_gaprao_u_channel.png new file mode 100644 index 0000000..40380de Binary files /dev/null and b/tests/data/pad_gaprao_u_channel.png differ diff --git a/tests/data/pad_gaprao_v_channel.png b/tests/data/pad_gaprao_v_channel.png new file mode 100644 index 0000000..3ce9a14 Binary files /dev/null and b/tests/data/pad_gaprao_v_channel.png differ diff --git a/tests/data/pad_gaprao_y_channel.png b/tests/data/pad_gaprao_y_channel.png new file mode 100644 index 0000000..4b6017c Binary files /dev/null and b/tests/data/pad_gaprao_y_channel.png differ diff --git a/tests/features/decompose.feature b/tests/features/decompose.feature new file mode 100644 index 0000000..d2c2d1a --- /dev/null +++ b/tests/features/decompose.feature @@ -0,0 +1,35 @@ +Feature: Decompose trait test + +Scenario Outline: Compare newly split yuv channels to expected values + Given the image '' is loaded + When comparing yuv channels + Then check split success + +Examples: +| full_image | +| tests/data/pad_gaprao.png | +| tests/data/pad_gaprao_lighter.png | +| tests/data/pad_gaprao_noise.png | +| tests/data/pad_gaprao_gray_inverted.png | +| tests/data/pad_gaprao_color_filters.png | +| tests/data/colored_primitives_swapped.png | +| tests/data/colored_primitives_hybrid_compare_rgb.png | +| tests/data/colored_primitives.png | + + + +Scenario Outline: Compare newly split yuv channels to expected values + Given the image '' is loaded + When comparing rgb channels + Then check split success + +Examples: +| full_image | +| tests/data/pad_gaprao.png | +| tests/data/pad_gaprao_lighter.png | +| tests/data/pad_gaprao_noise.png | +| tests/data/pad_gaprao_gray_inverted.png | +| tests/data/pad_gaprao_color_filters.png | +| tests/data/colored_primitives_swapped.png | +| tests/data/colored_primitives_hybrid_compare_rgb.png | +| tests/data/colored_primitives.png | \ No newline at end of file diff --git a/tests/features/hybrid_yuv.feature b/tests/features/hybrid_yuv.feature new file mode 100644 index 0000000..fb94174 --- /dev/null +++ b/tests/features/hybrid_yuv.feature @@ -0,0 +1,20 @@ +Feature: YUV image comparison using hybrid mode - MSSIM for for Y channel, RMS for U and V + + Scenario: Comparing an image to the original with hybrid mode and checking the difference image + Given the images 'tests/data/colored_primitives.png' and 'tests/data/colored_primitives_swapped.png' are loaded + When comparing the images using the hybrid mode as yuv + Then the YUV similarity image matches 'tests/data/colored_primitives_hybrid_compare_rgb.png' + + +Scenario Outline: Comparing a modified image to the original using hybrid mode algorithm +Given the images 'tests/data/pad_gaprao.png' and '' are loaded +When comparing the images using the hybrid mode as yuv +Then the similarity score is + +Examples: +| compare_image | result | +| tests/data/pad_gaprao.png | 1.0 | +| tests/data/pad_gaprao_lighter.png | 0.9514066504143178 | +| tests/data/pad_gaprao_noise.png | 0.13009783371684705 | +| tests/data/pad_gaprao_gray_inverted.png | 3.2566565189821024e-5 | +| tests/data/pad_gaprao_color_filters.png | 0.9876923700357477 | diff --git a/tests/utils.rs b/tests/utils.rs new file mode 100644 index 0000000..c4e5906 --- /dev/null +++ b/tests/utils.rs @@ -0,0 +1,74 @@ +use cucumber::{given, then, when, World}; +use image::DynamicImage; +use image_compare::prelude::*; +extern crate image; + +#[cfg(feature = "yuv_compare")] // Do not remove, ensures rgb_to_yuv stays public not pub(crate) or private +use image_compare::utils::rgb_to_yuv; +#[cfg(feature = "yuv_compare")] // Do not remove, ensures yuv_to_rgb stays public not pub(crate) or private +use image_compare::utils::yuv_to_rgb; + +// `World` is your shared, likely mutable state. +#[derive(Debug, World, Default)] +pub struct SplitWorld { + image: Option, + channel_r: Option, + channel_g: Option, + channel_b: Option, + channel_y: Option, + channel_u: Option, + channel_v: Option, + success: Option +} + +#[cfg(feature = "yuv_compare")] +#[given(expr = "the image {string} is loaded")] +fn load_images(world: &mut SplitWorld, image: String) { + let full_image = image::open(image.clone()).expect(format!("Could not find test-image {}", image).as_str()); + world.image = Some(full_image.clone()); + world.channel_y = Some(image::open(image.clone().replace(".png", "_y_channel.png")).expect("Could not load y_channel").to_luma8()); // Automatically loads images + world.channel_u = Some(image::open(image.clone().replace(".png", "_u_channel.png")).expect("Could not load u_channel").to_luma8()); + world.channel_v = Some(image::open(image.clone().replace(".png", "_v_channel.png")).expect("Could not load v_channel").to_luma8()); + world.channel_r = Some(image::open(image.clone().replace(".png", "_r_channel.png")).expect("Could not load r_channel").to_luma8()); + world.channel_g = Some(image::open(image.clone().replace(".png", "_g_channel.png")).expect("Could not load g_channel").to_luma8()); + world.channel_b = Some(image::open(image.clone().replace(".png", "_b_channel.png")).expect("Could not load b_channel").to_luma8()); +} + +#[cfg(feature = "yuv_compare")] +#[when(expr = "comparing yuv channels")] +fn decompose_yuv(world: &mut SplitWorld) { + use image_compare::utils::Decompose; + + let [channel_y, channel_u, channel_v] = world.image.as_ref().unwrap().to_rgb8().split_to_yuv(); + let y_comp = image_compare::gray_similarity_structure(&Algorithm::RootMeanSquared, &channel_y, world.channel_y.as_ref().unwrap()).expect("RMS Gray compare error").score == 1.0; + let u_comp = image_compare::gray_similarity_structure(&Algorithm::RootMeanSquared, &channel_u, world.channel_u.as_ref().unwrap()).expect("RMS Gray compare error").score == 1.0; + let v_comp = image_compare::gray_similarity_structure(&Algorithm::RootMeanSquared, &channel_v, world.channel_v.as_ref().unwrap()).expect("RMS Gray compare error").score == 1.0; + + world.success = Some(y_comp && u_comp && v_comp); +} + +#[cfg(feature = "yuv_compare")] +#[when(expr = "comparing rgb channels")] +fn decompose_rgb(world: &mut SplitWorld) { + use image_compare::utils::Decompose; + + let [channel_r, channel_g, channel_b] = world.image.as_ref().unwrap().to_rgb8().split_channels(); + let r_comp = image_compare::gray_similarity_structure(&Algorithm::RootMeanSquared, &channel_r, world.channel_r.as_ref().unwrap()).expect("RMS Gray compare error").score == 1.0; + let g_comp = image_compare::gray_similarity_structure(&Algorithm::RootMeanSquared, &channel_g, world.channel_g.as_ref().unwrap()).expect("RMS Gray compare error").score == 1.0; + let b_comp = image_compare::gray_similarity_structure(&Algorithm::RootMeanSquared, &channel_b, world.channel_b.as_ref().unwrap()).expect("RMS Gray compare error").score == 1.0; + + world.success = Some(r_comp && g_comp && b_comp); +} + +#[cfg(feature = "yuv_compare")] +#[then(expr = "check split success")] +fn check_success(world: &mut SplitWorld) { + assert!(world.success.unwrap() == true); +} + + +#[tokio::main] +async fn main() { + #[cfg(feature = "yuv_compare")] + SplitWorld::run("tests/features/decompose.feature").await; +} \ No newline at end of file