Evaluation & Metrics
Peak Signal-to-Noise Ratio
The mean squared error between an image and its reference expressed in decibels relative to the largest possible pixel value; how the peak value, colour channels, border cropping and averaging over a test set change the number, and why a higher PSNR does not always mean a better-looking image.
beginner
The peak signal-to-noise ratio (PSNR) compares an image with a reference image of the same size by taking their mean squared error and expressing it in decibels relative to the largest value a pixel can take. It is the standard score of image restoration and reconstruction: denoising, super-resolution, compression, inpainting and rendered views in novel view synthesis are all routinely reported in PSNR. Higher is better, and identical images have an infinite PSNR. Because it is a fixed function of the MSE, PSNR inherits all of the MSE’s blind spots; what it adds is a scale on which the peak value, the colour channels and the averaging over a test set must be stated before two numbers can be compared.
Formula
For a reference image and a test image with the same values, and the largest possible intensity,
measured in decibels (dB). Wang and Bovik (2009) give this definition with , the dynamic range of allowable intensities, in place of , and note that PSNR helps when images with different dynamic ranges are compared but otherwise carries no information beyond the MSE.
The peak value
is the largest value the data format allows, not the largest value in the image:
- 8-bit images in : , the example Wang and Bovik give.
- Floating-point images in : .
- Higher bit depths: for bits, so 1023 for 10-bit and 4095 for 12-bit data (bit depth is covered in digital image). A 12-bit image stored in a 16-bit container still has .
- Images in , a range used by some generative models: the range has width 2, so and .
The same pair of images gives the same PSNR in with and in with , because the MSE and scale together. That invariance is the reason to use PSNR rather than the raw MSE, whose value changes by a factor of 65,025 between the two ranges. A mismatched peak shifts every score by a constant: computing data with adds dB.
Rules of thumb that are arithmetic
Three consequences of the logarithm (our own arithmetic, not taken from a source):
- Halving the MSE adds dB, and halving the RMSE adds about 6.02 dB. A 1 dB gain is a reduction of the MSE by about 21%, and 0.1 dB by about 2.3%.
- Additive noise. If with zero-mean noise of standard deviation , independent across pixels, the expected MSE is and . A denoising input with on the 8-bit scale therefore starts near 20.17 dB. The relation breaks when the noisy image is clipped to the valid range or rounded to integers, as the example below shows; the noise models themselves are in image noise.
- Identical images have and . Code that averages PSNR over a set must decide what to do with such images, since a single infinite value makes the mean infinite; the options include excluding such images, capping the value or adding a small constant to the MSE, and a paper should say which.
Colour images
For an RGB image the common definition averages the squared error over all three channels, values, and applies the formula once; this equals PSNR computed from the mean of the per-channel MSEs, not the mean of per-channel PSNRs. Super-resolution papers instead often evaluate only the luma channel of YCbCr. SRCNN follows earlier work in super-resolving and evaluating the Y channel only, upscaling the chroma channels Cb and Cr by bicubic interpolation (Dong et al., 2016, §4 and §4.5), and EDSR reports its benchmark PSNR on Y as well. ITU-R BT.601 defines luma from gamma-encoded in as (§2.5.1), and its 8-bit digital form scales this to the studio range 16 to 235 before rounding (§2.5.3):
The papers cited here say “Y channel” without printing the conversion (EDSR adds that it used MATLAB functions), so which variant of the conversion was used, studio range or full range, is a detail to state in any new evaluation. It matters slightly: the studio-range Y spans 219 steps rather than 255, so with the luma error between the same two RGB images scores dB higher than in full-range luma (arithmetic). The colour space conventions themselves are in color spaces.
Y-only and RGB PSNR can differ by several dB for the same images. In SRCNN’s colour experiment on Set5 at ×3 (Table 5), bicubic interpolation scores 30.39 dB on Y, about 45 dB on each chroma channel and 34.57 dB on the RGB image. The paper does not say how the RGB value was computed, and by our arithmetic the channels alone cannot explain the gap: an error confined to luma appears at full size in each of R, G and B, and chroma errors of about 45 dB are too small to cancel it, so the two columns must also differ in other details of the evaluation.
Border cropping. Super-resolution benchmarks also discard pixels near the image border before computing PSNR. For its benchmark table EDSR measured PSNR and SSIM on Y and ignored as many border pixels as the scale factor, while for its DIV2K ablations it used all RGB channels and ignored border pixels (Lim et al., 2017, §4.4 and §4.5). Numbers produced under different protocols, even within one paper, are not comparable.
Averaging over a test set
Benchmark tables report one PSNR per method and dataset, and there are two ways to get it:
- Mean of per-image PSNRs, , the “average PSNR” of super-resolution tables (SRCNN reports per-image values in its supplementary material and averages in its tables).
- PSNR of the mean MSE, .
They differ whenever the images have different MSEs. Averaging logarithms is the logarithm of the geometric mean, so the mean of per-image PSNRs equals the PSNR of the geometric mean of the MSEs; since a geometric mean never exceeds the arithmetic mean, the mean of PSNRs is always at least the PSNR of the mean MSE, with equality only when all MSEs are equal (our derivation, the AM–GM inequality). The mean of PSNRs gives easy images with tiny MSEs a large weight in dB; the PSNR of the mean MSE is dominated by the worst images. The same distinction for RMSE is discussed in mean squared error.
Intuition
The name comes from signal processing: the ratio of the power of the largest possible signal, , to the power of the error, treated as noise. The decibel scale turns multiplicative improvements into additive ones, which suits error values spanning several orders of magnitude: a crude reconstruction and a nearly perfect one might differ by a factor of 1,000 in MSE, which is 30 dB.
Because it is a decreasing function of the MSE, PSNR ranks two results on the same reference exactly as the MSE does, and it rewards what the MSE rewards, including the smooth conditional-mean predictions discussed in mean squared error.
Interpretation
PSNR ranges from in principle to for identical images; with the peak correctly set it is at least 0 dB for images in the valid range, since the MSE cannot exceed . Higher means a smaller pixel error.
There is no universal threshold for a good PSNR, because the value depends on image content as much as on the method: textured images are harder to reconstruct than flat ones. Numbers are meaningful as comparisons between methods on the same images under the same protocol. As one sourced example of the scale, EDSR’s benchmark table (Lim et al., 2017, Table 3, Y channel with the scale factor cropped from the border) lists bicubic upsampling on Set5 at ×4 at 28.42 dB and EDSR at 32.46 dB, with the neighbouring methods in the table separated by fractions of a dB. A difference of 0.1 dB, about 2% in MSE, can be within the spread caused by evaluation details such as the border, the luma conversion or rounding the output to 8 bits; whether such differences are statistically meaningful is the subject of statistical significance in evaluation.
When to Use It
PSNR fits any task with a pixel-aligned reference image:
- Compression and other signal processing, where, as Wang and Bovik (2009) note, competing algorithms have most often been compared by MSE or PSNR.
- Image denoising, where the noisy input’s PSNR is set by the noise level, as derived above.
- Super-resolution, under the Y-channel and border conventions above.
- Inpainting, where the known pixels outside the hole are copied unchanged, so including them in the MSE raises the score with the image area outside the hole (our reasoning); whether the PSNR covers the whole image or the hole only should be stated.
- Novel view synthesis and neural radiance fields, where held-out photographs are compared with renderings at the same poses, together with SSIM and LPIPS (see the neural rendering hub).
It is a good choice when the error really is close to independent noise, when the goal is fidelity to the reference rather than appearance, and as a sanity check alongside perceptual metrics. It is a poor sole criterion when outputs can be plausible without matching the reference pixel for pixel.
Failure Cases
Perceptual mismatch. PSNR inherits the MSE’s indifference to where and how errors occur, so Wang and Bovik’s (2009) equal-MSE demonstration, described in mean squared error, applies to it unchanged. The structural similarity index (SSIM) and learned metrics such as LPIPS were developed to address it.
The perception–distortion tradeoff. Blau and Michaeli (2018) prove that, for a restoration method, the best achievable perceptual quality (measured as a divergence between the distributions of restored and natural images) cannot improve as the mean distortion decreases: if the divergence is convex in its second argument, the perception-distortion function, the smallest divergence attainable at a given distortion, is monotonically non-increasing and convex (their Theorem 1), and the authors note that it is non-increasing even without that assumption. The result holds for any distortion measure, not only for MSE or SSIM, although in their experiments it is less severe for distances between deep features. Near that boundary, pushing distortion down costs realism. The authors open from the observation that restoration methods with better perceptual quality are often worse in PSNR and SSIM, which their result explains without blaming the metrics alone; image-to-image translation discusses the same effect for per-pixel metrics in general.
Misalignment and invented detail. A one-pixel shift, a slight change in colour balance or a plausible but different texture lowers PSNR heavily, and so does any task with several valid answers.
Wrong protocol. A mismatched shifts all numbers by a constant, while RGB versus Y, cropped versus uncropped, and the choice of dataset average change them by amounts that vary by image. Nothing in the number reveals the mistake, and the results silently become incomparable with published tables.
Example
The example uses a synthetic 128 × 128 RGB image with values in : a colour ramp, a red disk, a blue square and fine grey stripes. It checks the noise relation over seeds 0 to 4, with and without clipping and rounding to uint8; the effect of the peak value, including scikit-image’s defaults; RGB versus luma PSNR for two distortions with the same RGB MSE (a luma-preserving 30% loss of saturation and grey noise) and for a ×4 downsampling followed by cubic-spline upsampling; and the two dataset averages over five noisy images. scikit-image 0.25.2 is used only to cross-check the NumPy function.
import numpy as np
from scipy.ndimage import zoom
from skimage.metrics import peak_signal_noise_ratio # scikit-image 0.25.2, cross-check only
def psnr(ref, test, peak):
"""PSNR in dB; both images on the same scale, peak = largest possible value."""
mse = np.mean((np.asarray(ref, float) - np.asarray(test, float)) ** 2)
return 10 * np.log10(peak ** 2 / mse) if mse > 0 else np.inf
def luma(rgb):
"""BT.601 luma, 8-bit studio range: Y = 16 + 219 * (0.299 R' + 0.587 G' + 0.114 B')."""
return 16 + 219 * (rgb / 255) @ np.array([0.299, 0.587, 0.114])
# A 128x128 RGB test image with values in [0, 255]: a colour ramp, a red disk,
# a blue square and a patch of fine grey stripes.
yy, xx = np.mgrid[0:128, 0:128].astype(float)
img = np.stack([40 + 150 * xx / 127, 60 + 120 * yy / 127, np.full_like(xx, 110)], -1)
img[(yy - 40) ** 2 + (xx - 40) ** 2 < 22 ** 2] = [220, 40, 40]
img[80:115, 15:50] = [30, 60, 200]
img[70:120, 70:120] = 128 + 60 * np.sin(2 * np.pi * xx[70:120, 70:120] / 6)[..., None]
ref8 = np.round(img).astype(np.uint8)
ref = ref8.astype(float)
# 1. Additive Gaussian noise: PSNR = 20 log10(255 / sigma), until clipping bites.
print("sigma 20log10(255/s) float, no clipping uint8 (clipped, rounded) seeds 0-4")
for s in [2, 5, 10, 25, 50]:
raw, u8 = [], []
for seed in range(5):
noisy = ref + s * np.random.default_rng(seed).standard_normal(ref.shape)
raw.append(psnr(ref, noisy, 255))
u8.append(psnr(ref, np.clip(np.round(noisy), 0, 255), 255))
print(f"{s:5d} {20 * np.log10(255 / s):13.2f} {min(raw):10.2f}-{max(raw):.2f} "
f"{min(u8):14.2f}-{max(u8):.2f}")
# 2. The peak must match the data range.
noisy8 = np.clip(np.round(ref + 10 * np.random.default_rng(0).standard_normal(ref.shape)),
0, 255).astype(np.uint8)
r1, n1 = ref8 / 255, noisy8 / 255 # same images as floats in [0, 1]
r12, n12 = [np.round(a / 255 * 4095).astype(np.uint16) for a in (ref8, noisy8)] # 12-bit data
print(f"\n[0, 255], MAX 255: {psnr(ref8, noisy8, 255):.2f} dB "
f"skimage uint8 default: {peak_signal_noise_ratio(ref8, noisy8):.2f}")
print(f"[0, 1], MAX 1: {psnr(r1, n1, 1):.2f} dB "
f"skimage float default: {peak_signal_noise_ratio(r1, n1):.2f}")
print(f"[0, 1], MAX 255: {psnr(r1, n1, 255):.2f} dB (+20 log10 255 = +{20 * np.log10(255):.2f})")
print(f"12-bit in uint16: skimage default {peak_signal_noise_ratio(r12, n12):.2f} dB, "
f"data_range=4095: {peak_signal_noise_ratio(r12, n12, data_range=4095):.2f}")
try:
peak_signal_noise_ratio(ref, noisy8.astype(float)) # floats holding 0..255
except ValueError as e:
print("skimage, float array in [0, 255]: ValueError:", str(e)[:60] + "...")
with np.errstate(divide="ignore"):
print("identical images, skimage:", peak_signal_noise_ratio(ref8, ref8))
# 3. RGB versus luma: two distortions with the same RGB MSE, and x4 super-resolution.
lum = (ref @ np.array([0.299, 0.587, 0.114]))[..., None] # full-range luma
chroma = lum + 0.7 * (ref - lum) # 30% less saturation, same luma
g = np.random.default_rng(1).standard_normal(ref.shape[:2])[..., None]
gray_noise = ref + np.sqrt(np.mean((chroma - ref) ** 2)) * g / g.std() # same RGB MSE, luma only
small = ref.reshape(32, 4, 32, 4, 3).mean(axis=(1, 3)) # x4 downsampling by box averaging
up = zoom(small, (4, 4, 1), order=3) # cubic-spline upsampling back to 128
print("\ndistortion RGB PSNR Y PSNR Y PSNR, 4-pixel border removed")
for name, d in [("saturation -30%", chroma), ("grey noise, same MSE", gray_noise),
("x4 down, cubic up", up)]:
yr, yd = luma(ref), luma(d)
print(f"{name:24s} {psnr(ref, d, 255):9.2f} {psnr(yr, yd, 255):7.2f} "
f"{psnr(yr[4:-4, 4:-4], yd[4:-4, 4:-4], 255):9.2f}")
# 4. Averaging over a set: mean of per-image PSNRs vs PSNR of the mean MSE.
rng = np.random.default_rng(2)
sigmas = [2, 5, 10, 20, 40] # five test images with different noise levels
mses = [np.mean((ref - (ref + s * rng.standard_normal(ref.shape))) ** 2) for s in sigmas]
per_image = [10 * np.log10(255 ** 2 / m) for m in mses]
print("\nper-image PSNR:", " ".join(f"{p:.2f}" for p in per_image))
print(f"mean of PSNRs {np.mean(per_image):.2f} dB, PSNR of mean MSE "
f"{10 * np.log10(255 ** 2 / np.mean(mses)):.2f} dB, "
f"PSNR of geometric-mean MSE {10 * np.log10(255 ** 2 / np.exp(np.mean(np.log(mses)))):.2f} dB")
Output (NumPy 2.3.5, SciPy 1.16.3, scikit-image 0.25.2):
sigma 20log10(255/s) float, no clipping uint8 (clipped, rounded) seeds 0-4
2 42.11 42.10-42.15 42.01-42.07
5 34.15 34.14-34.19 34.13-34.18
10 28.13 28.12-28.17 28.12-28.17
25 20.17 20.16-20.21 20.24-20.30
50 14.15 14.14-14.19 14.63-14.69
[0, 255], MAX 255: 28.12 dB skimage uint8 default: 28.12
[0, 1], MAX 1: 28.12 dB skimage float default: 28.12
[0, 1], MAX 255: 76.25 dB (+20 log10 255 = +48.13)
12-bit in uint16: skimage default 52.20 dB, data_range=4095: 28.12
skimage, float array in [0, 255]: ValueError: image_true has intensity values outside the range expected f...
identical images, skimage: inf
distortion RGB PSNR Y PSNR Y PSNR, 4-pixel border removed
saturation -30% 26.14 325.39 325.39
grey noise, same MSE 26.14 27.46 27.44
x4 down, cubic up 22.20 24.18 23.62
per-image PSNR: 42.13 34.14 28.12 22.08 16.15
mean of PSNRs 28.52 dB, PSNR of mean MSE 21.88 dB, PSNR of geometric-mean MSE 28.52 dB
Noise. Without clipping, the measured PSNR stays within about 0.05 dB of for every noise level and seed. Storing the noisy image as uint8 changes this at both ends. At rounding adds its own error, of variance about for a uniform rounding error, which lowers the PSNR by roughly 0.1 dB. At and especially , values pushed below 0 or above 255 are clipped back, which shrinks the error, so at the clipped image scores about 0.5 dB higher than the formula predicts. The noise level implied by a reported PSNR is only approximate once the data are clipped.
Peak value. The PSNR is identical in and when the peak follows the range, and scikit-image’s default peak is right for uint8 and for floats in . A wrong peak adds a constant: for data scored with 255, and 24.08 dB () for 12-bit data in a uint16 array, where the default takes the peak from the container type (see Implementations below).
Colour. The desaturation and the grey noise have the same RGB PSNR, but the desaturation leaves luma unchanged, so its Y PSNR is mathematically infinite; the 325 dB printed comes from floating-point rounding residuals of about in the luma values, while grey noise is fully visible in Y. Y-channel PSNR is blind to pure colour errors. The grey noise scores 1.32 dB more on Y than on RGB, the studio-range factor derived above, since its error is the same in all channels. For the ×4 reconstruction, Y and RGB PSNR differ by about 2 dB, and cropping 4 border pixels changes the Y value by about 0.6 dB; here it lowers it, because the removed border of this image is smooth ramp, which interpolation handles well. Where a reconstruction is worse near the border, for example because of padding, cropping raises the score instead.
Averaging. The mean of the per-image PSNRs equals the PSNR of the geometric-mean MSE, as the derivation above requires, and lies about 6.6 dB above the PSNR of the arithmetic-mean MSE, which the noisiest image dominates. Both are legitimate summaries; they are not interchangeable.
Variants
- Y-PSNR and RGB-PSNR, the luma-only and all-channel forms above; papers should name the one they report and the luma conversion.
- Cropped and masked PSNR. The MSE is computed over a subset of pixels: the interior after removing a border, the hole in inpainting, or valid foreground pixels. The CO3D benchmark, for example, scores PSNR on foreground pixels only (see novel view synthesis).
- Weighted PSNR (WPSNR) weights the squared error to reflect visual sensitivity; SRCNN reports it among its additional metrics without defining it (Dong et al., 2016, §4.4).
Implementations
In scikit-image 0.25.2, skimage.metrics.peak_signal_noise_ratio(image_true, image_test, data_range=None) computes , with the data_range, over all values of the arrays, so a colour image is averaged over its channels. When data_range is omitted, it is inferred from the data type of image_true: 255 for uint8, 65535 for uint16, 1 for floats with no negative values, and 2 for floats containing negative values. If image_true lies outside the range expected for its type, for example a float array above 1, it raises a ValueError and asks for data_range. Identical images return inf, with NumPy’s divide-by-zero warning. Passing data_range explicitly avoids both pitfalls and is the safer habit for any data that do not fill their container type. The function has no Y-channel or border options, so those conventions are applied before calling it.
Related Metrics
- Mean squared error carries the same information for a fixed peak; RMSE and MAE are its common companions.
- The structural similarity index (SSIM) compares local means, variances and correlations, and responds to error structure that PSNR ignores; it is usually reported next to PSNR.
- LPIPS compares deep-network features and, in its authors’ tests, agreed with human similarity judgments more often than per-pixel distances (figures in novel view synthesis); view synthesis papers report PSNR, SSIM and LPIPS together.
- Fréchet Inception Distance compares distributions of images and needs no pixel-aligned reference, for outputs that are plausible rather than faithful.
Related
- Image Denoising
Estimating a clean image from a noisy observation, removing random variation while keeping edges, texture, and fine detail.
- Image Noise
The random variation in recorded pixel values that does not come from the scene, its physical sources in the camera, and how it is modeled and measured.
- Color Spaces
A color space assigns numbers to colors; choosing and converting between RGB, CIE XYZ, CIELAB, YCbCr, and HSV correctly matters for both image processing and learning.
- Neural Rendering
Image synthesis that combines learned components with the physics of rendering, from 2D networks that turn rasterized features into photographs to 3D scene representations fitted to photographs through a differentiable renderer; the main families, why gradients through rendering matter, and how results are measured.
- Novel View Synthesis
Rendering a scene from a camera that took no picture of it, given images and usually their camera poses; the variants of the problem, the families of methods from image warping to radiance fields and diffusion models, the benchmarks, and why the results are hard to measure.
- Image-to-Image Translation
The task of mapping an image from one domain to a corresponding image in another, such as label maps to photos, maps to aerial views or day to night; paired and unpaired training, pix2pix, CycleGAN and their successors, semantic image synthesis, diffusion-based translation, and why per-pixel losses and metrics favour blurry averages.
References
- Wang, Z. & Bovik, A. C. (2009). Mean Squared Error: Love It or Leave It? A New Look at Signal Fidelity Measures. IEEE Signal Processing Magazine, 26(1), 98–117.
- Dong, C., Loy, C. C., He, K. & Tang, X. (2016). Image Super-Resolution Using Deep Convolutional Networks. IEEE Transactions on Pattern Analysis and Machine Intelligence, 38(2), 295–307.
- Lim, B., Son, S., Kim, H., Nah, S. & Lee, K. M. (2017). Enhanced Deep Residual Networks for Single Image Super-Resolution. IEEE Conference on Computer Vision and Pattern Recognition Workshops (CVPRW), 1132–1140.
- Blau, Y. & Michaeli, T. (2018). The Perception-Distortion Tradeoff. IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR), 6228–6237.
- ITU-R (2011). Recommendation ITU-R BT.601-7: Studio Encoding Parameters of Digital Television for Standard 4:3 and Wide-Screen 16:9 Aspect Ratios.
- scikit-image developers (2025). skimage.metrics.peak_signal_noise_ratio, scikit-image 0.25.2 API reference.