Skip to content

XPSNR

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

XPSNR (extended perceptually weighted peak signal-to-noise ratio) is a full-reference video metric from Fraunhofer HHI. It computes PSNR block by block and weights each block by the reference’s local spatial activity and, for video, its temporal activity. Errors in busy or fast-moving areas, where they are hard to see, count for less; errors in flat, static areas count for more. Scores are in dB, per plane, like PSNR.

XPSNR keeps PSNR’s low cost but correlates better with how video looks, and unlike image metrics such as SSIMULACRA2 it takes motion into account. That makes it a good first pass over many test encodes and a useful companion to SSIMULACRA2 and Butteraugli: the metrics have different blind spots, so agreement between them means more than any one score.

It is still a PSNR derivative. Scores depend heavily on the content, so there is no fixed “good” value; compare encodes of the same clip with each other.

The xpsnr filter has been built into FFmpeg since 7.1; the Fraunhofer patch for FFmpeg 7.0 is no longer needed or maintained.

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

The summary looks like XPSNR y: 29.5228 u: 30.0330 v: 29.9182 (minimum: 29.5228), and xpsnr.log holds the per-frame values for each plane.

  • Both inputs need the same dimensions. Scale the encode to the reference size if needed: -lavfi "[1:v]scale=1920:1080:flags=bicubic[d];[0:v][d]xpsnr".
  • The bit depths may differ. Other pixel-format differences are converted automatically; the filter is designed for YCbCr input.
  • As with the other FFmpeg metric filters, check that frame counts match, because FFmpeg repeats the last frame of a shorter input. Add setpts=PTS-STARTPTS to both inputs only if one starts at a non-zero timestamp.

The XPSNR authors recommend reporting the minimum of the per-plane averages, which FFmpeg prints as minimum. Some community tools, such as Av1an’s xpsnr-weighted target, instead combine the planes with luma weighted 4:1:1 against the two chroma planes. That gives a different number, so say which one you report and don’t mix them.

The printed averages hide short bad sections. Use the per-frame log to find the lowest-scoring frames and look at them; see Compare encoding quality.

vszip (pip install vapoursynth-vszip) has an XPSNR filter that matches FFmpeg’s numbers. It also takes the reference first and stores per-frame scores in the XPSNR_Y, XPSNR_U and XPSNR_V frame properties:

clip = core.vszip.XPSNR(reference, distorted)