热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

结构相似度索引(SSIM)全攻略:理论+代码(PyTorch)

作者:PranjalDatta编译:ronghuaiyang导读有很多资料解释了SSIM背后的理论,但很少有资源深入研究细节࿰





作者:Pranjal Datta

编译:ronghuaiyang


导读

有很多资料解释了SSIM背后的理论,但很少有资源深入研究细节,本文就是试图填补这一空白的谦虚尝试。

最近,在实现一篇深度估计论文时,我遇到了术语结构相似性指数(SSIM)。SSIM作为度量两个给定图像之间相似度的度量指标。由于这项技术从2004年就开始了,有很多资料解释了SSIM背后的理论,但很少有资源深入研究细节,这对于基于梯度的实现太特别了,因为SSIM经常被用作损失函数。因此,本文就是我试图填补这一空白的谦虚尝试!

本文的目标有两个:


  • 解释SSIM背后的理论和直觉,探索其在当前前沿深度学习中的一些应用。

  • 深入PyTorch实现。

所以让我们开始吧!


理论

SSIM首先在2004年IEEE的论文中被引入,Image Quality Assessment: From Error Visibility to Structural Similarity


评估感知图像质量的客观方法,传统上试图量化失真图像和参考图像之间的误差(差异)可见性,使用人类视觉系统的各种已知属性。在假设人类视觉感知高度适应于从场景中提取结构信息的前提下,我们引入了一种基于结构信息退化的质量评估替代互补框架。


小结:作者提出了两点要点,


  • 大多数图像质量评估技术依赖于量化参考图像和样本图像之间的误差。一种常用的度量是量化样本和参考图像之间对应的每个像素的值的差异(例如,使用均方误差)

  • 人类视觉感知系统能够从一个场景中识别结构信息,从而识别从参考场景和样本场景中提取的信息之间的差异。因此,复制此行为的指标将在涉及区分样本图像和参考图像的任务中执行得更好。

结构相似性指数(SSIM)度量从一幅图像中提取3个关键特征:


  • 亮度

  • 对比度

  • 结构

两幅图像的比较就是根据这3个特征进行的。

结构相似性度量系统的组织和流程如下图所示。X和Y分别为参考图像和样本图像。

图1: 结构相似性度量系统


这个度量是计算什么呢?

这个系统计算2幅给定图像之间的结构相似度指数,其值在-1到+1之间。值为+1表示两张给定的图像非常相似或相同,值为-1表示两张给定的图像非常不同。这些值通常被调整到范围[0,1],其中极端值具有相同的含义。

现在,让我们简要地探讨一下这些特性是如何用数学表示的,以及它们是如何影响最终的SSIM分数的。


  • 亮度:亮度通过对所有像素值进行平均测量。用μ表示,公式如下:

式中,xi为图像x的第i个像素值,N为像素值的总数。


  • 对比度:取所有像素值的标准差(方差的平方根)来测量。用σ表示,表示为:

其中x和y为两幅图像,μ为图像像素值的平均值。


  • 结构:结构比较是通过使用一个合并公式来完成的(后面会详细介绍),但在本质上,我们用输入信号的标准差来除以它,因此结果有单位标准差,这可以得到一个更稳健的比较。

其中x是输入图像。

现在我们已经建立了这三个参数背后的数学直觉。我们还没有完成数学运算,还有一点。我们现在缺少的是比较函数,它可以在这些参数上比较两个给定的图像,最后,一个组合函数,将它们组合在一起。在这里,我们定义了比较函数,最后定义了产生相似性指标值的组合函数。


  • 亮度比较函数:由函数定义,l(x, y),如下图所示。μ表示给定图像的平均值。x和y是被比较的两个图像。

其中C1为常数,保证分母为0时的稳定性。C1这样给出:


  • 对比度比较函数:由函数c(x, y)定义,如下图所示。σ表示给定图像的标准差。x和y是被比较的两个图像。

其中C2这样给出:


  • 结构比较函数:由函数s(x, y)定义,如下图所示。σ表示给定图像的标准差。x和y是被比较的两个图像。

其中σ(xy)定义为:

最后,SSIM定义为:

