FFmpeg计算图像的SSIM的原理

发布于:2024-05-04 ⋅ 阅读:(28) ⋅ 点赞:(0)

SSIM算法基于HVS更擅长从图像中提取结构信息的事实,并且利用结构相似度来计算图像的感知质量。
在Z. Wang等人的论文Multi-scale structural similarity for image quality assessment中也提到, S S I M SSIM SSIM算法要好于当时的其它的感知图像质量指标。
SSIM的计算公式:
在这里插入图片描述
SSIM计算中的图像分割
==在整幅图片的跨度上,图像亮度的均值和方差变化较为剧烈;并且图像上不同区块的失真程度也有可能不同;再者人眼睛每次只能聚焦于一处,更关注局部数据而非全局数据。==因此如上的 S S I M SSIM SSIM算法不能直接作用于一整副图像。
在论文Image quality assessment: From error visibility to structural similarity中,作者采用 11 × 11 11 \times 11 11×11的滑动窗口将整副图像分割为 N N N个patch,然后计算每一个patch的 S S I M SSIM SSIM,最后计算所有patch的 S S I M SSIM SSIM值的平均数( M e a n    S S I M : M S S I M Mean \ \ SSIM:MSSIM Mean  SSIM:MSSIM)作为整副图像的 S S I M SSIM SSIM
为避免滑动窗口带来的块效应,在计算每个patch的均值 μ \mu μ和方差 σ 2 \sigma^2 σ2时,作者采用 σ = 1.5 \sigma=1.5 σ=1.5的高斯卷积核作加权平均。整副图像有 N N N个patch,则 M S S I M MSSIM MSSIM的计算公式为:
在这里插入图片描述
FFmpeg计算SSIM的实际实现

FFmpeg提供了计算 S S I M SSIM SSIM的实现:
https://github.com/FFmpeg/FFmpeg/blob/master/tests/tiny_ssim.c。
从代码注释中看到:为提升算法性能,没采用论文中的高斯加权方式计算每个patch的 S S I M SSIM SSIM,而采用了 8 × 8 8 \times 8 8×8的块来计算每个patch的 S S I M SSIM SSIM
在这里插入图片描述
standard approximation of overlapped 8x8 block sums

解释一下注释中的standard approximation of overlapped 8x8 block sums 的含义。分解成两个部分来解释:overlapped 8x8 block和sums。
overlapped 8x8 block的含义:
FFmpeg在计算图像 S S I M SSIM SSIM时,首先以 4 × 4 4 \times 4 4×4的块大小把图1所示的分辨率为 W × H W \times H W×H的图像:
在这里插入图片描述
图1:原始图像

在这里插入图片描述
图2:分割后的图像

对图2中的每一块用 b l o c k ( i , j ) block(i,j) block(i,j)来表示(图2中的红色块),FFmpeg使用 b l o c k ( i , j ) block(i,j) block(i,j)及其上、右、右上块(图2中的绿色块)来计算 S S I M : S S I M ( x i j , y i j ) SSIM:SSIM(x_{ij},y_{ij}) SSIM:SSIM(xij,yij)
b l o c k ( i , j ) block(i,j) block(i,j)及其上、右、右上块构成一个 8 × 8 8\times8 8×8的像素块,并且该 8 × 8 8\times8 8×8块和计算 b l o c k ( i , j + 1 ) block(i,j+1) block(i,j+1) S S I M SSIM SSIM用到的 8 × 8 8\times8 8×8的块存在重合像素,这就是注释中的overlapped 8x8 block的真正含义。
根据如上规则: i ∈ [ 1 , H 4 ] , j ∈ [ 0 , W 4 − 1 ] i \in [1,\frac{H}{4}],j \in [0,\frac{W}{4}-1] i[1,4H],j[0,4W1],即第0行和最后一列的块不会计算 S S I M SSIM SSIM。最后FFmpeg中的 S S I M SSIM SSIM公式为:
在这里插入图片描述
sums的含义

