图像质量评估算法SSIM(结构相似性)

SSIM(structural similarity index)

由于最近在阅读图像超分辨率方面的RCAN论文,里面涉及到了两幅图像之间的相似性,所以就引入了这个指标,并最终使用pyhton进行实现。结构相似性,是一种衡量两幅图像相似度的指标。该指标首先由德州大学奥斯丁分校的图像和视频工程实验室(Laboratory for Image and Video Engineering)提出。而如果两幅图像是压缩前和压缩后的图像,那么SSIM算法就可以用来评估压缩后的图像质量。本文着重在于代码实现SSIM函数,而该算法的原理以及为什么需要高斯核函数作为模板的权值并不关心,如果需要的话可以在文章的后面大的连接里面自行阅读。

如何计算结构相似性

在计算两幅图像的结构相似性,需要高斯模板用来作为一个像素区域的概率值,进而计算得到某个区域的像素平均值,标准差,方差和协方差等概念。除此之外给出一组公式用来计算SSIM:
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+C1} L(X,Y)=μX2+μY2+C12μXμY+C1
C ( X , Y ) = 2 σ X σ Y + C 2 σ 2 2 + σ Y 2 + C 2 C(X,Y) = \frac{2\sigma_X\sigma_Y+C_2}{\sigma_2^2+\sigma_Y^2+C_2} C(X,Y)=σ22+σ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+C3} S(X,Y)=σXσY+C3σXY+C3
当里面的参数 C 3 = C 2 / 2 C_3 = C_2/2 C3=C2/2 S S I M ( X , Y ) 也 可 以 写 成 下 面 的 式 子 SSIM(X,Y)也可以写成下面的式子 SSIM(X,Y)
S S I M ( X , Y ) = L ( X , Y ) C ( X , Y ) S ( X , Y ) = 2 μ X μ Y + C 1 μ X 2 + μ Y 2 + C 1 ∗ 2 σ X Y + C 2 σ 2 2 + σ Y 2 + C 2 SSIM(X,Y) = L(X,Y)C(X,Y)S(X,Y)=\frac{2\mu_X \mu_Y+C_1}{\mu_X^2 + \mu_Y^2+C1}* \frac{2\sigma_{XY}+C_2}{\sigma_2^2+\sigma_Y^2+C_2} SSIM(X,Y)=L(X,Y)C(X,Y)S(X,Y)=μX2+μY2+C12μXμY+C1σ22+σY2+C22σXY+C2
里面的 μ X 、 σ X \mu_X、\sigma_X μXσX分别代表X这幅图片里面的均值。
下面用图示的方式演示涌口滑动的过程:
在这里插入图片描述
上图的左边是高斯核尺寸是 ( 2 k + 1 ) ∗ ( 2 k + 1 ) (2k+1)*(2k+1) (2k+1)(2k+1),加入我们将要从图片的第 ( i , j ) (i,j) (i,j)个位置与高斯核进行运算(如果熟悉卷积神经网络的话大家肯定就很快就可以理解)。然后我们使用高斯模块在整个图像上移动得到一个SSIM_MAP,其尺寸就是 ( w − 2 k ) ∗ ( d − 2 k ) (w-2k)*(d-2k) (w2k)(d2k)

高斯核的实现

按照前人的做法是使用一个 ( 2 k + 1 ) ∗ ( 2 k + 1 ) (2k+1)*(2k+1) (2k+1)(2k+1)的高斯模板滑动整个图像得到一个SSIM_MAP之后再计算其平均值作为两幅图像的SSIM。这里需要强调的是区域里面的每个像素出现的概率是符合高斯分布的,而不是均匀分布。故我们首先要设计一个高斯核函数确定权重,计算公式如下:
K ( i , j ) = 1 2 π σ 2 e x p − ( i − k ) 2 + ( j − k ) 2 2 π σ 2 , 0 ≤ i , j ≤ 2 k K(i,j) = \frac{1}{2\pi\sigma^2}exp^{-\frac{(i-k)^2+(j-k)^2}{2\pi\sigma^2}}, 0 \le i,j \le2k K(i,j)=2πσ21exp2πσ2(ik)2+(jk)2,0i,j2k

区域平均值实现

由于区域里面的每个点具有不同的权重,在这里我们认为从图片的 ( i , j ) (i,j) (i,j)点开始与高斯核匹配的的平均值是:
μ X ( i , j ) = Σ m = 0 m = 2 k Σ n = 0 n = 2 k k ( m , n ) X ( i + m , j + n ) \mu_X(i,j) = \Sigma_{m=0}^{m=2k}\Sigma_{n=0}^{n=2k}k(m,n)X(i+m,j+n) μX(i,j)=Σm=0m=2kΣn=0n=2kk(m,n)X(i+m,j+n)

区域方差实现