其中 α > 0, β > 0, γ > 0 表示每个度量标准的相对重要性。为了简化表达式,我们设:


现在问题来了!

虽然你可以使用上述公式实现SSIM,但它可能不如已有的现成实现那么好,正如作者解释的那样,


对于图像质量评估,应该局部应用SSIM指数而不是全局应用。首先,图像统计特征通常是高度空间非平稳的。其次,图像失真可能取决于或不取决于局部图像统计,也可能是空间变量。第三,在典型的观测距离下,由于HVS的凹点特征,人眼一次只能看到图像中的具有高分辨率的局部区域。最后,局部质量测量可以提供一个空间变化的图像质量图,它提供了更多关于图像质量退化的信息,在某些应用中可能有用。


总结:与其在全局范围内应用上述度量值(即一次在图像上的所有区域),不如在局部范围内应用这些度量值(即在图像的小部分中,然后取整体的平均值)。

这种方法通常被称为平均结构相似度指数。

由于方法上的这种变化,我们的公式也应该进行修改以反映相同的情况(应该注意的是,这种方法更常见,将用于解释代码)。

(:如果下面的内容看起来有点难以应付,不用担心!如果你理解了它的要点,那么浏览一下代码会让你更清楚。)

作者使用一个11x11圆对称高斯加权函数(基本上就是一个11x11矩阵,其值来自高斯分布)在整个图像上逐像素移动。在每一步中,在局部窗口内计算局部统计信息和SSIM索引。由于我们现在在局部计算,我们的公式被修改为:

其中wi是高斯加权函数。

如果你觉得这有点不直观,不用担心!可以将wi想象成乘法,并通过一些数学技巧来计算所需的值。

一旦对整个图像进行了计算,我们只需取所有局部SSIM值的平均值,就得到了全局的 SSIM值。

理论终于完成了!现在进入代码!


代码

在深入研究代码之前,需要注意的是,我们不会遍历每行,但是我们将深入研究最基本的那些代码。让我们开始吧!

首先,让我们研究一些执行一些基本任务的函数。


Function #1: gaussian(window_size, sigma)

这个函数本质上是从一个高斯分布中采样的一个数字列表(长度等于window_size)。所有元素的和等于1,值被归一化。Sigma是高斯分布的标准差。

注意:它用于生成上面提到的11x11高斯窗口。

例子:

Code:gauss_dis = gaussian(11, 1.5)
print("Distribution: ", gauss_dis)
print("Sum of Gauss Distribution:", torch.sum(gauss_dis))Output: Distribution: tensor([0.0010, 0.0076, 0.0360, 0.1094, 0.2130, 0.2660, 0.2130, 0.1094, 0.0360,0.0076, 0.0010])Sum of Gauss Distribution: tensor(1.)

Function #2: create_window(window_size, channel)

当我们生成一维高斯张量时,一维张量本身对我们没有用处。因此,我们必须将它转换为一个2D张量(我们之前谈到的11x11张量)。该函数的步骤如下:


  • 使用高斯函数生成一维张量

  • 该一维张量与其转置交叉相乘得到二维张量(这保持了高斯特性)

  • 增加两个额外的维度,将其转换为四维。(仅当SSIM在计算机视觉中用作损失函数时)

  • Reshape得到PyTorch的权值格式。

Code:window = create_window(11, 3)
print(window.shape)Output:torch.Size([3, 1, 11, 11])

现在我们已经研究了两个函数,让我们看一下主代码!核心的SSIM是通过下面讨论的SSIM()函数实现的。


Function #3: ssim(img1, img2, val_range, window_size=11, window=None, size_average=True, full=False)

在讨论基本内容之前,让我们先看看在计算ssim指标之前函数中发生了什么,


  • 我们设置了标准化像素的最大值

  • 如果在函数调用期间没有提供窗口,我们通过*create_window()*函数初始化高斯窗口。

一旦这些步骤完成,我们开始计算各种值,这是得到最终SSIM分数所需要的。

:由于我们在计算局部统计数据,并且我们需要使其计算更效率,使用的公式有轻微不同。

我们首先计算μ(x),μ(y),它们的平方,以及μ(xy)。这里的channels存储了输入图像的彩色通道的数量。参数的groups用于应用卷积滤波器的所有的输入通道。