如前所述,分析了FFmpeg计算图像的 S S I M SSIM SSIM的整体思路,接下来分析FFmpeg是如何计算 b l o c k ( i , j ) block(i,j) block(i,j) S S I M ( x i j , y i j ) SSIM(x_{ij},y_{ij}) SSIM(xij,yij)的,即可解释sums的含义。
首先利用源码中的函数ssim_4x4x2_core()来计算 b l o c k ( i , j ) block(i,j) block(i,j)块的结构相似性指标,包含4个指标:
s1:参考图像在 b l o c k ( i , j ) block(i,j) block(i,j)块的像素之和
s2:受损图像在 b l o c k ( i , j ) block(i,j) block(i,j)块的像素之和
ss:参考图像和受损图像在 b l o c k ( i , j ) block(i,j) block(i,j)块的像素平方之和
s12:参考图像和受损图像在 b l o c k ( i , j ) block(i,j) block(i,j)块的对应像素乘积之和。
在这里插入图片描述
如上4个指标是后续会用到的sums(4类sum,称为sums),该sums也就是overlapped 8x8 block sums中的sums的概念。

利用sums计算各4x4块的SSIM
接下来利用该sums值计算 S S I M SSIM SSIM。为提升效率,FFmpeg会按照行来计算每一行的各个块的sums数据,并将每个行块的sums数据存储在长度为 W 4 \frac{W}{4} 4W的数组指针sum((int(*)[4]))中
sum指针有两种:

sum0:存储当前行的各块的sums结果
sum1:存储当前行的上一行的sums结果

先计算第 i − 1 i-1 i1行块和第 i i i行块的sums结果,并分别存入sum1和sum0中。然后遍历第 i i i行块的每一个块,并利用sum1和sum0中计算的结果来计算每一块的 S S I M SSIM SSIM
函数ssim_end4()展示了如何利用 b l o c k ( i − 1 , j ) block(i-1,j) block(i1,j) b l o c k ( i − 1 , j + 1 ) block(i-1,j+1) block(i1,j+1) b l o c k ( i , j ) block(i,j) block(i,j) b l o c k ( i , j + 1 ) block(i,j+1) block(i,j+1)的sums信息来计算 S S I M ( x i j , y i j ) SSIM(x_{ij},y_{ij}) SSIM(xij,yij)

  1. 先对4个块的sums结果进行加和处理,得到 8 × 8 8\times8 8×8块的sums结果
  2. 利用该 8 × 8 8\times8 8×8块的sums来计算 b l o c k ( i , j ) block(i,j) block(i,j) S S I M SSIM SSIM
    源码中ssim_end1()展示了如何利用 8 × 8 8\times8 8×8块的sums信息来计算 S S I M SSIM SSIM。具体的计算方法如下。
    将红色区块 b l o c k ( i , j ) block(i,j) block(i,j)的图像放大一点,如图3所示。我们接下来计算其 S S I M SSIM SSIM
    在这里插入图片描述
    图3: b l o c k ( i , j ) block(i,j) block(i,j)的示意图

在计算时,首先将4个区块的sums值求和,得到 8 × 8 8\times8 8×8区块的sums值,分别为:
在这里插入图片描述
利用如上的公式对 S S I M SSIM SSIM的公式进行计算可以得到:
在这里插入图片描述
FFmpeg源码中,对 C 1 C_1 C1 C 2 C_2 C2的定义中的因子64或63也是根据上面的公式,但是从公式看,FFmpeg对ssim_c1的计算少乘了64:
在这里插入图片描述
为简化计算,FFmpeg还做了如下的定义:

在这里插入图片描述
最终在FFmpeg中,计算 S S I M SSIM SSIM的公式为:
在这里插入图片描述
如上公式就是源码的函数ssim_end1()中最终的计算方式。

利用各块的SSIM计算图像的SSIM

计算完所有块的 S S I M SSIM SSIM之后,所有块的平均 S S I M SSIM SSIM作为该图像的 S S I M SSIM SSIM
在这里插入图片描述

编码过程中的技巧
FFmpeg计算 S S I M SSIM SSIM的实现中,为提升效率和抽象代码逻辑,利用很多的编程技巧,如:

  1. 计算YUV各分量图像宽度时用w >> !!i
  2. 为了避免对第0行的特殊处理,采用两层循环来处理
  3. 计算每一行的各块的sums信息时,为了降低循环次数,每次循环计算2个块的sums结果,ssim_4x4x2_core的函数名可能就是这么来的。
  4. 计算每一行的各块的 S S I M SSIM SSIM时,为了降低循环次数,每次循环计算4个块的 S S I M SSIM SSIM,源码中的ssim_end4的函数名可能就是这么来的。

网站公告

今日签到

点亮在社区的每一天
去签到