Skip to content

Intensity metrics

metrics::intensity contains pure, domain-agnostic calculations for comparing two scalar two-dimensional images. It does not contain losses used to optimize a reconstruction: those remain in algorithms::objective.

Every two-image metric calls its inputs reference and candidate. A signed residual is always candidate - reference. An optional valid_mask has the same shape as both images; true includes a pixel and false excludes it. All metrics reject mismatched shapes, non-finite included values, and an empty valid mask.

Rust API

All atomic functions accept ndarray::ArrayView2<'_, T> with T: num_traits::ToPrimitive. They convert individual included samples to f64 for evaluation and return f64; the image data is borrowed, never copied. This supports ordinary u8, u16, u32, f32, and f64 images.

use fpm_rs::metrics::intensity::{nrmse, psnr};

let relative_error = nrmse(reference.view(), candidate.view(), None)?;
let quality_db = psnr(reference.view(), candidate.view(), None, 65_535.0)?;

The atomic API is intentionally not an optimization primitive. Reconstruction objectives operate on the algorithm's floating scalar and have gradient and stability contracts that differ from reporting metrics.

Metric definitions

Function Definition or convention
bias Mean signed residual.
mae, mse, rmse Mean absolute, squared, and root mean squared residual.
relative_l1 sum(abs(candidate - reference)) / sum(abs(reference)).
nrmse L2(candidate - reference) / L2(reference).
amplitude_nrmse NRMSE after applying sqrt to both intensities; requires non-negative values.
correlation Pearson correlation; undefined for a constant valid image.
psnr Peak signal-to-noise ratio in dB. data_range is required, finite, and positive; identical inputs produce +∞.
ssim Single-scale SSIM, higher is more similar. Uses an 11×11 Gaussian window with σ=1.5, K1=0.01, and K2=0.03.
poisson_deviance Summed Poisson deviance for non-negative intensities. A positive epsilon floors candidate intensity.
mean_poisson_deviance Poisson deviance divided by valid-pixel count.
fitted_gain Least-squares gain in candidate ≈ gain × reference.

relative_l1, nrmse, and fitted_gain are undefined for a zero reference normalization and return an error rather than silently choosing a scale.

data_range is explicit for PSNR and SSIM because deriving it separately from each image makes results incomparable between frames and runs. Use a documented normalization such as 1.0, or the camera's calibrated full-scale value.

For SSIM, a masked local window is included only when all of its 11×11 pixels are valid. Images smaller than the window, or masks without a fully valid window, return an error.

Python API

The same functions are available under fpm_rs.metrics:

import fpm_rs as fpm

score = fpm.metrics.ssim(
    reference,
    candidate,
    valid_mask=valid_pixels,
    data_range=65535.0,
)
gain = fpm.metrics.fitted_gain(reference, candidate)

Python accepts any real, two-dimensional NumPy-compatible numeric array, including uint8 and uint16, and evaluates it as float64. valid_mask must be a two-dimensional Boolean array. The conversion is part of the Python adapter; the Rust ArrayView2 API itself remains zero-copy.