参考自维基百科SSIM
结构相似性指标(英文:structural similarity index,SSIM index),是一种用以衡量两张数字图象相似性的指标。结构相似性在于衡量数字图像相邻像素的关联性,图像中相邻像素的关联性反映了实际场景中物体的结构信息。因此,在设计图像失真的衡量指标时,必须考虑结构性失真。SSIM
指标于2004
年提出1。但当图像出现位移、缩放、旋转(皆属于非结构性的失真)的情况无法有效的反映。
给定两个图像x,y
,两者的结构相似性SSIM
定义为:
S S I M ( x , y ) = [ l ( x , y ) ] α [ c ( x , y ) ] β [ l ( x , y ) ] γ SSIM(x,y)={[l(x,y)]}^{\alpha}{[c(x,y)]}^{\beta}{[l(x,y)]}^{\gamma} SSIM(x,y)=[l(x,y)]α[c(x,y)]β[l(x,y)]γ
l ( x , y ) = 2 μ x μ y + C 1 μ x 2 + μ y 2 + C 1 l(x,y)=\frac{2\mu_x\mu_y+C_1}{\mu_x^2+\mu_y^2+C_1} l(x,y)=μx2+μy2+C12μxμy+C1
c ( x , y ) = 2 σ x σ y + C 2 σ x 2 + σ y 2 + C 2 c(x,y)=\frac{2\sigma_x\sigma_y+C_2}{\sigma_x^2+\sigma_y^2+C_2} c(x,y)=σx2+σy2+C22σxσy+C2
s ( x , y ) = σ x y + C 3 σ x σ y + C 3 s(x,y)=\frac{\sigma_{xy}+C_3}{\sigma_x\sigma_y+C_3} s(x,y)=σxσy+C3σxy+C3
上式中:
———— l(x,y)
:比较两个图像的亮度,像素均值
———— c(x,y)
:比较两个图像的对比度,像素方差
———— s(x,y)
:比较两个图像的结构,协方差,像素间关系。
———— α , β , γ \alpha,\beta,\gamma α,β,γ:大于零,调整l(x,y),c(x,y),s(x,y)
相对重要性的参数。
———— μ x , μ y , σ x , σ y \mu_x,\mu_y,\sigma_x,\sigma_y μx,μy,σx,σy分别是x,y
的均值和方差。
————— σ x y \sigma_{xy} σxy:是x,y
的协方差,为 E [ ( X − E ( X ) ) ( Y − E ( Y ) ) ] E[(X-E(X))(Y-E(Y))] E[(X−E(X))(Y−E(Y))]
———— C 1 , C 2 , C 3 C_1,C_2,C_3 C1,C2,C3:都为常数,用以保证l(x,y),c(x,y),s(x,y)
的稳定
SSIM值越大代表相似度越高。
特点:
SSIM(x,y)=SSIM(y,x)
。实际使用中,会使用滑动窗口,在NXN
的小区块中分别计算视窗内的结构相似性指标,最后将所有的局部结构相似性指标值求平均,即为两张图像的SSIM值,也被称作MSSIM(Mean SSIm)
。计算时,一般会将参数设置为 α = β = β = 1 , C 3 = C 2 2 \alpha=\beta=\beta=1,C_3=\frac{C_2}{2} α=β=β=1,C3=2C2,SSIM计算可简化为:
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 ) SSIM(x,y)=\frac{(2\mu_x\mu_y+C_1)(2\sigma_{xy}+C_2)}{(\mu_x^2+\mu_y^2+C_1)(\sigma_x^2+\sigma_y^2+C_2)} SSIM(x,y)=(μx2+μy2+C1)(σx2+σy2+C2)(2μxμy+C1)(2σxy+C2)
import sys
import numpy
from scipy import signal
from scipy import ndimage
def fspecial_gauss(size, sigma):
x, y = numpy.mgrid[-size//2 + 1:size//2 + 1, -size//2 + 1:size//2 + 1]
g = numpy.exp(-((x**2 + y**2)/(2.0*sigma**2)))
return g/g.sum()
def ssim(img1, img2, cs_map=False):
img1 = img1.astype(numpy.float64)
img2 = img2.astype(numpy.float64)
size = 11
sigma = 1.5
window = fspecial_gauss(size, sigma)
K1 = 0.01
K2 = 0.03
L = 255 #bitdepth of image
C1 = (K1*L)**2
C2 = (K2*L)**2
mu1 = signal.fftconvolve(window, img1, mode='valid')
mu2 = signal.fftconvolve(window, img2, mode='valid')
mu1_sq = mu1*mu1
mu2_sq = mu2*mu2
mu1_mu2 = mu1*mu2
sigma1_sq = signal.fftconvolve(window, img1*img1, mode='valid') - mu1_sq
sigma2_sq = signal.fftconvolve(window, img2*img2, mode='valid') - mu2_sq
sigma12 = signal.fftconvolve(window, img1*img2, mode='valid') - mu1_mu2
if cs_map:
return (((2*mu1_mu2 + C1)*(2*sigma12 + C2))/((mu1_sq + mu2_sq + C1)*
(sigma1_sq + sigma2_sq + C2)),
(2.0*sigma12 + C2)/(sigma1_sq + sigma2_sq + C2))
else:
return ((2*mu1_mu2 + C1)*(2*sigma12 + C2))/((mu1_sq + mu2_sq + C1)*
(sigma1_sq + sigma2_sq + C2))
def mssim(img1, img2):
"""
refer to https://github.com/mubeta06/python/tree/master/signal_processing/sp
"""
level = 5
weight = numpy.array([0.0448, 0.2856, 0.3001, 0.2363, 0.1333])
downsample_filter = numpy.ones((2, 2))/4.0
im1 = img1.astype(numpy.float64)
im2 = img2.astype(numpy.float64)
mssim = numpy.array([])
mcs = numpy.array([])
for l in range(level):
ssim_map, cs_map = ssim(im1, im2, cs_map=True)
mssim = numpy.append(mssim, ssim_map.mean())
mcs = numpy.append(mcs, cs_map.mean())
filtered_im1 = ndimage.filters.convolve(im1, downsample_filter,
mode='reflect')
filtered_im2 = ndimage.filters.convolve(im2, downsample_filter,
mode='reflect')
im1 = filtered_im1[::2, ::2]
im2 = filtered_im2[::2, ::2]
return (numpy.prod(mcs[0:level-1]**weight[0:level-1])*
(mssim[level-1]**weight[level-1]))
mssim_val = mssim(img, noise_img)
ssim_val = ssim(img, noise_img)
print(f"mssim_val: {mssim_val}, ssim_val: {ssim_val.mean()}")
峰值信噪比(Peak Signal to Noise Ratio, PSNR),表示的是信号的最大功率与噪声功率的比值。峰值信噪比越高,表示噪声影响越小;峰值信噪比越低,表示噪声影响越大。3单位是分贝dB
,大于30dB
,压缩前后图像质量肉眼很难看出区别。
通常噪声常使用均方误差(Mean Square Error,MSE)来描述。两个宽高为w,h
的灰度图I\K
,I
为无噪声图像,K
为I
的带噪声近似,则:
M S E = 1 ω h ∑ i = 0 ω − 1 ∑ j = 0 h − 1 [ I ( i , j ) − K ( i , j ) ] 2 MSE=\frac{1}{\omega h}\sum_{i=0}^{\omega-1}\sum_{j=0}^{h-1}[I(i,j)-K(i,j)]^2 MSE=ωh1∑i=0ω−1∑j=0h−1[I(i,j)−K(i,j)]2
P S N R = 10 l o g 10 ( M A X I 2 M S E ) = 20 l o g 10 ( M A X 1 M S E ) PSNR=10{log}_{10}\left (\frac{MAX_I^2}{MSE} \right )=20{log}_{10}\left (\frac{MAX_1}{\sqrt{MSE}} \right ) PSNR=10log10(MSEMAXI2)=20log10(MSEMAX1)
M A X I MAX_I MAXI是表示图像点颜色的最大数值,如果每个采样点用 8 位表示,则为 2 8 = 255 2^8=255 28=255
若为彩色图像,通常有三种方法来计算:
MSE
使RGB三通道MSE的平均YCbCr
格式,然后只计算Y
分量也就是亮度分量的 PSNR针对超光谱图像,我们需要针对不同波段分别计算 PSNR,然后取平均值,这个指标称为 MPSNR。
import numpy as np
def get_psnr(I, K):
error = K - I
mse = np.mean(np.square(error))
psnr = 10 * np.log10(255 * 255 / mse)
return psnr
psnr_value = get_psnr(img, noise_img)
print(f"psnr_value: {psnr_value}")
补充:
上述计算使用的是添加了高斯噪声的图像和原图。
import cv2
import numpy as np
file_path = "img.png"
def get_gauss_noise_image(image):
row,col= image.shape
gauss = np.random.normal(0, 50, (row,col))
gauss = gauss.reshape(row,col)
noisy = image + gauss
return noisy
img = cv2.imread(file_path, cv2.IMREAD_GRAYSCALE)
cv2.imwrite("gray_text.png", img)
noise_img = get_gauss_noise_image(img)
cv2.imwrite("gray_noise_text.png", noise_img)
# pnsr=12
参考:
[1]:https://www.cns.nyu.edu/pub/lcv/wang03-preprint.pdf
[2]:https://zh.wikipedia.org/wiki/%E7%B5%90%E6%A7%8B%E7%9B%B8%E4%BC%BC%E6%80%A7#cite_note-SSIM-1
[3]:https://zh.wikipedia.org/wiki/%E5%B3%B0%E5%80%BC%E4%BF%A1%E5%99%AA%E6%AF%94