SSIM算法
在图像重建,压缩领域,有很多算法可以计算输出图像与原图的差距,在SSIM算法出现之前,最长用的是MSE(Mean Square Error loss)算法,他的公式很简单:
也就是计算重建后图像与输入图像的像素值差的平方和,然后在全图上求平均
但这个计算方法明显是有问题的,例如两张图片假如只是亮度不同,MSE可能很大,而一张模糊处理后的图与原图,可能MSE就很小。于是有人在Image Quality Assessment: From Error Visibility to Structural Similarity这篇论文里提出了SSIM算法。
人类在衡量两幅图的差距时,更偏重于两幅图的结构相似性,而不是像MSE那样,逐像素计算差距,SSIM就是一种基于结构相似度的度量。有两幅图x,y,他们的相似度按三个维度进行比较:亮度(luminance)l(x,y),对比度(contrast)c(x,y),和结构(structure)s(x,y),最终x与y的相似度为这三者的函数:
作者设计了三个公式定量计算这三者的相似性,公式的设计遵循三个原则:
对称性:s(x,y)=s(y,x)
有界性:s(x,y)≤1
极限值唯一:s(x,y)=1 当且仅当 x = y
首先研究亮度。如果一幅图有 N 个像素点,每个像素点的像素值为xi,那么该图像的平均亮度为:
亮度相似度为:
C1是为了防止分母为零的情况,且
K1默认0.01,L是灰度动态范围,根据像素位深决定,8位的话,L=2^8-1=255
然后研究对比度,所谓对比度,就是像素值的标准差:
对比度的相似度计算公式:
其中,
第三步,通过归一化向量的关系,研究结构相似度
两个归一化向量: (x-μx)/σx和(y-μy)/σy
他们的余弦相似度:
结构相似度计算公式:
C3一般取C2/2
最后,SSIM公式:
yuv格式
在FFmpeg实现好的tiny_ssim里,视频文件是以yuv格式读入的
yuv类似于rgb,Y代表明亮度,U代表色度,V代表浓度,通常UV一起描述影像色彩和饱和度,用于指定像素的颜色。如果只有Y分量而没有UV分量,则图像就是黑白电视机里的那样。
人眼对色彩的敏感度低于对亮度的敏感度,即使适当降低色彩的采样人眼并不会有明显的感觉。所以并不是每个像素点都需要包含了 Y、U、V 三个分量,根据不同的采样格式,可以每个 Y 分量都对应自己的 UV 分量,也可以几个 Y 分量共用 UV 分量。相比 RGB,能够节约不少存储空间。
YUV图像的主流采样方式主要有三种:
- YUV 4:4:4采样
- YUV 4:2:2 采样
- YUV 4:2:0 采样
一个分量信息占8bit,也就是一个字节,假如采用444采样,则一个像素信息占3个字节。为什么叫4:4:4 , 意思就是每4个像素里的数据有4个Y, 4个U, 4个V。
在tiny_ssim中输入的yuv格式视频文件,是YUV420格式,YUV 4:2:0 并不意味着不采样 V 分量。它指的是对每条扫描线来说,只有一种色度分量以 2:1 的采样率存储,相邻的扫描行存储不同的色度分量。也就是说,如果第一行是 4:2:0,下一行就是 4:0:2,在下一行就是 4:2:0,以此类推。
YUV 数据有两种存储格式:平面格式(planar format)和打包格式(packed format)。
- planar format:先连续存储所有像素点的 Y,紧接着存储所有像素点的 U,随后是所有像素点的 V。
- packed format:每个像素点的 Y、U、V 是连续交错存储的。
YUV420有两种小类,YUV420P与YUV420SP:
YUV420P 是基于 planar 平面模式进行存储,先存储所有的 Y 分量,然后存储所有的 U 分量或者 V 分量。
YUV420SP 也是基于planar 平面模式存储,与 YUV420P 的区别在于它的 U、V 分量是按照 UV 或者 VU 交替顺序进行存储。
在tiny_ssim中负责输入的YUV格式,就是YUV420P的格式(也叫YV12)
FFmpeg代码里的术语
ssd
Sum of Squared Differences 估算值与估算对象差的平方和
PSNR
Peak Signal-to-Noise Ratio 峰值信噪比
plane
平面/分量,指Y,U,V
stride (间距,跨距)
stride为什么会出现
这个参数看起来似乎没什么用,因为它的值和图像的宽度一样。但是那是大多数情况下,一旦遇到它和宽度不一样的时候,如果你不了解它的含义,那么程序肯定要出问题。可是为什么有时候它等于宽度,有时候又不等于呢?这就和它的含义有关了。
我们都知道现在计算机的cpu都是32位或者64位的cpu,他们一次最少读取4、8个字节,如果少于这些,反而要做一些额外的工作,会花更长的时间。所有会有一个概念叫做内存对齐,将结构体的长度设为4、8的倍数。
间距也是因为同样的理由出现的。因为图像的操作通常按行操作的,如果图像的所有数据都紧密排列,那么会发生非常多次的读取非对齐内存。会影响效率。而图像的处理本就是一个分秒必争的操作,所以为了性能的提高就引入了间距这个概念。stride的含义
间距就是指图像中的一行图像数据所占的存储空间的长度,它是一个大于等于图像宽度的内存对齐的长度。这样每次以行为基准读取数据的时候就能内存对齐,虽然可能会有一点内存浪费,但是在内存充裕的今天已经无所谓了。
stride的值
所以如果图像的宽度如果是内存对齐长度的整数倍,那么间距就会等于宽度,而现在的cpu通常一次读取都是4个字节,而我们通常见到的分辨率都是4的整数倍,所以我们通常发现间距和图像的宽度一样(这里通常指rgb32格式或者以通道表示的yuv420p格式的y通道)。但是如果遇到一些少见的分辨率时间距和图像的宽度就不一样。