SSIM(结构相似性指数)是一种用于衡量两个图像之间结构相似性的指标。它是一种全参考图像质量评价指标,用于衡量两个图像在亮度、对比度和结构方面的相似程度。
SSIM 被广泛应用于图像处理领域,尤其在图像压缩、图像恢复、图像质量评价等方面具有重要作用。与传统的 PSNR(峰值信噪比)相比,SSIM 考虑了人眼对图像感知的特性,更能反映人眼感知到的图像质量。
SSIM 的计算基于以下三个方面的信息:
亮度相似性(Luminance Similarity):衡量两个图像的亮度是否相似。通过计算两个图像的亮度信息的均值和方差来评估亮度相似性。
对比度相似性(Contrast Similarity):衡量两个图像的对比度是否相似。通过计算两个图像的像素值的标准差和协方差来评估对比度相似性。
结构相似性(Structure Similarity):衡量两个图像的结构是否相似。通过计算两个图像的像素值的协方差来评估结构相似性。
SSIM 的计算结果范围为 -1 到 1,值越接近 1 表示两个图像结构相似性越高,质量越好;值越接近 -1 表示结构相似性越低,质量越差。
SSIM 的优点是能够考虑到图像的结构信息,更贴近人眼的感知结果。然而,SSIM 也有一些局限性,例如它对亮度、对比度和结构的权重分配是固定的,不适用于所有类型的图像。
SSIM 的计算公式如下:
SSIM(x, y) = [l(x, y) * c(x, y) * s(x, y)]^α
其中,x 和 y 是要比较的两个图像。
l(x, y) 表示亮度相似性,计算方式为:
l(x, y) = (2 * μx * μy + c1) / (μx^2 + μy^2 + c1)
其中,μx 和 μy 分别是 x 和 y 的像素值的均值,c1 是一个常数,用于防止分母为零。
c(x, y) 表示对比度相似性,计算方式为:
c(x, y) = (2 * σxy + c2) / (σx^2 + σy^2 + c2)
其中,σx 和 σy 分别是 x 和 y 的像素值的标准差,σxy 是 x 和 y 的像素值的协方差,c2 是一个常数,用于防止分母为零。
s(x, y) 表示结构相似性,计算方式为:
s(x, y) = (σxy + c3) / (σx * σy + c3)
其中,c3 是一个常数,用于防止分母为零。
α 是一个指数参数,一般取值为 1。
以下是使用 PyTorch 实现 SSIM 的示例代码:
import torch
import torch.nn.functional as F
def gaussian(window_size, sigma):
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):
_1D_window = gaussian(window_size, 1.5).unsqueeze(1)
_2D_window = _1D_window.mm(_1D_window.t()).float().unsqueeze(0).unsqueeze(0)
window = Variable(_2D_window.expand(channel, 1, window_size, window_size).contiguous())
return window
def ssim(x, y, window_size=11, size_average=True):
c1 = 0.01 ** 2
c2 = 0.03 ** 2
window = create_window(window_size, 1)
# window = torch.Tensor([1.0 / (window_size ** 2)] * window_size ** 2).view(1, 1, window_size, window_size).to(x.device)
mu1 = F.conv2d(x, window, padding=window_size // 2, groups=x.shape[1])
mu2 = F.conv2d(y, window, padding=window_size // 2, groups=y.shape[1])
mu1_sq = mu1 ** 2
mu2_sq = mu2 ** 2
mu12 = mu1 * mu2
sigma1_sq = F.conv2d(x * x, window, padding=window_size // 2, groups=x.shape[1]) - mu1_sq
sigma2_sq = F.conv2d(y * y, window, padding=window_size // 2, groups=y.shape[1]) - mu2_sq
sigma12 = F.conv2d(x * y, window, padding=window_size // 2, groups=x.shape[1]) - mu12
ssim_map = ((2 * mu12 + 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) # 分别对每个通道求均值,然后再对所有通道求均值
# 示例使用
x = torch.randn(1, 3, 256, 256)
y = torch.randn(1, 3, 256, 256)
ssim_value = ssim(x, y)
print(ssim_value)
在上述示例中,我们定义了一个 ssim()
函数来计算 SSIM。该函数接受两个输入张量 x
和 y
,以及一个窗口大小 window_size
和一个布尔值 size_average
,用于指定是否对输出结果进行平均。
在函数内部,我们首先定义了常数 c1
和 c2
,然后创建了一个窗口权重张量 window
。接下来,我们使用 F.conv2d()
函数来计算输入张量的均值和方差。最后,我们根据 SSIM 的公式计算相似性,并返回结果。