Skip to content

Commit

Permalink
Replace anyhow with concrete error type (via thiserror) (#16)
Browse files Browse the repository at this point in the history
* Replace anyhow with thiserror, add some docs

Also bumped MSRV to 1.65 for let-else. 1.65.0 is >1y old at
this point, so it should be fine.

* Add `Blur` documentation

* Adjust docs
  • Loading branch information
FreezyLemon authored Jan 30, 2024
1 parent b1515ff commit e27f97a
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 15 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "ssimulacra2"
version = "0.4.0"
edition = "2021"
rust-version = "1.61.0"
rust-version = "1.65.0"
description = "Rust implementation of the SSIMULACRA2 metric"
repository = "https://github.com/rust-av/ssimulacra2"
homepage = "https://github.com/rust-av/ssimulacra2"
Expand All @@ -15,10 +15,10 @@ default = ["rayon"]

[dependencies]
aligned = "0.4.1"
anyhow = "1.0.0"
nalgebra = "0.32.2"
num-traits = "0.2.15"
rayon = { version = "1.5.3", optional = true }
thiserror = "1.0.56"
wide = "0.7.5"
yuvxyb = "0.3.0"

Expand Down
15 changes: 15 additions & 0 deletions src/blur.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ use std::f64::consts::PI;
use aligned::{Aligned, A16};
use nalgebra::base::{Matrix3, Matrix3x1};

/// Structure handling image blur.
///
/// This struct contains the necessary buffers and the kernel used for blurring
/// (currently a recursive approximation of the Gaussian filter).
///
/// Note that the width and height of the image passed to [blur][Self::blur] needs to exactly
/// match the width and height of this instance. If you reduce the image size (e.g. via
/// downscaling), [`shrink_to`][Self::shrink_to] can be used to resize the internal buffers.
pub struct Blur {
kernel: RecursiveGaussian,
temp: Vec<f32>,
Expand All @@ -11,6 +19,8 @@ pub struct Blur {
}

impl Blur {
/// Create a new [Blur] for images of the given width and height.
/// This pre-allocates the necessary buffers.
#[must_use]
pub fn new(width: usize, height: usize) -> Self {
Blur {
Expand All @@ -21,12 +31,17 @@ impl Blur {
}
}

/// Truncates the internal buffers to fit images of the given width and height.
///
/// This will [truncate][Vec::truncate] the internal buffers
/// without affecting the allocated memory.
pub fn shrink_to(&mut self, width: usize, height: usize) {
self.temp.truncate(width * height);
self.width = width;
self.height = height;
}

/// Blur the given image.
pub fn blur(&mut self, img: &[Vec<f32>; 3]) -> [Vec<f32>; 3] {
[
self.blur_plane(&img[0]),
Expand Down
47 changes: 34 additions & 13 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,37 +41,58 @@

mod blur;

use anyhow::{bail, Result};
pub use blur::Blur;
pub use yuvxyb::{CastFromPrimitive, Frame, LinearRgb, Pixel, Plane, Rgb, Xyb, Yuv};
pub use yuvxyb::{ColorPrimaries, MatrixCoefficients, TransferCharacteristic, YuvConfig};

// How often to downscale and score the input images.
// Each scaling step will downscale by a factor of two.
const NUM_SCALES: usize = 6;

/// Errors which can occur when attempting to calculate a SSIMULACRA2 score from two input images.
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
pub enum Ssimulacra2Error {
/// The conversion from input image to [LinearRgb] (via [TryFrom]) returned an [Err].
/// Note that the conversion from LinearRgb to [Xyb] cannot fail, which means that
/// this is the only point of failure regarding image conversion.
#[error("Failed to convert input image to linear RGB")]
LinearRgbConversionFailed,

/// The two input images do not have the same width and height.
#[error("Source and distorted image width and height must be equal")]
NonMatchingImageDimensions,

/// One of the input images has a width and/or height of less than 8 pixels.
/// This is not currently supported by the SSIMULACRA2 metric.
#[error("Images must be at least 8x8 pixels")]
InvalidImageSize,
}

/// Computes the SSIMULACRA2 score for a given input frame and the distorted
/// version of that frame.
///
/// # Errors
/// - If the source and distorted image width and height do not match
/// - If the source or distorted image cannot be converted to XYB successfully
/// - If the image is smaller than 8x8 pixels
pub fn compute_frame_ssimulacra2<T: TryInto<LinearRgb>, U: TryInto<LinearRgb>>(
source: T,
distorted: U,
) -> Result<f64> {
let mut img1: LinearRgb = source
.try_into()
.map_err(|_e| anyhow::anyhow!("Failed to convert to Linear Rgb"))?;
let mut img2: LinearRgb = distorted
.try_into()
.map_err(|_e| anyhow::anyhow!("Failed to convert to Linear Rgb"))?;
pub fn compute_frame_ssimulacra2<T, U>(source: T, distorted: U) -> Result<f64, Ssimulacra2Error>
where
LinearRgb: TryFrom<T> + TryFrom<U>,
{
let Ok(mut img1) = LinearRgb::try_from(source) else {
return Err(Ssimulacra2Error::LinearRgbConversionFailed);
};

let Ok(mut img2) = LinearRgb::try_from(distorted) else {
return Err(Ssimulacra2Error::LinearRgbConversionFailed);
};

if img1.width() != img2.width() || img1.height() != img2.height() {
bail!("Source and distorted image width and height must be equal");
return Err(Ssimulacra2Error::NonMatchingImageDimensions);
}

if img1.width() < 8 || img1.height() < 8 {
bail!("Images must be at least 8x8 pixels");
return Err(Ssimulacra2Error::InvalidImageSize);
}

let mut width = img1.width();
Expand Down

0 comments on commit e27f97a

Please sign in to comment.