channels, height, width = img1.size()mu1 = F.conv2d(img1, window, padding=pad, groups=channels)
mu2 = F.conv2d(img2, window, padding=pad, groups=channels)mu1_sq = mu1 ** 2
mu2_sq = mu2 ** 2mu12 = mu1 * mu2

  • 然后我们继续计算σ(x), σ(y)和σ(xy)的平方。

sigma1_sq = F.conv2d(img1 * img1, window, padding=pad, groups=channels) - mu1_sq
sigma2_sq = F.conv2d(img2 * img2, window, padding=pad, groups=channels) - mu2_s
sigma12 = F.conv2d(img1 * img2, window, padding=pad, groups=channels) - mu12

  • 第三,我们根据提到的公式计算对比度量

contrast_metric = (2.0 * sigma12 + C2) / (sigma1_sq + sigma2_sq + C2)contrast_metric = torch.mean(contrast_metric)

  • 最后,我们计算SSIM分数并根据前面提到的公式返回平均值

numerator1 = 2 * mu12 + C1
numerator2 = 2 * sigma12 + C2
denominator1 = mu1_sq + mu2_sq + C1
denominator2 = sigma1_sq + sigma2_sq + C2ssim_score = (numerator1 * numerator2) / (denominator1 * denominator2)return ssim_score.mean()

代码的执行情况

我们将在三种情况下测试代码,以检查其执行情况。


  • Case #1: 真图像vs假图像

在第一个场景中,我们对2个非常不同的图像执行SSIM。其中一个是真图像,而另一个是假图像。(既然我们测量的是差异,真伪标签本质上是可以互换的,它们仅被用作参考。)

这些图像是:

假图像(左)真图像(右)

代码如下:

