SSIM(structural similarity index)

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

如何计算结构相似性

在计算两幅图像的结构相似性,需要高斯模板用来作为一个像素区域的概率值,进而计算得到某个区域的像素平均值,标准差,方差和协方差等概念。除此之外给出一组公式用来计算SSIM:

深度学习 图片相似性 指标_算法

深度学习 图片相似性 指标_相似度_02

深度学习 图片相似性 指标_概率论_03

当里面的参数深度学习 图片相似性 指标_算法_04深度学习 图片相似性 指标_概率论_05

深度学习 图片相似性 指标_相似度_06

里面的深度学习 图片相似性 指标_计算机视觉_07分别代表X这幅图片里面的均值。

下面用图示的方式演示涌口滑动的过程:

深度学习 图片相似性 指标_算法_08


上图的左边是高斯核尺寸是深度学习 图片相似性 指标_相似度_09,加入我们将要从图片的第深度学习 图片相似性 指标_相似度_10个位置与高斯核进行运算(如果熟悉卷积神经网络的话大家肯定就很快就可以理解)。然后我们使用高斯模块在整个图像上移动得到一个SSIM_MAP,其尺寸就是深度学习 图片相似性 指标_算法_11

高斯核的实现

按照前人的做法是使用一个深度学习 图片相似性 指标_相似度_09的高斯模板滑动整个图像得到一个SSIM_MAP之后再计算其平均值作为两幅图像的SSIM。这里需要强调的是区域里面的每个像素出现的概率是符合高斯分布的,而不是均匀分布。故我们首先要设计一个高斯核函数确定权重,计算公式如下:
深度学习 图片相似性 指标_算法_13

区域平均值实现

由于区域里面的每个点具有不同的权重,在这里我们认为从图片的深度学习 图片相似性 指标_相似度_10点开始与高斯核匹配的的平均值是:
深度学习 图片相似性 指标_计算机视觉_15

区域方差实现

根据方差的公式我们写作:
深度学习 图片相似性 指标_概率论_16

区域协方差实现

协方差公式:
深度学习 图片相似性 指标_深度学习 图片相似性 指标_17

代码实现

由于上面的几部分已经将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)