SSIM

SSIM captures the perceptual similarity of two images. It is used, for example, to test out the quality of image compression or denoising algorithms.

Given two images x and y, SSIM(x, y) is defined as:

\[\text{SSIM}(x, y) = \underbrace{\left[\frac{2\mu_x \mu_y + C_1}{\mu_x^2 + \mu_y^2 + C_1}\right]}_{\text{luminance}} \cdot \underbrace{\left[\frac{2\sigma_x \sigma_y + C_2}{\sigma_x^2 + \sigma_y^2 + C_2}\right]}_{\text{contrast}} \cdot \underbrace{\left[\frac{\sigma_{xy} + C_3}{\sigma_x \sigma_y + C_3}\right]}_{\text{structure}}\]

where:

  • \(\mu_x\) and \(\mu_y\) are the local luminance of x and y.

  • \(\sigma_x\) and \(\sigma_y\) are the local contrast of x and y.

  • \(\sigma_{xy}\) is the joint variation of x and y, which carries how far their local structures agree.

  • \(C_1 = (k_1 L)^2\) and \(C_2 = (k_2 L)^2\) are stabilization constants that keep the fractions well-defined where their denominators come close to zero. Both \(k_1\) and \(k_2\) are exposed as parameters.

  • window_size and sigma are the side length and the standard deviation of the Gaussian window that defines the neighborhood of a pixel.

With \(C_3 = C_2 / 2\), the three components collapse into one fraction:

\[\text{SSIM}(x, y) = \frac{\left(2\mu_x \mu_y + C_1\right)\left(2\sigma_{xy} + C_2\right)}{\left(\mu_x^2 + \mu_y^2 + C_1\right)\left(\sigma_x^2 + \sigma_y^2 + C_2\right)}\]

Here, \(L\) is the dynamic range of the pixel values. In pyvisim, it is fixed at 255 (every input is normalized to the [0, 255] range).

Identical images score 1, unrelated images score near 0, and inverted structures score below 0.

Usage

from pyvisim.dense.structural import SSIM

ssim = SSIM()
scores = ssim.similarity_score(image1, image2)   # (1, 1) matrix

Benchmark

Important

This file was generated by the script docs/dense/structural/benchmarks/generate_benchmark.py. Do not edit manually!

All images are drawn from the Oxford Flower dataset (train split, seed 0), with a disjoint image subset per experiment and per metric. num_workers=4. 7 timed calls after one warm-up.

Baseline: scikit-image 0.26.0 — skimage.metrics.structural_similarity(win_size=11, gaussian_weights=True, sigma=1.5, use_sample_covariance=False, data_range=255).

Accuracy

Each image (native resolution) is scored against a copy distorted with Gaussian noise (std 15). The error is the absolute difference between the pyvisim score and the baseline score.

Image

pyvisim

Baseline

Abs. error

image_00273.jpg

0.334296

0.334297

7.47e-07

image_01205.jpg

0.334991

0.334990

3.06e-07

image_03960.jpg

0.432977

0.432975

1.32e-06

image_03963.jpg

0.550576

0.550576

1.91e-07

image_04678.jpg

0.361865

0.361864

7.84e-07

image_04824.jpg

0.448853

0.448853

3.91e-07

image_05899.jpg

0.351737

0.351736

4.60e-07

image_06746.jpg

0.442485

0.442485

1.91e-07

Runtime

Median wall-clock time per full scoring call (SSIM of every image in the first gallery against every image in the second). For a fair comparison, GPU is disabled.

Scenario

Images

pyvisim (ms)

Baseline (ms)

Speed-up

1 pair (512x512), 512x512

image_00226.jpg, image_03707.jpg

8.1

71.3

8.8x

1 expanded pair (1024x1024), 1024x1024

image_00150.jpg

52.7

376

7.1x

batch of 4 (16 pairs), 256x256

image_07561.jpg, image_02145.jpg, image_00112.jpg, image_07844.jpg

27.6

176

6.4x

batch of 8 (64 pairs), 256x256

image_04707.jpg, image_06877.jpg, image_05021.jpg, image_06347.jpg, image_00057.jpg, image_00507.jpg, image_01199.jpg, image_03657.jpg

110

659

6.0x

SSIM median runtime

API reference

class pyvisim.dense.structural.SSIM(window_size=11, sigma=1.5, k1=0.01, k2=0.03, batch_size=16, num_workers=None)[source]

Bases: DenseMetricBase

Structural Similarity index (Wang et al., 2004) between image batches.

For more information, see the documentation: https://mechacritter.github.io/Python-Visual-Similarity/dense/structural/ssim/ssim.html.

NOTE

Windows are applied in “valid” mode with population statistics, and the final score is the mean over the whole SSIM map (all channels pooled)

param window_size:

Side length of the Gaussian window, an odd integer >= 3.

param sigma:

Standard deviation of the Gaussian window.

param k1:

Luminance stabilization constant.

param k2:

Contrast stabilization constant.

param batch_size:

Maximum number of image pairs processed in a single batch. Set to -1 to process all images as a single batch.

param num_workers:

Number of threads to use for the computation. If None, the PYVISIM_NUM_THREADS environment variable decides, which can also be changed through os.environ["PYVISIM_NUM_THREADS"].

raises ValueError:

If any parameter is outside its valid range.

set_batch_size(batch_size)

Sets the number of items processed per batch.

Parameters:

batch_size (int) – Maximum number of images processed in a single batch. Set to -1 to process all images as a single batch.

Raises:

ValueError – If batch_size is neither -1 nor a positive integer.

Return type:

None

similarity_score(image1, image2, *, dims='HWC', value_range=(0.0, 255.0))

Compute the pairwise score matrix between two image batches.

Every image is normalized to the canonical uint8 (H, W[, C]) layout in [0, 255] first, so the metric always operates on the same value scale regardless of the input dtype or range.

Parameters:
Returns:

A (N, M) matrix scoring every image of image1 against every image of image2.

Raises:
  • InvalidImageError – If an input cannot be converted to a numeric array.

  • ValueError – If a batch is empty, the two batches hold images of different shapes, or the images are too small for the metric.

Return type:

ndarray[tuple[int, …], dtype[floating[Any]]]

property k1: float

Luminance stabilization constant.

property k2: float

Contrast stabilization constant.

property sigma: float

Standard deviation of the Gaussian window.

property window_size: int

Side length of the Gaussian window, in pixels.

Parameters: