MSSSIM¶
MS-SSIM goes one step further than SSIM by computing
the SSIM at multiple scales. Hence, images are first downsampled by half (up to
5 times) and the SSIM is computed at each scale, then aggregated. Given two
images x and y, MS-SSIM(x, y) is defined as:
where:
\(l_j\), \(c_j\) and \(s_j\) are the luminance, contrast and structure components at scale \(j\), as defined in the
SSIMformula.\(M\) is the coarsest scale, which is set by the number of entries in
weights.\(\alpha_M\), \(\beta_j\) and \(\gamma_j\) are the exponents that weight the contribution of each scale. In
pyvisim, the default weights, proposed by Wang et al. (2003), are used.
At the end of each scale, the images are low-pass filtered and downsampled by a factor of two through 2x2 average pooling. The luminance component is evaluated at the coarsest scale \(M\) only, so the product above is computed from the per-scale contrast-structure map means \(cs_j\) and the full SSIM mean \(\text{ssim}_M\):
Each entry of weights is one exponent \(w_j\), ordered with the coarsest
scale last, and the number of entries sets the number of scales.
The Gaussian window has to fit the images after n_scales - 1 halvings, so
each image side has to measure at least window_size * 2 ** (n_scales - 1)
pixels.
Usage¶
from pyvisim.dense.structural import MSSSIM
msssim = MSSSIM(batch_size=16)
matrix = msssim.similarity_score(gallery, queries) # (N, M) 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: torchmetrics 1.9.0 — torchmetrics.functional.image.multiscale_structural_similarity_index_measure(data_range=255.0, kernel_size=11, sigma=1.5, normalize=’relu’).
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 |
|---|---|---|---|
|
0.820745 |
0.820213 |
5.33e-04 |
|
0.844088 |
0.841904 |
2.18e-03 |
|
0.861056 |
0.860066 |
9.90e-04 |
|
0.889506 |
0.888125 |
1.38e-03 |
|
0.877060 |
0.877032 |
2.75e-05 |
|
0.850458 |
0.850367 |
9.07e-05 |
|
0.854924 |
0.854978 |
5.38e-05 |
|
0.854107 |
0.854030 |
7.69e-05 |
Runtime¶
Median wall-clock time per full scoring call (MS-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 |
|
25.3 |
76.3 |
3.0x |
1 expanded pair (1024x1024), 1024x1024 |
|
121 |
325 |
2.7x |
batch of 4 (16 pairs), 256x256 |
|
98.8 |
323 |
3.3x |
batch of 8 (64 pairs), 256x256 |
|
369 |
1,360 |
3.7x |

API reference¶
- class pyvisim.dense.structural.MSSSIM(weights=(0.0448, 0.2856, 0.3001, 0.2363, 0.1333), window_size=11, sigma=1.5, k1=0.01, k2=0.03, batch_size=16, num_workers=None)[source]¶
Bases:
DenseMetricBaseMulti-Scale Structural Similarity index (Wang et al., 2003).
For more information, see the documentation:
https://mechacritter.github.io/Python-Visual-Similarity/dense/structural/ms_ssim/ms_ssim.html.NOTE¶
Scale means below zero are clamped to zero before they are raised to a fractional power, which keeps the result real. Howevcer, natural image pairs are rarely affected by the clamping.
- param weights:
One positive exponent per scale, coarsest scale last. The number of weights sets the number of scales.
WANG_WEIGHTSholds the five exponents calibrated in the original paper.- 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
-1to process all images as a single batch.- param num_workers:
Number of threads to use for the computation. If
None, thePYVISIM_NUM_THREADSenvironment variable decides, which can also be changed throughos.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
-1to process all images as a single batch.- Raises:
ValueError – If
batch_sizeis neither-1nor 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:
image1 (_Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes] | Iterable[_Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes]]) – First (batch of) image(s) as
MatLike(NumPy array, torch tensor or array-like).image2 (_Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes] | Iterable[_Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes]]) – Second (batch of) image(s) as
MatLike.dims (str) – Axis-label string, one character per array axis in order:
"H"= height (rows),"W"= width (columns),"C"= channels (e.g. RGB),"B"= batch size. For example,"HWC"is height × width × channels (NumPy/OpenCV single-image layout);"CHW"is channels × height × width (PyTorch single-image layout);"BCHW"is batch × channels × height × width (PyTorch batched layout). Seepyvisim.typing.value_range (tuple[float, float]) – The
(low, high)range the input values live in; converted into the canonical[0, 255]range.
- Returns:
A
(N, M)matrix scoring every image ofimage1against every image ofimage2.- 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: