Skip to content

Commit

Permalink
Add premultiplied option to RGBA image compare
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisRega committed Mar 27, 2023
1 parent 7187baa commit feeda63
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 2 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ Neither [memory optimizations](https://actix.vdop.org/view_post?post_num=10) nor
- Comparing U and V channels via RMS
- Recombining the differences to a nice visualization image
- RGB Score is calculated as: $\mathrm{score}=\mathrm{avg}_{x,y}\left(\mathrm{min}\left[\Delta \mathrm{MSSIM}(Y,x,y),\sqrt{(\Delta RMS(U,x,y))^2 + (\Delta RMS(V,x,y))^2}\right]\right)$
- For RGBA the $\alpha$ channel is also compared using MSSIM and taken into account.
- RGBA can either be premultiplied with a specifiable background color using `rgba_blended_hybrid_compare`
- Otherwise, for `rgba_hybrid_compare` the $\alpha$ channel is also compared using MSSIM and taken into account.
- The average alpha of each pixel $\bar{\alpha}(x,y) = 1/2 (\alpha_1(x,y) + \alpha_2(x,y))$ is then used as a linear weighting factor
- RGBA Score is calculated as: $\mathrm{score}=\mathrm{avg}_{x,y}\left(1/\bar{\alpha} \cdot \mathrm{min}\left[\Delta \mathrm{MSSIM}(Y,x,y),\sqrt{(\Delta RMS(U,x,y))^2 + (\Delta RMS(V,x,y))^2}, \Delta \mathrm{RMS}(\alpha,x,y)\right]\right)$
- Edge cases RGBA: $\mathrm{score} \in (0, 1)$ and $\mathrm{score} = 1.0$ if $\bar{\alpha} = 0.0$
Expand Down
14 changes: 13 additions & 1 deletion src/hybrid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::colorization::{GraySimilarityImage, RGBASimilarityImage, RGBSimilarit
use crate::prelude::*;
use crate::squared_error::root_mean_squared_error_simple;
use crate::ssim::ssim_simple;
use crate::utils::split_rgba_to_yuva;
use crate::utils::{blend_alpha, split_rgba_to_yuva};
use crate::Decompose;
use image::{Rgba, RgbaImage};
use itertools::izip;
Expand Down Expand Up @@ -120,6 +120,18 @@ pub fn rgba_hybrid_compare(
))
}

/// This processes the RGBA images be pre-blending the colors with the desired background color.
/// It's faster then the full RGBA similarity and more intuitive.
pub fn rgba_blended_hybrid_compare(
first: &RgbaImage,
second: &RgbaImage,
background: Rgb<u8>,
) -> Result<Similarity, CompareError> {
let first = blend_alpha(first, background);
let second = blend_alpha(second, background);
rgb_hybrid_compare(&first, &second)
}

/// 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)
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ use crate::ssim::ssim_simple;
#[doc(inline)]
pub use hybrid::rgba_hybrid_compare;

#[doc(inline)]
pub use hybrid::rgba_blended_hybrid_compare;

#[cfg(test)]
mod tests {
use super::*;
Expand Down
44 changes: 44 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,29 @@ pub(crate) fn split_rgba_to_yuva(source: &RgbaImage) -> [GrayImage; 4] {
[y, u, v, a]
}

fn blend(c: u8, a: u8, c_b: u8) -> u8 {
let c = c as f32 / 255.;
let a = a as f32 / 255.;
let c_b = c_b as f32 / 255.;
let blended = (c * a) + (c_b * (1. - a));
(blended.clamp(0., 1.) * 255.) as u8
}

pub(crate) fn blend_alpha(image: &RgbaImage, color: Rgb<u8>) -> RgbImage {
let mut buffer = ImageBuffer::new(image.width(), image.height());

for (input, output) in image.pixels().zip(buffer.pixels_mut()) {
let [r, g, b, a] = input.0;

*output = Rgb([
blend(r, a, color.0[0]),
blend(g, a, color.0[1]),
blend(b, a, color.0[2]),
]);
}
buffer
}

pub trait Decompose {
fn split_channels(&self) -> [GrayImage; 3];
fn split_to_yuv(&self) -> [GrayImage; 3];
Expand Down Expand Up @@ -191,6 +214,7 @@ pub fn draw_window_to_image(window: &Window, image: &mut GraySimilarityImage, va
#[cfg(test)]
mod tests {
use super::*;
use image::Rgba;

#[test]
fn window_test() {
Expand Down Expand Up @@ -275,4 +299,24 @@ mod tests {
assert_eq!(black[1], 0.);
assert_eq!(black[2], 0.);
}

#[test]
fn blend_test() {
// black with white background but no alpha = white
assert_eq!(blend(0, 0, 255), 255);
// white with black background and full alpha = white
assert_eq!(blend(255, 255, 0), 255);
// white with black background and no alpha = black
assert_eq!(blend(255, 0, 0), 0);
// white with black background and half alpha = gray
assert_eq!(blend(255, 127, 0), 127);
}

#[test]
fn blend_image_test() {
let test_image = RgbaImage::from_pixel(2, 2, Rgba([255, 0, 127, 127]));
let pre_mult = blend_alpha(&test_image, Rgb([255, 255, 255]));
let b_target = 127 + 127 / 2 + 1;
assert_eq!(pre_mult.get_pixel(0, 0).0, [255u8, 127u8, b_target]);
}
}

0 comments on commit feeda63

Please sign in to comment.