写在前面

psnr作为图像质量评价指标,在很多图像领域如图像超分辨率、图像压缩、图像去噪等都有广泛的应用。

PSNR(峰值信噪比)

简介

Peak signal-to-noise ratio(简称PSNR)是一个工程术语,表示信号的最大可能功率与影响信号表示精度的干扰噪声功率之间的比值。由于许多信号都有非常宽的动态范围,峰值讯噪比常用对数分贝单位来表示。

定义

它常简单地通过均方误差(MSE)进行定义。两个 m × n m×n m×n 单色图像 I I I K K K I I I 为一无噪声的原始图像, K K K I I I 的噪声近似(例: I I I 为未压缩的原始图像, K K K I I I 经过压缩后的图像),那么它们的的均方误差定义为:
M S E = 1 m n ∑ i = 0 m − 1 ∑ j = 0 n − 1 [ I ( i , j ) − K ( i , j ) ] 2 {\displaystyle {\mathit {MSE}}={\frac {1}{mn}}\sum _{i=0}^{m-1}\sum _{j=0}^{n-1}[I(i,j)-K(i,j)]^{2}} MSE=mn1i=0m1j=0n1[I(i,j)K(i,j)]2
峰值信噪比定义为:
P S N R = 10 ⋅ log ⁡ 10 ( M A X I 2 M S E ) = 20 ⋅ log ⁡ 10 ( M A X I M S E ) {\displaystyle {\mathit {PSNR}}=10\cdot \log _{10}\left({\frac {{\mathit {MAX}}_{I}^{2}}{\mathit {MSE}}}\right)=20\cdot \log _{10}\left({\frac {{\mathit {MAX}}_{I}}{\sqrt {\mathit {MSE}}}}\right)} PSNR=10log10(MSEMAXI2)=20log10(MSE MAXI)
其中, M A X I MAX_{I} MAXI 是表示图像点颜色的最大数值,如果每个采样点用 8 位表示(例:影像处理),那么就是 255。更为通用的表示是,如果每个采样点用 B 位线性脉冲编码调制表示,那么 M A X I MAX_{I} MAXI 就是:
2 B − 1 {\displaystyle 2^{B}-1} 2B1
对于每点有RGB三个值的彩色图像来说,峰值信噪比的定义类似。除了横轴、纵轴 m m m n n n 以外,还要考虑它的颜色组成RGB。我们需要分别对每个颜色处理其MSE,因为有3个颜色通道,所以MSE需再除以3。

彩色图像的峰值讯噪比定义为:
P S N R = 10 ⋅ log ⁡ 10 ( M A X I 2 1 3 m n ∑ R , G , B ∑ i = 0 m − 1 ∑ j = 0 n − 1 [ I c o l o r ( i , j ) − K c o l o r ( i , j ) ] 2 ) {\displaystyle {\mathit {PSNR}}=10\cdot \log _{10}\left({\frac {{\mathit {MAX}}_{I}^{2}}{{\frac {1}{3mn}}\sum _{R,G,B}^{}\sum _{i=0}^{m-1}\sum _{j=0}^{n-1}[I_{color}(i,j)-K_{color}(i,j)]^{2}}}\right)} PSNR=10log10(3mn1R,G,Bi=0m1j=0n1[Icolor(i,j)Kcolor(i,j)]2MAXI2)

实现

这是我一开始写的代码,两个地方有点问题,也是大家常会犯的错误。

from PIL import Image
import numpy as np

img1 = np.array(Image.open('original.jpg'))
img2 = np.array(Image.open('compress.jpg'))


def psnr(img1, img2):
    mse = np.mean((img1-img2)**2)
    if mse == 0:
        return 100
    else:
        return 20*np.log10(255/np.sqrt(mse))


if __name__ == "__main__":
    print(psnr(img1, img2))

1、如果mse == 0不应该返回100,而是正无穷,正无穷在python中用float('inf')表示。(参考维基英文百科:In the absence of noise, the two images I I I and K K K are identical, and thus the MSE is zero. In this case the PSNR is infinite.)

2、数据类型的问题。img1img2原本是np.int8类型,应该转换为np.float64再参与计算,否则会导致精度丢失,计算psnr值偏大。(这里参考了psnr百科词条中的matlab代码实现,代码中对图片进行了double操作,学过matlab的同学应该清楚,这就是将无符号int8类型转换为双精度浮点型

修改后的代码:

from PIL import Image
import numpy as np

img1 = np.array(Image.open('original.jpg')).astype(np.float64)
img2 = np.array(Image.open('compress.jpg')).astype(np.float64)


def psnr(img1, img2):
    mse = np.mean((img1-img2)**2)
    if mse == 0:
        return float('inf')
    else:
        return 20*np.log10(255/np.sqrt(mse))


if __name__ == "__main__":
    print(psnr(img1, img2))

如果你不想自己写,也可以调用第三方库skimage实现,里面有封装好的计算psnr的代码,和我修改后的代码计算结果是一模一样的,具体调用方式如下:

from skimage.metrics import peak_signal_noise_ratio as psnr
from PIL import Image
import numpy as np


img1 = np.array(Image.open('original.jpg'))
img2 = np.array(Image.open('compress.jpg'))


if __name__ == "__main__":
    print(psnr(img1, img2))

备注:skimage的大名叫scikit-image,安装请用pip install scikit-image

意义

  • PSNR接近 50dB ,代表压缩后的图像仅有些许非常小的误差。
  • PSNR大于 30dB ,人眼很难查觉压缩后和原始影像的差异。
  • PSNR介于 20dB 到 30dB 之间,人眼就可以察觉出图像的差异。
  • PSNR介于 10dB 到 20dB 之间,人眼还是可以用肉眼看出这个图像原始的结构,且直观上会判断两张图像不存在很大的差异。
  • PSNR低于 10dB,人类很难用肉眼去判断两个图像是否为相同,一个图像是否为另一个图像的压缩结果。
同压缩比例的PSNR比较(点击图片可放大)图片为台湾桃园国际机场停机坪
未压缩的原图PSNR 47.61dBPSNR 34.02dBPSNR 24.46dB

拓展阅读

psnr的matlab实现:

function PSNR = psnr(f1, f2)
%计算两幅图像的峰值信噪比
k = 8;
%k为图像是表示地个像素点所用的二进制位数,即位深。
fmax = 2.^k - 1;
a = fmax.^2;
MSE =(double(im2uint8(f1)) -double( im2uint8(f2))).^2;
b = mean(mean(MSE));
PSNR = 10*log10(a/b);

相关推荐

【python】ssim原理简介及代码实现

引用参考

https://zh.wikipedia.org/wiki/峰值信噪比
https://en.wikipedia.org/wiki/Peak_signal-to-noise_ratio
https://baike.baidu.com/item/psnr/2925132

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