Skip to content

PSNR

currentMigrated from docs/metrics/PSNR.mdx at 37c3317fd058; modified for Encode Wiki · CC BY-SA 4.0

PSNR (peak signal-to-noise ratio) is the mean squared error (MSE) between a reference and a distorted image, expressed in decibels relative to the largest possible sample value: 10 × log10(MAX² / MSE). Higher is better, and identical inputs score infinity. It is cheap and exact, which makes it the standard yardstick for codec development, but it rewards blur and ignores how the eye works, so it can’t tell you which of two encodes looks better.

What PSNR is good for, and what it isn’t

Section titled “What PSNR is good for, and what it isn’t”

PSNR is the classic metric for regression tests, conformance work and BD-rate figures in codec papers. It is also a good pipeline check: a PSNR far below what you expect usually means misaligned frames, an unintended scale, or a wrong colour range or matrix rather than a bad encode.

It is a poor judge of how an encode looks. Squared error treats every difference the same, whether it sits in flat sky or in busy foliage where nobody would notice it. A smooth approximation of grain or fine texture has lower squared error than a sharp reproduction that doesn’t match the source pixel for pixel, so blur and denoising raise PSNR while the detail that psychovisual tuning keeps lowers it. On Cloudinary’s CID22 image dataset, PSNR-Y’s rank correlation with viewer scores is 0.62, against 0.88 for SSIMULACRA2.

This is why encodes tuned for PSNR (x264 and x265 --tune psnr, SVT-AV1 --tune 1, which is its default) look worse than psychovisually tuned encodes that score lower. x264 warns that --psnr results “will be invalid” when psy optimisations are on. Don’t use PSNR to compare encoders or settings that use psychovisual tuning, or to decide whether an encode looks good enough.

For perceptual comparisons use SSIMULACRA2, Butteraugli, XPSNR or CVVDP, and watch the video. See Compare encoding quality for the full method. The reasoning behind psy tuning is on the psychovisual page.

Terminal window
ffmpeg -i distorted.mkv -i reference.mkv -lavfi psnr=stats_file=psnr.log -f null -

FFmpeg prints a summary at the end: PSNR per plane (y, u, v), an average over all planes, and the min and max frame. psnr.log has one line per frame with the MSE and PSNR of each plane.

  • PSNR is symmetric, but keep the distorted file first and the reference second. That is the order ssim and libvmaf expect too; XPSNR is the exception.
  • Both inputs must have the same dimensions. If the encode was downscaled, scale it back to the reference size first: -lavfi "[0:v]scale=1920:1080:flags=bicubic[d];[d][1:v]psnr".
  • A mismatched pixel format, such as a 10-bit encode of an 8-bit source, is converted automatically.
  • Only if one file starts at a non-zero timestamp (for example a clip cut from a longer stream), add setpts=PTS-STARTPTS to both inputs before the psnr filter.
  • Check that both files have the same frame count. FFmpeg won’t complain if they don’t: it repeats the last frame of the shorter input and keeps scoring until the longer one ends.

FFmpeg’s per-plane and average summary figures are computed from the MSE averaged over all frames, then converted to dB. That differs slightly from averaging per-frame dB values, which some tools do, so compare numbers produced the same way. One identical frame does not make the summary infinite.

Codec papers usually quote luma PSNR (y). FFmpeg’s average weights the planes by pixel count, so with 4:2:0 luma makes up two-thirds of it.

libvmaf’s psnr feature caps scores at 60 dB for 8-bit and 72 dB for 10-bit input, while FFmpeg’s filter does not, so the two disagree for near-lossless encodes.