0、直接使用
单通道图片计算指标代码看2.2
三通道图片计算指标代码看2.3
1、PSNR,SSIM的知识点讲解、原理分析
1.1 PSNR
Peak Signal-to-Noise Ratio 峰值信噪比 单位为 d B dB dB
给定一个大小为
m
×
n
m \times n
m×n的干净图像
I
I
I和噪声图像
K
K
K,均方误差
M
S
E
MSE
MSE定义为:
M
S
E
=
1
m
n
∑
i
=
0
m
−
1
∑
j
=
0
n
−
1
[
I
(
i
,
j
)
−
K
(
i
,
j
)
]
2
M S E=\frac{1}{m n} \sum_{i=0}^{m-1} \sum_{j=0}^{n-1}[I(i, j)-K(i, j)]^{2}
MSE=mn1i=0∑m−1j=0∑n−1[I(i,j)−K(i,j)]2
然后
P
S
N
R
PSNR
PSNR就定义为:
P
S
N
R
=
10
⋅
log
10
(
M
A
X
I
2
M
S
E
)
或
者
P
S
N
R
=
20
⋅
log
10
(
M
A
X
I
M
S
E
)
P S N R=10 \cdot \log _{10}\left(\frac{M A X_{I}^{2}}{M S E}\right)\\ 或者\\ P S N R=20 \cdot \log _{10}\left(\frac{M A X_{I}}{\sqrt{M S E}}\right)
PSNR=10⋅log10(MSEMAXI2)或者PSNR=20⋅log10(MSE
MAXI)
其中
M
A
X
I
2
M A X_{I}^{2}
MAXI2为图片可能的最大像素值。如果每个像素都由 8 位二进制来表示,那么就为 255。通常,如果像素值由位
B
B
B二进制来表示,那么
M
A
X
I
=
2
B
−
1
M A X_{I}=2^{B}-1
MAXI=2B−1。
一般地,针对 uint8
数据,最大像素值为 255;针对浮点型数据,最大像素值为 1。
上面是针对灰度图像的计算方法,如果是彩色图像,通常有三种方法来计算。其中,第二和第三种方法比较常见。
- 分别计算 RGB 三个通道的 PSNR,然后取平均值。
- 计算 RGB 三通道的 MSE ,然后再除以 3 。
- 将图片转化为 YCbCr 格式,然后只计算 Y 分量也就是亮度分量的 PSNR。
针对超光谱图像,我们需要针对不同波段分别计算 P S N R PSNR PSNR,然后取平均值,这个指标称为 M P S N R MPSNR MPSNR。
1.2 SSIM
Structural SIMilarity 结构相似性
S
S
I
M
SSIM
SSIM公式基于样本
x
x
x和之
y
y
y间的三个比较衡量:亮度 (luminance)、对比度 (contrast) 和结构 (structure)。
l
(
x
,
y
)
=
2
μ
x
μ
y
+
c
1
μ
x
2
+
μ
y
2
+
c
1
c
(
x
,
y
)
=
2
σ
x
σ
y
+
c
2
σ
x
2
+
σ
y
2
+
c
2
s
(
x
,
y
)
=
σ
x
y
+
c
3
σ
x
σ
y
+
c
3
l(x, y)=\frac{2 \mu_{x} \mu_{y}+c_{1}}{\mu_{x}^{2}+\mu_{y}^{2}+c_{1}}\\c(x, y)=\frac{2 \sigma_{x} \sigma_{y}+c_{2}}{\sigma_{x}^{2}+\sigma_{y}^{2}+c_{2}}\\s(x, y)=\frac{\sigma_{x y}+c_{3}}{\sigma_{x} \sigma_{y}+c_{3}}
l(x,y)=μx2+μy2+c12μxμy+c1c(x,y)=σx2+σy2+c22σxσy+c2s(x,y)=σxσy+c3σxy+c3
一般取
c
3
=
c
2
/
2
c_{3}=c_{2} / 2
c3=c2/2。
- μ x \mu_x μx为 x x x的均值
- μ y \mu_y μy为 y y y的均值
- σ x 2 \sigma_x^2 σx2为 x x x的方差
- σ y 2 \sigma_y^2 σy2为 y y y的方差
- σ x y \sigma_{xy} σxy为 x x x和 y y y的协方差
- c 1 = ( k 1 L ) 2 , c 2 = ( k 2 L ) 2 c_{1}=\left(k_{1} L\right)^{2}, c_{2}=\left(k_{2} L\right)^{2} c1=(k1L)2,c2=(k2L)2为两个常数,避免除零
- L L L为像素值的范围, 2 B − 1 2^B-1 2B−1
- k 1 = 0.01 , k 2 = 0.03 k_{1}=0.01, k_{2}=0.03 k1=0.01,k2=0.03为默认值
那么
S
S
I
M
(
x
,
y
)
=
[
l
(
x
,
y
)
α
⋅
c
(
x
,
y
)
β
⋅
s
(
x
,
y
)
γ
]
S S I M(x, y)=\left[l(x, y)^{\alpha} \cdot c(x, y)^{\beta} \cdot s(x, y)^{\gamma}\right]
SSIM(x,y)=[l(x,y)α⋅c(x,y)β⋅s(x,y)γ]
将
α
,
β
,
γ
\alpha, \beta, \gamma
α,β,γ设为1,可以得到
S
S
I
M
(
x
,
y
)
=
(
2
μ
x
μ
y
+
c
1
)
(
2
σ
x
y
+
c
2
)
(
μ
x
2
+
μ
y
2
+
c
1
)
(
σ
x
2
+
σ
y
2
+
c
2
)
S S I M(x, y)=\frac{\left(2 \mu_{x} \mu_{y}+c_{1}\right)\left(2 \sigma_{x y}+c_{2}\right)}{\left(\mu_{x}^{2}+\mu_{y}^{2}+c_{1}\right)\left(\sigma_{x}^{2}+\sigma_{y}^{2}+c_{2}\right)}
SSIM(x,y)=(μx2+μy2+c1)(σx2+σy2+c2)(2μxμy+c1)(2σxy+c2)
每次计算的时候都从图片上取一个
N
×
N
N×N
N×N的窗口,然后不断滑动窗口进行计算,最后取平均值作为全局的 SSIM。
对于多通道的SSIM
- 分别计算 RGB 各个通道上的 PSNR\SSIM均值,然后取平均值(除以3)。
- 将图像转换为YCbCr格式,然后只计算Y分量(亮度分量)的PSNR\SSIM。
针对超光谱图像,我们需要针对不同波段分别计算 SSIM,然后取平均值,这个指标称为 MSSIM。
2、计算代码
2.1 看一下skimage的源码
def peak_signal_noise_ratio(image_true, image_test, *, data_range=None):
"""
Compute the peak signal to noise ratio (PSNR) for an image.
Parameters
----------
image_true : ndarray
Ground-truth image, same shape as im_test.
image_test : ndarray
Test image.
data_range : int, optional
The data range of the input image (distance between minimum and
maximum possible values). By default, this is estimated from the image
data-type.
Returns
-------
psnr : float
The PSNR metric.
Notes
-----
.. versionchanged:: 0.16
This function was renamed from ``skimage.measure.compare_psnr`` to
``skimage.metrics.peak_signal_noise_ratio``.
References
----------
.. [1] https://en.wikipedia.org/wiki/Peak_signal-to-noise_ratio
"""
check_shape_equality(image_true, image_test)
if data_range is None:
if image_true.dtype != image_test.dtype:
warn("Inputs have mismatched dtype. Setting data_range based on "
"image_true.")
dmin, dmax = dtype_range[image_true.dtype.type]#在dtype_range这个字典中会存储不同numpy类型所对应的可能去到的最大最小值 这个字典在下面一代码中
true_min, true_max = np.min(image_true), np.max(image_true)
if true_max > dmax or true_min < dmin:
raise ValueError(
"image_true has intensity values outside the range expected "
"for its data type. Please manually specify the data_range.")
# 真实图片最小值是不是会取到负的
# 针对无符号整型 dmin, dmax 为0,255 data_range为255
# 针对无符号整型 dmin, dmax 为-1,1 data_range为1
if true_min >= 0:
# most common case (255 for uint8, 1 for float)
data_range = dmax
else:
data_range = dmax - dmin
image_true, image_test = _as_floats(image_true, image_test)
err = mean_squared_error(image_true, image_test)
return 10 * np.log10((data_range ** 2) / err)
字典代码
dtype_range = {bool: (False, True),
np.bool_: (False, True),
np.bool8: (False, True),
float: (-1, 1),
np.float_: (-1, 1),
np.float16: (-1, 1),
np.float32: (-1, 1),
np.float64: (-1, 1)}
dtype_range.update(_integer_ranges)#还补充了整型数据的取值范围 实际取值我算了一下是
测试一下这个dtype_range
image = image.astype(np.uint8)
print("数据类型:",type(image))
print("数据结构:",image.dtype)
print("最大最小值:",dtype_range[image.dtype.type])
image = image.astype(np.float64)
print("数据类型:",type(image))
print("数据结构:",image.dtype)
print("最大最小值:",dtype_range[image.dtype.type])
2.2 实际使用情况1:单通道情况
尽可能将输入转化为
数据类型为uint8,范围为0-255的图像image1,image2
数据类型为float64,范围为0-1.0的图像image1,image2
错误示范:输入不符合标准
float64 对应的范围应该归一化到0-1,判断的时候会出错报错说你的范围超过了数据类型所对应的范围
from skimage.metrics import peak_signal_noise_ratio
from skimage.metrics import structural_similarity
import skimage.io as io
image_path1 = "./1.png"
image_path2 = "./2.png"
# 因为是张彩色图片所以截取出一个通道
image1 = io.imread(image_path1)[...,0]
image2 = io.imread(image_path2)[...,0]
image1 = image1/1.0
image2 = image2/1.0
# 至此image1为float64 且0-255.0 就会报错
print(image1.dtype)
psnr_val = peak_signal_noise_ratio(image1, image2)
ssim_val = structural_similarity(image1,image2,win_size=11,gaussian_weights=True,multichannel=True,data_range=1.0,K1=0.01,K2=0.03,sigma=1.5)
print("psnr_val",psnr_val)
print("ssim_val",ssim_val)
正确示例:数据类型为uint8,范围为0-255的图像image1,image2
from skimage.metrics import peak_signal_noise_ratio
from skimage.metrics import structural_similarity
import skimage.io as io
image_path1 = "./1.png"
image_path2 = "./2.png"
# 因为是张彩色图片所以截取出一个通道
image1 = io.imread(image_path1)[...,0]
image2 = io.imread(image_path2)[...,0]
print(image1.dtype)# uint8 范围0-255
psnr_val = peak_signal_noise_ratio(image1, image2)
ssim_val = structural_similarity(image1,image2,win_size=11,gaussian_weights=True,multichannel=True,data_range=1.0,K1=0.01,K2=0.03,sigma=1.5)
print("psnr_val",psnr_val)
print("ssim_val",ssim_val)
正确示例:数据类型为float64,范围为0-1.0的图像image1,image2
from skimage.metrics import peak_signal_noise_ratio
from skimage.metrics import structural_similarity
import skimage.io as io
image_path1 = "./1.png"
image_path2 = "./2.png"
image1 = io.imread(image_path1)[...,0]
image2 = io.imread(image_path2)[...,0]
image1 = image1/1.0
image2 = image2/1.0
# 至此image1为float64 且0-255.0
# 归一化到0-1.0
image1 = image1/255.0
image2 = image2/255.0
print(image1.dtype)
psnr_val = peak_signal_noise_ratio(image1, image2)
ssim_val = structural_similarity(image1,image2,win_size=11,gaussian_weights=True,multichannel=True,data_range=1.0,K1=0.01,K2=0.03,sigma=1.5)
print("psnr_val",psnr_val)
print("ssim_val",ssim_val)
2.3 实际使用情况2:RGB三通道
需要先转换成YCbCr空间然后对亮度进行求解PSNR,转换方法可以参照我的另一篇博客
RGB图像转换成YCbCr图像,rgb2ycbcr的使用,转换参数_呆呆象呆呆的博客-CSDN博客
同时也要保证数值范围和数值类型要相符合,尽可能将输入转化为
数据类型为uint8,范围为0-255的图像image1,image2(不太推荐因为算出Y通道后,大概率都是浮点型的数据,强行转换成uint8这样精度会下降,所以比较推荐下面一种方式)
数据类型为float64,范围为0-1.0的图像image1,image2
正确示例1:使用rgb2ycbcr计算Y通道后求PSNR或者SSIM
from skimage.metrics import peak_signal_noise_ratio
from skimage.metrics import structural_similarity
from skimage.color import rgb2ycbcr
import skimage.io as io
image_path1 = "./1.png"
image_path2 = "./2.png"
image1 = io.imread(image_path1)
image2 = io.imread(image_path2)
# rgb2ycbcr的输入需要归一化到0-1.0的float
#这个在上一篇blog中讲过了rgb2ycbcr输出为浮点型且范围是0-255.0 所以需要再次归一化0-1
image1 = image1/255.0
image2 = image2/255.0
image1 = rgb2ycbcr(image1)[:, :, 0:1]
image2 = rgb2ycbcr(image2)[:, :, 0:1]
image1 = image1/255.0
image2 = image2/255.0
print(image1.dtype)
psnr_val = peak_signal_noise_ratio(image1, image2)
ssim_val = structural_similarity(image1,image2,win_size=11,gaussian_weights=True,multichannel=True,data_range=1.0,K1=0.01,K2=0.03,sigma=1.5)
print("psnr_val",psnr_val)
print("ssim_val",ssim_val)
三通道的PSNR和单通道的PSNR肯定是不一样的
正确示例2:直接计算Y通道后求PSNR或者SSIM
from skimage.metrics import peak_signal_noise_ratio
from skimage.metrics import structural_similarity
import skimage.io as io
image_path1 = "./1.png"
image_path2 = "./2.png"
# 因为是张彩色图片 所以选一个通道
image1 = io.imread(image_path1)
image2 = io.imread(image_path2)
#我认为最简单的方法
image1 = image1/255.0
image2 = image2/255.0
image1 = 65.481 * image1[:,:,0] + 128.553 * image1[:,:,1] + 24.966 * image1[:,:,2] # 不加16是因为之后会抵消
image2 = 65.481 * image2[:,:,0] + 128.553 * image2[:,:,1] + 24.966 * image2[:,:,2]
image1 = image1/255.0
image2 = image2/255.0
# 只计算Y通道的值
print(image1.dtype)
psnr_val = peak_signal_noise_ratio(image1, image2)
ssim_val = structural_similarity(image1,image2,win_size=11,gaussian_weights=True,multichannel=True,data_range=1.0,K1=0.01,K2=0.03,sigma=1.5)
print("psnr_val",psnr_val)
print("ssim_val",ssim_val)
LAST、参考文献
scikit-image/simple_metrics.py at main · scikit-image/scikit-image · GitHub
PSNR与SSIM对于彩色图像和灰度图像的计算区别_风雪夜归人o的博客-CSDN博客
图像质量的客观评估指标PSNR与SSIM_小村长技术blog-CSDN博客