图像质量评估指标——SSIM介绍及计算方法

图像质量评估指标——SSIM介绍及计算方法

  SSIM全称为Structural Similarity,即结构相似性,用于评估两幅图像相似度的指标,常用于衡量图像失真前与失真后的相似性,也用于衡量模型生成图像的真实性,如图像去雨、图像去雾、图像和谐化等等。

计算方法

  SSIM的计算基于滑动窗口实现,即每次计算均从图片上取一个尺寸为 N × N N\times N N×N的窗口,基于窗口计算SSIM指标,遍历整张图像后再将所有窗口的数值取平均值,作为整张图像的SSIM指标

  假设 x x x表示第一张图像窗口中的数据, y y y表示第二张图像窗口中的数据。其中图像的相似性由三部分构成:luminance(亮度)、contrast(对比度)和structure(结构)。luminance计算公式为:
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
contrast计算公式为:
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
structure计算公式为:
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
其中 μ x \mu_x μx μ y \mu_y μy依次表示 x x x y y y的均值, σ x \sigma_x σx σ y \sigma_y σy依次表示 x x x y y y的方差, σ x y \sigma_{xy} σxy表示 x x x y y y之间的协方差, c 1 = ( k 1 L ) 2 c_1=(k_1L)^2 c1=(k1L)2 c 2 = ( k 2 L ) 2 c_2=(k_2L)^2 c2=(k2L)2以及 c 3 = c 2 / 2 c_3=c_2/2 c3=c2/2,表示三个常数,避免分母为0, k 1 k_1 k1 k 2 k_2 k2依次默认为 0.01 0.01 0.01 0.03 0.03 0.03 L L L表示图像像素值的范围,即 2 B − 1 2^B-1 2B1

  最后SSIM的计算公式为:
S S I M ( x , y ) = [ l ( x , y ) α ⋅ c ( x , y ) β ⋅ s ( x , y ) γ ] SSIM(x,y)=[l(x,y)^{\alpha}·c(x,y)^{\beta}·s(x,y)^{\gamma}] SSIM(x,y)=[l(x,y)αc(x,y)βs(x,y)γ]
如果令 α , β , γ \alpha, \beta, \gamma α,β,γ均为1,则得到常用的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)

代码实现

def gaussian(window_size, sigma):
    # 计算公式:e^(-x^2)/(2*sigma^2),其中x表示距离中心点的距离,sigma默认1.5
    gauss = torch.Tensor([exp(-(x - window_size // 2) ** 2 / float(2 * sigma ** 2)) for x in range(window_size)])
    # 数据归一化
    return gauss / gauss.sum()


# 计算滑动窗口权重
def create_window(window_size, channel):
    # 利用滑动窗口尺寸先计算一个一维,并且服从正态分布的数据
    # 注意这里利用unsqueeze函数扩了一下维度,从行向量变为了列向量
    _1D_window = gaussian(window_size, 1.5).unsqueeze(1)
    # 列向量乘以行向量,变为n*n的矩阵,正好对应窗口权重
    _2D_window = _1D_window.mm(_1D_window.t()).float().unsqueeze(0).unsqueeze(0)
    # 沿通道维度复制channel遍,每个通道对应一个权重(这里所有通道权重相同,均服从正态分布),并且变为连续存储的数据
    window = Variable(_2D_window.expand(channel, 1, window_size, window_size).contiguous())
    # 返回窗口权重数据
    return window


def _ssim(img1, img2, window, window_size, channel, size_average=True):
    # 计算每个滑动窗口的均值
    # 卷积运算正好是窗口数据按权重求和再取均值,因此可以利用二维卷积运算来计算窗口中数据的均值
    mu1 = F.conv2d(img1, window, padding=window_size // 2, groups=channel)
    mu2 = F.conv2d(img2, window, padding=window_size // 2, groups=channel)
    # 均值取平方,即E^2(X)
    mu1_sq = mu1.pow(2)
    mu2_sq = mu2.pow(2)
    # 计算E(X)E(Y),用于后续计算协方差
    mu1_mu2 = mu1 * mu2
    # 依次计算img1与img2的方差
    # 这里计算方差利用公式D(X)=E(X^2)-E^2(X),其中E^2(X)表示均值的平方,即上述公式中的mu1_sq、mu2_sq、mu1_mu2
    sigma1_sq = F.conv2d(img1 * img1, window, padding=window_size // 2, groups=channel) - mu1_sq
    sigma2_sq = F.conv2d(img2 * img2, window, padding=window_size // 2, groups=channel) - mu2_sq
    # 计算img1、img2之间的协方差
    # 利用公式Conv(X,Y)=E(XY)-E(X)E(Y)
    sigma12 = F.conv2d(img1 * img2, window, padding=window_size // 2, groups=channel) - mu1_mu2
    C1 = 0.01 ** 2
    C2 = 0.03 ** 2
    # 利用上述得到的指标,传入公式计算ssim值,此时会得到一张图,最后再求均值即可
    ssim_map = ((2 * mu1_mu2 + C1) * (2 * sigma12 + C2)) / ((mu1_sq + mu2_sq + C1) * (sigma1_sq + sigma2_sq + C2))

    if size_average:
        return ssim_map.mean()
    else:
        return ssim_map.mean(1).mean(1).mean(1)


def ssim(img1, img2, window_size=11, size_average=True):
    # 将输入的图像数据限制为0-1之间(一般数据就是位于0-1之间,防止出现异常值)
    img1 = torch.clamp(img1, min=0, max=1)
    img2 = torch.clamp(img2, min=0, max=1)
    # 得到图片通道数
    (_, channel, _, _) = img1.size()
    # 得到窗口的权重数据,离窗口中心越远,权重越小。权重服从高斯分布(正态分布)
    window = create_window(window_size, channel)
    # 如果图片数据储存在cuda上(即利用显卡训练),则将窗口权重数据也传入cuda中
    if img1.is_cuda:
        window = window.cuda(img1.get_device())
    # 统一数据类型
    window = window.type_as(img1)
    # 调用_ssim,计算ssim值
    return _ssim(img1, img2, window, window_size, channel, size_average)

直接调用skimage库中的函数

from skimage.metrics import structural_similarity


# im1, im2分别表示参与计算的图像数据
# data_range表示图像数据的范围,一般设置为255或者1(如果对图像数据做了归一化操作,则为1)
# channel_axis表示颜色通道位于图像的第几维度,如果不指定的话,则默认输入灰度图像
ssim = structural_similarity(im1, im2, win_size=None, gradient=False, data_range=None, 
        channel_axis=None, multichannel=False, gaussian_weights=False, full=False)

以上仅是笔者个人见解,若有问题,欢迎指正。

  • 21
    点赞
  • 117
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

视觉萌新、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值