# SSIMULACRA2

Canonical URL: https://encode.wiki/reference/metrics/ssimulacra2/
Content version: ca42e61ede0a24f789d7287a0001244ad22f4abf
Status: current
License: CC BY-SA 4.0
Checksum (SHA-256): a1230d16a8f0f8a88cb319cc59e78ab60fefdd6d13a685ab8e93f8a828cf971e

SSIMULACRA2 (Structural SIMilarity Unveiling Local And Compression Related Artifacts) is a full-reference image metric by Jon Sneyers at Cloudinary, released in 2022 and updated to version 2.1 in 2023. It extends multi-scale SSIM with a perceptual colour space and separate penalties for added artifacts and lost detail, and it was tuned against large sets of viewer ratings.

It is the community's main metric for comparing encoders and settings. It correlates with viewer scores better than [PSNR](/reference/metrics/psnr/), [SSIM](/reference/metrics/ssim/) or [VMAF](/reference/metrics/vmaf/) on every dataset in its README, and it penalises both blur and ringing, so smoothing an encode doesn't buy points. For the very-high-fidelity end, many people also check [Butteraugli](/reference/metrics/butteraugli/).

SSIMULACRA2 is an image metric: on video it scores each frame independently and knows nothing about motion. Pair it with [XPSNR](/reference/metrics/xpsnr/) or [CVVDP](/reference/metrics/cvvdp/) if temporal artifacts matter, and watch the result.

## Score scale

Scores run from 100 (mathematically lossless) down without a lower limit; higher is better. The upstream README relates them to subjective quality like this:

| Score    | Upstream description                                                                                                  | Typical output of                          |
| -------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ |
| 100      | Mathematically lossless                                                                                               |                                            |
| 90       | Visually lossless: not noticeable by an average observer in a flicker test at 1:1 from a normal viewing distance     | `cjxl -d 0.5`, libjpeg-turbo 4:4:4 q95     |
| 85       | Excellent: not noticeable in an in-place comparison at 1:1                                                            | `cjxl -d 1`, libjpeg-turbo 4:4:4 q90       |
| 80       | Very high: not noticeable in a side-by-side comparison at 1:1                                                         | `cjxl -d 1.5`, libjpeg-turbo 4:2:2 q85     |
| 70       | High: MOS around 4 ("good"); artifacts perceptible but not annoying, barely noticeable side by side at 1:1            | `cjxl -d 2.5`, libjpeg-turbo 4:2:0 q70     |
| 50       | Medium: MOS around 3 ("fair"); slightly annoying artifacts                                                            | `cjxl -d 5`, libjpeg-turbo 4:2:0 q35       |
| 30       | Low: MOS around 2 ("poor"); obvious and annoying artifacts                                                            | `cjxl -d 9`, libjpeg-turbo 4:2:0 q20       |
| 10       | Very low: MOS close to 1 ("bad"); very obvious and annoying artifacts                                                 | `cjxl -d 14`, libjpeg-turbo 4:2:0 q14      |
| Negative | Extremely low quality, very strong distortion                                                                         |                                            |

The scale was calibrated on still images viewed at 1:1. For video it is a useful orientation, but compare encodes of the same clip with each other rather than aiming at a fixed number.

## Score images

Cloudinary's reference implementation ships as the `ssimulacra2` tool in the [libjxl release archives](https://github.com/libjxl/libjxl/releases) for Linux and Windows. Many distribution packages of libjxl leave it out. Give it the original first:

```sh
ssimulacra2 original.png distorted.png
```

It prints a single score. [fssimu2](https://github.com/gianni-rosato/fssimu2) is a faster, lower-memory derivative that also reads JPEG, WebP, AVIF and Y4M; its scores differ slightly from the reference implementation, so don't mix the two in one comparison.

## Score video

For video, compute a score for every frame. The common tools are:

- **[Vship](/tools/utilities/vship/)**: GPU implementation (CUDA, HIP or Vulkan). Its `FFVship` command decodes the files itself; the VapourSynth plugin works inside scripts.
- **[vszip](https://github.com/dnjulek/vapoursynth-zip)**: CPU VapourSynth plugin, installed with `pip install vapoursynth-vszip`.
- **[Av1an](/tools/utilities/av1an/)** uses either plugin for its SSIMULACRA2 target-quality mode.

With FFVship, SSIMULACRA2 is the default metric, and `--json` saves the per-frame scores:

```sh
FFVship --source reference.mkv --encoded distorted.mkv --json ssimu2.json
```

FFVship prints the mean, standard deviation, median, 5th and 95th percentiles, minimum and maximum. In a VapourSynth script run with `python` (not `vspipe`), the same summary looks like this:

```python
import vapoursynth as vs
core = vs.core

ref = core.bs.VideoSource("reference.mkv")
dist = core.bs.VideoSource("distorted.mkv")

clip = core.vship.SSIMULACRA2(ref, dist)
scores = sorted(f.props["_SSIMULACRA2"] for f in clip.frames())
n = len(scores)
print(f"mean {sum(scores) / n:.2f}  5th percentile {scores[n // 20]:.2f}  worst {scores[0]:.2f}")
```

For vszip, call `core.vszip.SSIMULACRA2(ref, dist)` instead and read the `SSIMULACRA2` property (no underscore).

Both inputs must have the same dimensions and frame count. FFVship resizes a mismatched encode on its own; in VapourSynth, scale it yourself, e.g. `dist = dist.resize.Bicubic(ref.width, ref.height)`. The tools convert YUV to RGB using the clip's matrix, transfer and primaries properties, so missing or wrong colour tags give wrong scores; FFVship guesses missing tags, and `--verbose` shows what it used.

## Summarise per-frame scores

A mean hides a short ugly section, and that section is what viewers notice. Report at least:

- the **mean**, for the overall level;
- a **low percentile** such as the 5th, for how bad the worse parts get;
- the **worst frames**, by number, so you can look at them.

A large standard deviation, or a low percentile far below the mean, points to specific scenes worth inspecting at normal speed and frame by frame. See [Compare encoding quality](/guides/compare-encoding-quality/) for the whole workflow.

## How it works

- Both images are converted to linear light and then to the XYB colour space used by JPEG XL.
- Three error maps are computed for each of the X, Y and B channels: an SSIM map, a ringing/blockiness map (edges in the distorted image where the original is smooth) and a smoothing/blur map (the reverse).
- Each map is computed at six scales, from 1:1 to 1:32, downscaling in linear light.
- For each of the 54 maps, the mean (1-norm) and the 4-norm are taken; the 4-norm emphasises localised errors.
- A weighted sum of those 108 values, tuned on the CID22, TID2013, KADID-10k and KonFiG-IQA datasets, is mapped to the final score.