根据方差的公式我们写作:
σ X 2 ( i , j ) = Σ m = 0 m = 2 k Σ n = 0 n = 2 k k ( m , n ) ( X ( i + m , j + n ) − μ X ( i , j ) ) 2 = Σ m = 0 m = 2 k Σ n = 0 n = 2 k k ( m , n ) ( X ( i + m , j + n ) 2 − 2 X ( i + m , j + n ) μ X ( i , j ) 2 + μ X 2 ( i , j ) = Σ m = 0 m = 2 k Σ n = 0 n = 2 k k ( m , n ) X ( i + m , j + n ) 2 − μ X 2 ( i , j ) \sigma_X^2(i,j) = \Sigma_{m=0}^{m=2k}\Sigma_{n=0}^{n=2k}k(m,n)(X(i+m,j+n)-\mu_X(i,j))^2 \\=\Sigma_{m=0}^{m=2k}\Sigma_{n=0}^{n=2k}k(m,n)(X(i+m,j+n)^2-2X(i+m,j+n)\mu_X(i,j)^2+\mu_X^2(i,j) \\=\Sigma_{m=0}^{m=2k}\Sigma_{n=0}^{n=2k}k(m,n)X(i+m,j+n)^2-\mu_X^2(i,j) σX2(i,j)=Σm=0m=2kΣn=0n=2kk(m,n)(X(i+m,j+n)μX(i,j))2=Σm=0m=2kΣn=0n=2kk(m,n)(X(i+m,j+n)22X(i+m,j+n)μX(i,j)2+μX2(i,j)=Σm=0m=2kΣn=0n=2kk(m,n)X(i+m,j+n)2μX2(i,j)

区域协方差实现

协方差公式:
σ X , Y = Σ m = 0 m = 2 k Σ n = 0 n = 2 k k ( m , n ) ( X ( i + m , j + n ) − μ X ( i , j ) ) ( Y ( i + m , j + n ) − μ Y ( i , j ) ) = Σ m = 0 m = 2 k Σ n = 0 n = 2 k k ( m , n ) X ( i + m , j + n ) Y ( i + m , j + n ) − μ X ( i , j ) μ Y ( i , j ) \sigma_{X,Y} = \Sigma_{m=0}^{m=2k}\Sigma_{n=0}^{n=2k}k(m,n)(X(i+m,j+n)-\mu_X(i,j))(Y(i+m,j+n)-\mu_Y(i,j))\\ = \Sigma_{m=0}^{m=2k}\Sigma_{n=0}^{n=2k}k(m,n)X(i+m,j+n)Y(i+m,j+n) - \mu_X(i,j)\mu_Y(i,j) σX,Y=Σm=0m=2kΣn=0n=2kk(m,n)(X(i+m,j+n)μX(i,j))(Y(i+m,j+n)μY(i,j))=Σm=0m=2kΣn=0n=2kk(m,n)X(i+m,j+n)Y(i+m,j+n)μX(i,j)μY(i,j)

代码实现

由于上面的几部分已经将SSIM公式里面的参数计算出来了,下面就是我基于自己的理解编写出来的代码:

import numpy as np
import cv2

def getres(img, kernel, i,j):
    k_w, k_d = kernel.shape
    res = np.sum(img[i:i+k_w, j:j+k_d]*kernel)
    return res

def conv2d(img, kernel):
    img_w, img_d = img.shape
    k_w, k_d = kernel.shape
    res = [[getres(img, kernel, i,j) for j in range(img_d-k_d+1)] for i in range(img_w-k_w+1)]
    return np.array(res, dtype=np.float32)

def ssim(img1=None, img2=None, k=11, sigma=1.5):  #输入的图片是三通道的rgb图片格式为uint8
    if img1.shape != img2.shape:
        print("the shape of the two image is different, please check!!!")
        return None
    if k <= 5 and k%2 == 0:
        print("the kernel size is small or odd!!!")
        return None
    if sigma <= 0:
        print("the sigma can not be 0 or less 0!!!")
        return None
    
    img1 = cv2.cvtColor(img1, cv2.COLOR_RGB2YCR_CB)[:,:,0].astype(np.float32)
    img2 = cv2.cvtColor(img2, cv2.COLOR_RGB2YCR_CB)[:,:,0].astype(np.float32)
    c1 = (0.01*255)**2
    c2 = (0.03*255)**2
    filter = [[ ((i-k//2)*(i-k//2) + (j-k//2)*(j-k//2))/(-2*sigma*sigma) for j in range(k)] for i in range(k)]
    filter = np.array(np.exp(filter), dtype=np.float32)
    filter = filter / np.sum(filter)

    mu1 = conv2d(img1, filter)
    mu2 = conv2d(img2, filter)
    mu1_sq = mu1 * mu1
    mu2_sq = mu2 * mu2
    mu1_mu2 = mu1 * mu2
    sigma1_sq = conv2d(img1*img1, filter) - mu1_sq
    sigma2_sq = conv2d(img2*img2, filter) - mu2_sq
    sigma12 = conv2d(img1*img2, filter) - mu1_mu2

    ssim_map = (2*mu1_mu2 +c1)*(2*sigma12+c2) / (mu1_sq + mu2_sq + c2) / (sigma1_sq + sigma2_sq + c2)
    return np.mean(ssim_map)

代码使用

如果大家只是为了使用函数得到两幅图像之间的SSIM,那么简单来说就是先读取图像,并且将图片的通道转化为RGB模式,这个原因是因为我在代码里面将图片转化为YCBCR并且仅计算Y通道上的SSIM。这里的计算方式是参照RCAN论文所述。

#首先读取两张图片,因为opencv是按照BGR读入的所以我就先进行了color的cvt。
#要是引用的该函数的话只用调用ssim(x,y)即可,里面的其他变量我已经在函数里面设置好了。
#可以更改的就是高斯核的大小即参数k, 已经高斯核的标准差sigma
img1 = cv2.imread("D:/microsoft/RCAN-master/RCAN_TestCode/HR/B100/x4/3096_HR_x4.png")
img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2RGB)
img2 = img1
ssim(img1, img2)

引用

图像质量评估算法 SSIM(结构相似性)
今天终于弄清楚了fspecial(‘gaussian’,3,1.8)怎么算的了

附加资料

Residual Channel Attention Networks即RCAN差分辨率网络
SSIM百度百科

  • 7
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值