Butteraugli
37c3317fd058; modified for Encode Wiki · CC BY-SA 4.0Butteraugli is a psychovisual image difference metric from Google. It models how the eye’s colour receptors and early visual processing respond to an image, and outputs both a per-pixel difference map and a single distance: 0 for identical images, higher for more visible differences. It is maintained in libjxl, whose encoder uses it; the original standalone Google repository is archived.
What it is good for
Section titled “What it is good for”Butteraugli is designed for the high-fidelity range. Its authors describe it as reliable “in the domain of barely noticeable differences”, tuned mostly around JPEG quality 90–95, and say they don’t know how well it handles major distortions. libjxl’s distance setting is defined as a target maximum Butteraugli distance, with 1.0 described as visually lossless, which gives a feel for the scale.
That makes it a good check when you are comparing high-quality or near-transparent encodes, alongside SSIMULACRA2, which covers a wider quality range. The correlation tables in the SSIMULACRA2 README show the split: on the near-threshold KonFiG-IQA set the Butteraugli 3-norm is among the best metrics, while on TID2013 and KADID-10k, which are full of heavy distortions, it does no better than PSNR. It is strict: Vship’s documentation notes that it rarely calls a frame good when it isn’t, but it does flag differences a typical viewer wouldn’t see. Like SSIMULACRA2 it is an image metric, so on video it scores frames independently.
Distance and norms
Section titled “Distance and norms”The difference map has a value for every pixel, so a single score needs a pooling rule. Always say which one you report.
| Norm | What it measures | Where you see it |
|---|---|---|
| Max (∞-norm) | The single worst spot in the image | First line of libjxl’s butteraugli_main; Vship _BUTTERAUGLI_INFNorm |
| 3-norm | A mean over the whole map that weights larger errors more heavily | Second line of butteraugli_main (default --pnorm 3); Vship _BUTTERAUGLI_3Norm |
| q-norm | Any other p-norm you choose | butteraugli_main --pnorm; Vship qnorm (default 2), _BUTTERAUGLI_QNorm |
The max norm is the classic Butteraugli score, but one bad block decides it, so it is noisy for comparing encodes. The 3-norm is the figure commonly used for comparisons, including the correlation tables in the SSIMULACRA2 README and Av1an’s butteraugli-3 target.
Butteraugli also assumes a display brightness, the intensity target in nits. A brighter display makes artifacts more visible and raises the distance. libjxl’s tool uses 80 nits for SDR input by default, while Vship uses 203 nits, so the two give different numbers for the same images unless you set the same value.
Score images
Section titled “Score images”butteraugli_main is included in the libjxl release archives for Linux and Windows, next to ssimulacra2:
butteraugli_main reference.png distorted.png --distmap diff.pngIt prints the max-norm distance, then the 3-norm. --distmap writes a heatmap of where the differences are; --pnorm and --intensity_target change the pooling and display brightness. Images without an embedded colour profile are read as sRGB.
Score video
Section titled “Score video”Butteraugli is slow on a CPU, so for video most people use Vship on a GPU. With FFVship:
FFVship --source reference.mkv --encoded distorted.mkv -m Butteraugli --json butteraugli.jsonFFVship prints the mean, percentiles, minimum and maximum for the 2-norm, 3-norm and max norm. The JSON holds [q-norm, 3-norm, max-norm] for every frame. --qnorm and --intensity-target change the extra norm and the display brightness.
In VapourSynth, run with python:
import vapoursynth as vscore = vs.core
ref = core.bs.VideoSource("reference.mkv")dist = core.bs.VideoSource("distorted.mkv")
clip = core.vship.BUTTERAUGLI(ref, dist)d = sorted(f.props["_BUTTERAUGLI_3Norm"] for f in clip.frames())print(f"3-norm mean {sum(d) / len(d):.3f} 95th percentile {d[len(d) * 19 // 20]:.3f} worst {d[-1]:.3f}")Lower is better here, so the bad tail is the high end: report the mean, a high percentile and the worst frames. With distmap=1, core.vship.BUTTERAUGLI(ref, dist, distmap=1) returns a greyscale clip of the difference map that you can step through in a previewer to see where an encode falls apart. The old _BUTTERAUGLI_2Norm property is a deprecated alias of _BUTTERAUGLI_QNorm.
Both clips must have the same dimensions and frame count; scale the encode to the reference size first if needed. See Compare encoding quality for the rest of the workflow.