最近在研究图像质量评价方面的东西,正好对SSIM的计算有了比较深入的理解,特写下以记录.
SSIM,结构相似性.原始公式如下:
其中,分别代表原图像,目标图像,均值,标准差(无偏).
代表两个常数.目前流行的计算方式是上面公式的改进版Image Quality Assessment: From Error Visibility to Structural Similarity
(1)式是在全局图像上计算统计量,而这篇文章觉得用全局不好,故将(1)式改为了局部计算再平均,这个局部就像卷积一样的重叠小窗口.而每个小窗口的求平均也不再是使用简简单单的求和再除以总数的方式,而是使用高斯分布的权重(可以简单理解为换了一种求平均的方式,本质上还是求平均).下面我分析下他的代码实现.代码源自BasicSR
def _ssim(img, img2):
"""Calculate SSIM (structural similarity) for one channel images.
It is called by func:`calculate_ssim`.
Args:
img (ndarray): Images with range [0, 255] with order 'HWC'.
img2 (ndarray): Images with range [0, 255] with order 'HWC'.
Returns:
float: SSIM result.
"""
c1 = (0.01 * 255)**2 #没的说,就是那个常数C1
c2 = (0.03 * 255)**2 #没的说,就是那个常数C2
kernel = cv2.getGaussianKernel(11, 1.5) #获取一维高斯分布的权重
window = np.outer(kernel, kernel.transpose()) #获取二维高斯分布的权重
mu1 = cv2.filter2D(img, -1, window)[5:-5, 5:-5] #卷积操作取img的均值\mu_1,也就是说,mu1现在的每个点是对应于img相应窗口的均值.
mu2 = cv2.filter2D(img2, -1, window)[5:-5, 5:-5] #卷积操作取img2的均值\mu_2,也就是说,mu2现在的每个点是对应于img2相应窗口的均值.
mu1_sq = mu1**2 #mu1每个点的平方相当于img1每个窗口均值的平方,即\mu_1^2
mu2_sq = mu2**2 #同上
mu1_mu2 = mu1 * mu2 #两个图片对应位置窗口均值相乘,即\mu_1*\mu_2
sigma1_sq = cv2.filter2D(img**2, -1, window)[5:-5, 5:-5] - mu1_sq #求方差的变体公式,不再使用原始公式求方差的方式.我猜测是为了简化计算.
sigma2_sq = cv2.filter2D(img2**2, -1, window)[5:-5, 5:-5] - mu2_sq #同上
sigma12 = cv2.filter2D(img * img2, -1, window)[5:-5, 5:-5] - mu1_mu2 #求协方差的变体公式.
ssim_map = ((2 * mu1_mu2 + c1) * (2 * sigma12 + c2)) / ((mu1_sq + mu2_sq + c1) * (sigma1_sq + sigma2_sq + c2)) #对应于原始公式,只不过这里求得的是各个窗口的SSIM
return ssim_map.mean() #求总体的SSIM
个人理解,如有错误,不吝赐教.