Code: img1 = load_images("img1.jpg") # helper function to load images
img2 = load_images("img2.jpg")_img1 = tensorify(img1) # helper function to convert cv2 image to tensors
_img2 = tensorify(img2)ssim_score = ssim(_img1, _img2, 225)print(True vs False Image SSIM Score: ", ssim_score)
Output:
True vs False Image SSIM Score: tensor(0.3385)

  • Case #2: 真图像 vs 加了高斯噪声的真图像

在这个场景中,我们比较了真实的图像和加了噪声的版本。图像如下所示:

加了噪声的真图像(左),真图像(右)

运行与上面一样的代码:

Code: noise = np.random.randint(0, 255, (640, 480, 3)).astype(np.float32)
noisy_img = img1 + noise_img1 = tensorify(img1)
_img2 = tensorify(noisy_img)true_vs_false = ssim(_img1, _img2, val_range=255)print("True vs Noised True Image SSIM Score:", true_vs_false)Output:True vs Noised True Image SSIM Score: tensor(0.0185)

  • Case #3: 真图像 vs 真图像

在最后一种情况下,我们比较真实的图像和它自己。因此,下面显示的图像是与自身进行比较的。如果我们的SSIM代码工作完美,分数应该是**1 **。

在运行下面所示的代码段时,我们可以确认这个给定场景的SSIM得分确实是1

Code:_img1 = tensorify(img1)
true_vs_false = ssim(_img1, _img1, val_range=255)print("True vs True Image SSIM Score:", true_vs_false)Output:True vs True Image SSIM Score: tensor(1.)

总结

在本文中,我们介绍了SSIM背后的理论和实现它的代码。希望你比我更容易理解SSIM。我试着专注于我个人觉得复杂和难以理解的领域,希望可以不仅巩固我所学到的知识,而且在这个过程中,可以帮助到其他人。

—END—

英文原文:https://medium.com/srm-mic/all-about-structural-similarity-index-ssim-theory-code-in-pytorch-6551b455541e


推荐阅读
  • 关于如何快速定义自己的数据集,可以参考我的前一篇文章PyTorch中快速加载自定义数据(入门)_晨曦473的博客-CSDN博客刚开始学习P ... [详细]
  • 本文介绍了利用ARMA模型对平稳非白噪声序列进行建模的步骤及代码实现。首先对观察值序列进行样本自相关系数和样本偏自相关系数的计算,然后根据这些系数的性质选择适当的ARMA模型进行拟合,并估计模型中的位置参数。接着进行模型的有效性检验,如果不通过则重新选择模型再拟合,如果通过则进行模型优化。最后利用拟合模型预测序列的未来走势。文章还介绍了绘制时序图、平稳性检验、白噪声检验、确定ARMA阶数和预测未来走势的代码实现。 ... [详细]
  • 本文讨论了如何使用IF函数从基于有限输入列表的有限输出列表中获取输出,并提出了是否有更快/更有效的执行代码的方法。作者希望了解是否有办法缩短代码,并从自我开发的角度来看是否有更好的方法。提供的代码可以按原样工作,但作者想知道是否有更好的方法来执行这样的任务。 ... [详细]
  • Python教学练习二Python1-12练习二一、判断季节用户输入月份,判断这个月是哪个季节?3,4,5月----春 ... [详细]
  • 颜色迁移(reinhard VS welsh)
    不要谈什么天分,运气,你需要的是一个截稿日,以及一个不交稿就能打爆你狗头的人,然后你就会被自己的才华吓到。------ ... [详细]
  • Oracle分析函数first_value()和last_value()的用法及原理
    本文介绍了Oracle分析函数first_value()和last_value()的用法和原理,以及在查询销售记录日期和部门中的应用。通过示例和解释,详细说明了first_value()和last_value()的功能和不同之处。同时,对于last_value()的结果出现不一样的情况进行了解释,并提供了理解last_value()默认统计范围的方法。该文对于使用Oracle分析函数的开发人员和数据库管理员具有参考价值。 ... [详细]
  • 深度学习中的Vision Transformer (ViT)详解
    本文详细介绍了深度学习中的Vision Transformer (ViT)方法。首先介绍了相关工作和ViT的基本原理,包括图像块嵌入、可学习的嵌入、位置嵌入和Transformer编码器等。接着讨论了ViT的张量维度变化、归纳偏置与混合架构、微调及更高分辨率等方面。最后给出了实验结果和相关代码的链接。本文的研究表明,对于CV任务,直接应用纯Transformer架构于图像块序列是可行的,无需依赖于卷积网络。 ... [详细]
  • 基于dlib的人脸68特征点提取(眨眼张嘴检测)python版本
    文章目录引言开发环境和库流程设计张嘴和闭眼的检测引言(1)利用Dlib官方训练好的模型“shape_predictor_68_face_landmarks.dat”进行68个点标定 ... [详细]
  • Java SE从入门到放弃(三)的逻辑运算符详解
    本文详细介绍了Java SE中的逻辑运算符,包括逻辑运算符的操作和运算结果,以及与运算符的不同之处。通过代码演示,展示了逻辑运算符的使用方法和注意事项。文章以Java SE从入门到放弃(三)为背景,对逻辑运算符进行了深入的解析。 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
  • 本文介绍了使用Spark实现低配版高斯朴素贝叶斯模型的原因和原理。随着数据量的增大,单机上运行高斯朴素贝叶斯模型会变得很慢,因此考虑使用Spark来加速运行。然而,Spark的MLlib并没有实现高斯朴素贝叶斯模型,因此需要自己动手实现。文章还介绍了朴素贝叶斯的原理和公式,并对具有多个特征和类别的模型进行了讨论。最后,作者总结了实现低配版高斯朴素贝叶斯模型的步骤。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • 在本教程中,我们将看到如何使用FLASK制作第一个用于机器学习模型的RESTAPI。我们将从创建机器学习模型开始。然后,我们将看到使用Flask创建AP ... [详细]
  • 【论文】ICLR 2020 九篇满分论文!!!
    点击上方,选择星标或置顶,每天给你送干货!阅读大概需要11分钟跟随小博主,每天进步一丢丢来自:深度学习技术前沿 ... [详细]
  • [翻译]PyCairo指南裁剪和masking
    裁剪和masking在PyCairo指南的这个部分,我么将讨论裁剪和masking操作。裁剪裁剪就是将图形的绘制限定在一定的区域内。这样做有一些效率的因素࿰ ... [详细]
author-avatar
AK7000
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有