OpenCV 实现图像结构相似度算法 (SSIM 算法)

  看完原论文后(英语不太好,只看懂了个大概),大致上明白了它是做什么以及如何实现的等,于是决定写一篇博客,所以该篇文章简单介绍一下 SSIM(有能力的话,看原论文维基比我来介绍更好),并给出我用 opencv 在 C++ 中的 SSIM 算法实现。

  首先什么是 SSIM 算法,该算法主要用于检测两张尺寸相同的图像的相似度,但注意到论文标题的中的 Structural,所以实际上它主要通过两个图像的亮度(l)、对比度(c)、结构(s)这三个要素的测量,而在论文中这三个要素用下面公式来表示:

$$l(x, y) = \frac{(2\mu _x\mu _y + C_1)}{(\mu _{x}^{2} + \mu _{y}^{2} + C_1)}$$

$$c(x, y) = \frac{(2\sigma _{x}\sigma _{y} + C_2)}{(\sigma _{x}^{2} + \sigma _{y}^{2} + C_2)}$$

$$s(x, y) = \frac{(\sigma _{xy} + C_3)}{(\sigma _{x} \sigma _{y} + C_3)}$$

  这里 $\mu _x$ 为均值,$\sigma _{x}$ 为方差,$\sigma _{xy}$ 表示协方差。

  而 SSIM 的一般方程为:

$$ssim(x, y) = [l(x, y)^\alpha \cdot c(x, y)^\beta \cdot s(x, y)^\gamma ]$$

  这里一般 $\alpha$,$\beta$,$\gamma$ 取 $1$,并且令 $C_3 = \frac{C_2}{2}$,这样就得到简化的 SSIM 公式:

$$ssim(x, y) = \frac{(2\mu _x\mu _y + C_1)(\sigma _{xy} + C_2)}{(\mu _{x}^{2} + \mu _{y}^{2} + C_1)(\sigma _{x}^{2} + \sigma _{y}^{2} + C_2)}$$

  实际上公式是十分显然的,我们只要分别求出两张图像的均值以及方差,再对两张图像求协方差就可以带入到 SSIM 公式进行计算,这里计算结果其实得到的是一张图像,它显示了两张图的混叠,借用一下python 以及两张 opencv 的示例图:

  (pic5.png)

  (pic6.png)

import numpy as np
import matplotlib.pyplot as plt
import cv2 as cv
from skimage import data, img_as_float
from skimage.metrics import structural_similarity as ssim


img1 = cv.imread('C:/opencv/samples/data/pic5.png', cv.CV_8U)
img2 = cv.imread('C:/opencv/samples/data/pic6.png', cv.CV_8U)


def mse(x, y):
    return np.linalg.norm(x - y)


fig, axes = plt.subplots()

mse_none = mse(img1, img2)
mssim, s = ssim(img1, img2, data_range=img1.max() - img1.min(), full=True)

label = 'MSE: {:.2f}, SSIM: {:.2f}'

axes.imshow(s, cmap=plt.cm.gray, vmin=0, vmax=1)
axes.set_xlabel(label.format(mse_none, mssim))
axes.set_title('SSIM image')

plt.tight_layout()
plt.show()

  会看到 ssim 输出:

  而评估算法的性能指标公式是:

$$mssim(x, y) = \frac{1}{M}\sum_{j = 1}^{M} ssim(x_j, y_j)$$

  也就是像素取均值。

  该算法的优点在于对图像的结构信息敏感,所以识别率很高,但缺点也有:

  1.如果将原图经过旋转(尺寸不同的话试试旋转 180 度)、平移等变换后再和原图进行比较就显得吃力了:

  2.算法只能处理相同形状的两个图像,但可以多通道。

  最后,给出我的 C++ 实现(不支持多通道图像,以后或许会考虑实现多通道):

/********************************************************************************************************************************************************
* 该部分主要功能实现 SSIM 算法
*
* 关于 SSIM 算法参考文献:https://ece.uwaterloo.ca/~z70wang/publications/ssim.pdf
* 公式:$$ssim(x, y) = \frac{(2\mu _x\mu _y + C_1)(\sigma _{xy} + C_2)}{(\mu _{x}^{2} + \mu _{y}^{2} + C_1)(\sigma _{x}^{2} + \sigma _{y}^{2} + C_2)}$$
*
* 参数:
*   im1: 图像 1
*   im2: 图像 2
*   window: 滑动窗口大小,用于卷积滤波
*   k1: 可调节常数,默认 k1 = 0.01
*   k2: 可调节常数,默认 k2 = 0.03
*   L: 单通道灰度图像像素值范围,默认 L = 255.0
*
* 返回值:
*   相似度指标 (类型:double)
*********************************************************************************************************************************************************/
float ssim(Mat im1, Mat im2, int window = 7, float k1 = 0.01f, float k2 = 0.03f, float L = 255.f)
{
    CV_Assert(im1.size() == im2.size());
    
    int ndim = im1.dims;
    float NP = std::powf(window, ndim);
    float cov_norm = NP / (NP - 1);
    float C1 = (k1 * L) * (k1 * L);
    float C2 = (k2 * L) * (k2 * L);
    
    Mat ux, uy;
    Mat uxx = im1.mul(im1);
    Mat uyy = im2.mul(im2);
    Mat uxy = im1.mul(im2);
    
    blur(im1, ux, Size(window, window), Point(-1, -1));
    blur(im2, uy, Size(window, window), Point(-1, -1));
    
    blur(uxx, uxx, Size(window, window), Point(-1, -1));
    blur(uyy, uyy, Size(window, window), Point(-1, -1));
    blur(uxy, uxy, Size(window, window), Point(-1, -1));
    
    Mat ux_sq = ux.mul(ux);
    Mat uy_sq = uy.mul(uy);
    Mat uxy_m = ux.mul(uy);

    Mat vx = cov_norm * (uxx - ux_sq);
    Mat vy = cov_norm * (uyy - uy_sq);
    Mat vxy = cov_norm * (uxy - uxy_m);

    Mat A1 = 2 * uxy_m;
    Mat A2 = 2 * vxy;
    Mat B1 = ux_sq + uy_sq;
    Mat B2 = vx + vy;
    
    Mat ssim_map = (A1 + C1).mul(A2 + C2) / (B1 + C1).mul(B2 + C2);
    
    Scalar mssim = mean(ssim_map);
    ssim_map.convertTo(ssim_map, CV_8UC1, 255, 0);
    
    imshow("ssim", ssim_map);

    return mssim[0];
}

  参考文献:

    1.https://ece.uwaterloo.ca/~z70wang/publications/ssim.pdf

    2.https://en.wikipedia.org/wiki/Structural_similarity

  算法实现参考:

    1.https://www.cns.nyu.edu/~lcv/ssim/ssim_index.m

    2.https://github.com/PAHdb/ssim/blob/master/ssim.pro

    3.skimage.metrics.structural_similarity 源码

猜你喜欢

转载自www.cnblogs.com/darkchii/p/12679103.html