SSIM 是2004年提出来的,原始论文在这里。SSIM相较PSNR更符合人眼的主观感觉,因此在很多图片视频任务中广泛使用,包括压缩,降噪等等。本文简单介绍下SSIM公式,并根据论文On the Mathematical Properties of the SSIM对SSIM做了分析,最后总结了如何在编码器中针对SSIM来做优化。

SSIM简介

SSIM的计算公式如下:

其中l(x, y)代表x, y对于mean的相似度,c(x, y)代表的contrast的对比度,s(x, y)代表的是结构的相似度;他们的计算方式分别如下

其中$\mu_x$, $\mu_y$ 分别为块的均值;其中$\mu_x$, $\mu_y$相差越大,$l(x, y)$就越小;

而对比度相似度c(x, y)的计算方式与l(x, y)的相似,具体如下

结构相似度s(x, y)无法表示成一个数,作者是把他转换成归一化的向量,然后计算下余弦相似度:

其中$\sigma_{xy}$为x, y的covariance,expected value of the product of their deriations from there individual expected values. 计算方式为

也就是两个相对于均值一起变动的节奏或者波形是否一致。可以考虑一维的情况,协方差在计算的时候会减去均值。

如果$C_3 = C_2/2$,最终的SSIM的公式为

因此SSIM(x, y) 小于1, 大于-1;

On the Mathematical Properties of the SSIM

作者首先把SSIM分成两部分,

然后使用$d_1 = \sqrt{1 - S_1}$和$d_2 = \sqrt{1 - S_2}$作为normalized metrics; 作者先证明了NRMSE是一个metrics,然后说明$d_1$和$d_2$是NRMSE的一种。NRMSE的定义如下

之后通过构建$D_2 := ||d(x,y)||_2 = \sqrt{2 - S_1(x,y) - S_2(x, y)}$来表示一个距离,$||d(x,y)||_2$是$\sqrt{1-SSIM(x,y)}$的低阶近似。

在图片或者视频的很多处理中,大部分时候mean都是保持一致的,因此SSIM中的$c(x,y)$和$s(x,y)$更加重要。作者也通过计算SSIM和$D_2$的相关性,可以看出图像的均值相差不大的情况下,SSIM和其他$D_2$的相关性是很高的,大于0.967;

如何针对SSIM做优化

从以上分析可以看到,SSIM可以近似表示为$d_2$,$d_2$可以理解为相对损失,因此相同的SSIM分数下,复杂的块,也就是$\sigma_x$ 更大的块,可以允许有更大的损失。也可以把SSIM理解为weighted MSE,加权的因子是$\frac{1}{\sigma_x}$,因此简单的块有更大的权重。在图片或视频压缩中,可以针对简单区域分配更多的比特从而优化SSIM的表现。

Introduction

图像压缩属于图片处理的一种,经过处理后的图片,文件体积一般会变小。

图像压缩分为无损压缩和有损压缩,无损压缩对质量没有影响,压缩率比较低;而有损压缩则通过牺牲一定的质量,达到更高的压缩率。一般压缩比越高,体积越小,质量越差。

因此在评测质量的时候,分为客观评测和主观评测。其中客观评测是指用一些算法来对图像打分,而主观评测则是人眼对图像质量进行打分。毫无疑问,主观质量评测费时费力,而且不同人评测的结果不一样,包含了很多噪声。同时,主观评测无法给出精度很高的分数,只能给出一个粗略的分数;而客观质量与主观质量相反,精准度高,容易计算,但客观质量指标的缺点是他和主观不一定吻合,目前也没有完美的客观指标;

图片的内容千奇百遍,不同场景下的内容特征会差异很大。因此在评测的时候需要根据自己的需要选择出代表图片来,使用这些图片来做评测;

图像压缩通常情况下会涉及比较复杂的操作,一般都比较费时,在比较的时候需要考虑到耗时这个因素;因此在评判一个压缩算法,除了需要考虑质量和体积,还需要考虑耗时。

Test-set

在评测的时候,需要先选定测试集,可以使用公开的测试集,也可以使用自己定制的测试集。以下是几个测试集可供选择

Cloudinary CID22

JPEG AIC-3

Kodak & CLIC

客观评测

针对图片的客观指标是一个很活跃的研究领域,每年都会有新的客观指标出来。常用的客观指标有PSNR, SSIM, VMAF.SSIM, DSSIM, ssimara 2, VMAF。

PSNR

PSNR是pixel-level的指标,它会对比两张图片中每个像素的差异并加权平均。PSNR没有考虑人眼视觉模型,和主观不能很好的吻合,但计算简单。

后面基于PSNR开发了HVS-PSNR,WPSNR和XPSNR,其中WPSNR在计算的时候对每个块赋值一个权重,最终的PSNR是一个加权的PSNR.

SSIM

SSIM是2004年提出的,它同时考虑了均值,对比度和结构信息,比PSNR更符合人眼的主观。SSIM的具体公式如下:

$SSIM(x, y) = \frac{(2\mux\mu_y + C1)(2\sigma{xy} + C_2)}{(\mu_x^2 + \mu_y^2 + C_1)(\sigma_x^2 + \sigma_y^2 + C_2)}$

基于SSIM也有很多变体,包括MS-SSIM,DSSIM等

SSIM考虑到了人眼视觉效应,对平坦区域的失真更加敏感,这和人的主观是吻合的。

VMAF

VMAF是由Netflix提出来的,内部计算了多个指标,如Visual Quality Fidelity(VIF), Detail Loss Measure(DLM) 以及Temporal Information(TI),最终采用机器学习(SVM)的方式对多个指标进行融合得到最终的分数。VMAF与主观相关性较高,在业界内被广泛使用。

VMAF相关的模型有VMAF-Neg(VMAF-Non-enahncement-gain),

主观评测

主观评测主要由人员做主观打分,并对打分后做统计分析,目前主流的评测方法都是基于ITU-R BT.500来做的.

一般使用Double Stimulus Continuous Quality Scale的方式进行评测,评测的时候把原图和测试图并排放置,并随机打乱顺序,由观看者对两幅图片进行打分,分值是5分制,分别表示Excellent, Good, Fair, Poor and Bad. 因此这是一种全参考的设置方式。

还有种常见的主观评测是GSB(Good:Same:Bad)评价,适合于两个编码器对比分析;

除此以外,还可以采用以下几种方式:

ACR(Absolute Category Rating): 每次评估一个视频,对视频打分; 量级分为(Bad, Poor, Faire, Good, Excellent)

DCR(Differential Comparison Rating): 每次评估一对视频(源视频,处理后视频),对处理后视频相对于源视频的差异进行打分;(Imperceptible, Perceptible but not annoying, Slightly annoying, Annoying, very annoying)

PC/CCR: 每次评估一对视频(都是处理后的视频,对比两者的处理效果),给出哪个视频更好的打分;打分的量级分为(Much worse, worse, slightly worse, the same, slightly better, Better, Much Better)

打分之后一般使用MOS(Mean Opinion Score)或者DMOS(Differential Mean Opinion Score)来统计出分数来;MOS常用与ACR,分数越高,质量越好;而DMOS常用于DCR,分数越低,代表与原视频越接近).

打完分之后需要做统计分析(ANOVA, T-Test),判断下观察到的差异是因为质量变化还是因为随机的改变;另外可以检查和客观指标的吻合度,同时分析对于不同的subgroup的表现如何。

速度对比

可以计算编码/解码所用时间,内存占用率等来判断下编码器的速度;

不同Codec的对比

因为编码器在不同质量等级下生成图片的质量和大小都不一样,如果只跑出一个数据来,因为质量和体积都不一样,无法直接比较。工业界常用的方式是使用多个质量等级编码出多个数据(≥ 4),然后计算出对应的质量和体积,最后计算BD-Rate。例如有两个JPEG编码器A和B,其中A是anchor编码器,B是测试编码器。计算BD-Rate的时候,可以用A和B跑出四个质量等级的码流,分别计算码流对应的码率和质量,最后计算BD-Rate,如果BD-Rate为正值,如3%,可以解释为要达到同样的客观指标,B要比A多用3%的码率,也就是B的压缩率更低。如果BD-Rate为负值,如-10%,则说明B要比A少用10的码率,即B的压缩率更低。

注意计算BD-Rate的时候,需要考虑到选用的客观指标和质量等级,这些需要和实际场景中的匹配上。BD-Rate的介绍可以参考How to use BD-Rate to evaluate codecs

评测框架可以参考WebM codec-compare

总结

图片压缩的评测并不简单,中间有很多的坑,在实践中需要选好测试集,并交叉对比主观评测的结果和客观评测的结果,作为一个参考。在涉及多个codec的时候,可以采用BD-Rate来比较两个Codec。

BD-BR计算和注意事项

在视频压缩领域,常常需要比较不同算法或者不同编码器的优劣,而评价一个编码器很重要的一个指标是压缩率。计算压缩率需要比较质量和码率,相同画质下码率越低,压缩率越高;或者相同码率下,质量越高,压缩率也越高。

在实际比较的时候,因为很多的编码算法会同时影响质量和码率,导致码率或者质量无法卡齐,不好直接对比压缩率。在这种情况下就需要BD-Rate这个工具。

BD-BR全称是Bjontegaard delta bitrate (BD-BR) ,与2001年提出来的。它可以定量比较不同编码器的压缩性能,这些年一直被广泛用于编码器的评测;它主要是通过计算两条RD曲线上之间的差距来度量不同编码器的优劣,具体可以参见下图:

Figure1

R-D曲线的X轴是码率,Y轴是客观指标。计算的时候可以计算R-D曲线针对X轴的积分(BD-PSNR),也可以计算R-D曲线对于Y轴的积分(BD-Rate);BD-PSNR可以认为是delta psnr,因此正值代表了画质提升;BD-Rate可以认为是delta rate,因此负值代表了码率节省。这里重点说明下BD-Rate.

计算方式

假如要评测两个编码器A和B的压缩率,衡量质量的标准使用PSNR;在计算BD-Rate之前,需要先使用4个或更多个码率点来进行编码,例如2Mbps, 2.5Mbps, 3Mbps和4Mbps,编码完成后统计对应4个码率点的码率和质量(PSNR);

  1. 对码率先做对数变换,这个主要是为了防止不同质量等级下码率差异太大,导致高码率对整个的计算影响太大;
  1. 使用interpolation函数拟合出两条Rate-Distortion曲线,X轴是Rate,Y轴是PSNR; 例如$r = a + b D + c D^2 + d*D^3$
  2. 计算两条曲线对于Y轴积分的差,积分的时候只对Distortion有重叠的区域做积分;因此需要先计算出$D{high}$和$D{low}$
  3. 对Distortion部分做normalize,也就是除以$D{high} - D{low}$
  4. 最后再通过幂函数恢复到原来的域

最后的整个公式就是

以下是Netflix开源的bdrate计算脚本: https://github.com/Netflix/vmaf/tree/master/python/vmaf/tools

注意事项

虽然BD-Rate对应着码率节省,但实际计算的时候BD-Rate并不等于码率节省的比例。

另外BD-Rate有单调性的假设,也就是质量会随着码率升高而升高。

一般来说,0.5db提升等同于-10%的BD-Rate;

Mermaid是用文本来描述Diagram,因为博客使用了hexo和Next,所以尝试在把Mermaid用起来,可以编写一些代码流程;

  1. Install Mermaid: $ npm install hexo-filter-mermaid-diagrams
  2. Modify Next config, like _config.next.yml,把mermaid打开。
    1
    2
    3
    4
    5
    6
    7
    # Mermaid tag
    mermaid:
    enable: true
    # Available themes: default | dark | forest | neutral
    theme:
    light: default
    dark: dark
  3. Modify hexo config, _confg.yml:
    1
    2
    3
    highlight:
    exclude_languages:
    - mermaid
  4. Start to create diagram using Mermaid syntax, like
graph TD
A[Hard] -->|Text| B(Round)
B --> C{Decision}
C -->|One| D[Result 1]
C -->|Two| E[Result 2]

What is Webp?

Webp是一种图像格式,支持单张图片,动图;
什么平台支持?
如何使用?

Open-Source projects

libwebp: https://chromium.googlesource.com/webm/libwebp
mirror: https://github.com/webmproject/libwebp/

User Guide

Options

  1. q: 指定quality factor
  2. alpha_q: 指定透明通道的压缩率
  3. preset: 指定图片的type
  4. -z: 指定lossless压缩的效率
  5. m: 压缩的速度档次,0最快,6是最慢
  6. segment: 指定要使用的segments,默认是4
  7. -size: 指定目标size (in bytes)
  8. -psnr: 指定目标的PSNR
  9. -sns: spatial noise shaping, important
  10. -f: filter strength, default 60,是指loop filter的强度
  11. -sharpness: filter sharpness, 0: most sharp, 7: 最小sharp
  12. -strong: use strong filter
  13. -sharp_yuv: use sharper RGB->YUV的转换
  14. -partition_limit
  15. -pass: analysis pass number
  16. -qrange,指定最小和最大QP的范围
  17. -mt: 使用multi-threading
  18. -alpha_method: 指定透明通道压缩的方法
  19. -alpha_filter: predictive filtering for alpha plane
  20. -exact: preserve RGB value in transparent area; 也就是不修改完全透明区域的
  21. -noalpha: 丢掉alpha的特征
  22. -lossless: 无损压缩图像
  23. -near_lossless: use near-lossless image preprocessing
  24. -hint: 指定图片的特征或者提示;
  25. -af: using autoaf,指标是ssim

如何把其他图片转换成Webp?

JPG format
YUVJ420: YUV420P with JPEG color-range” (i.e. pixels use 0-255 range instead of 16-235 range, where 0 instead of 16 is black, and 255 instead of 235 is white);

使用工具cwebp,最简单的使用就是:
cwebp input.png -q 80 -o output.webp

重要的参数

重要的参数包括-preset -sns -f -m:
-preset: 用于指定content的类型,应该最先被设置,这样后续的设置就在这个基础上来修改; 编码器内部会根据preset去调整sns_strength, filter_sharpness, filter_strengthpreprocessing.
-f: 对应的是编码器内部的in-loop filter的filter strength;
-m: 用来指定编码速度,[0, 6],0最快,6最慢,default是4
-sns: 用来打开一些visual optimization,主要用来调节bit allocation. 用来把easy part的bit分配给hard part。通常升高sns,会导致文件增大,质量变好;

sns

method >= 4: distortion会包括spectural distortion
Distortion: 在method > 4的时候,会有distortion和spectral distortion
设置segment的时候,可以控制幅度。
控制uvac,越小的话,dq_uv_ac变大,和dq_uv_dc,越大,uv_dc boost的越多
It will set do_search

how to calculate alpha:
Do transform, collect coefficients stats, find last_none_zero and max_values;
alpha = last_non_zero/max_value

  1. 计算每个mb的alpha
    原理:
    TX: 4x4
    convert coefficients to bins,calculate the histogram of abs(level)
    maxvalue: 出现系数最多的系数的出现次数,越大,说明0系数的越多,说明能量越少
    last_non_zero: 最后一个非零系数,越大,说明高频的能量越多,
    alpha = ALPHA_SCALE * last_non
    +zero / max_value;
    best_alpha = MAX_ALPHA - (3 * best_alpha + best_uv_alpha + 2) >> 2
    高能量:
    1
    2
    3
    4
    5
    MBAnalyze:323 analyze mbx 0, mby 1
    mode 0, last_nonzero_count 31, max_value 95
    mode 1, last_nonzero_count 31, max_value 88
    MBAnalyze:330 best_alpha 179
    MBAnalyze:341 mbx 0, mby 1, alpha 57, uv_alpha 255
    低能量:
    1
    2
    3
    4
    5
    MBAnalyze:323 analyze mbx 0, mby 3
    mode 0, last_nonzero_count 11, max_value 234
    mode 1, last_nonzero_count 4, max_value 244
    MBAnalyze:330 best_alpha 23
    MBAnalyze:341 mbx 0, mby 3, alpha 234, uv_alpha 16
    alpha越大,Q越小;
    beta

filter params

Decoder perspective:
9.4 Loop Filter Type and Levels
frame-level loop filter params:

  1. filter_type: normal or simple, simple filter only applied to luma edges;
  2. loop_filter_level: defines the threshold, if the different is below this threshold, it should be filtered, otherwise, it should not be changed. Usually the level correlated to quantizer levels.
  3. sharpness_level, constant over the frame,
    如果loop_filter_level=0,那么应该跳过loop_filter;
    Differences in excess of a threshold(associated to the loop_filter_level) are assumed to not being modified.

mode_ref_lf_delta_update=1, allow per-macroblock adjustment, through delta; There are two types of delta values, one for reference frame-based adjustment, and the other group is for mode-based adjustment.

Filter header to write into bitstreams:
simple:
level: 对应fstrength_
sharpness

sharpness设置越大,fstrength越大;
Q越大,fstrength越大;

fstrength是针对segment来的;

lf_delta

process;
basestrength = F(q, sharpness)
strength = 5 filter_strength base_strength / *(256 + beta
)
level0 = fstrength_

只filter 4x4的块,不filter 16x16和skip的块;

How to decide the strength for each segments?

Why VP8StoreFilterStats? 存储不同的deltaq下,do filter之后的ssim并存储起来;
How to decide final strength
;
VP8AdjustFilterStrength

Internal Process

Speed 0/1

Speed 2~6

Alpha Encoding

主要通过一下几个接口来处理

  1. VP8EncInitAlpha
  2. VP8EncStartAlpha
  3. VP8EncFinishAlpha
  4. VP8EncDeleteAlpha
graph TD;
    A-->B;
    A-->C;
    B-->D;
    C-->D;

主要调用EncodeAlpha进行处理

Feature

Specify target_size/target_PSNR

sns:

Region-based quantization

Adaptive Loop-filter

根据prediction mode或参考帧来决定loop filter strength
Adjustment of loop-filter stregnth for each segment.

RDO

Preprocessing

sharp YUV

This is applied when converting RGB to YUV

Smooth segment

pseudo-random dithering log2

Alpha cleanup

Lossless

主要通过调用WebPEncodeLosslessRGB,内部是调用EncodeLossless.
VP8LEncodeImage is used to encoding lossless.
内部调用VP8LEncodeStream
QuantizeLevels
VP8FiltersInit
ApplyFiltersAndEncode
EncodeAlphaInternal —-> EncodeLossless —-> VP8LEncodeStream

Segment

不打开Segment情况下,

rd_opt_level:

  1. no_rd
  2. rd_opt_basic
  3. rd_opt_trellis, perform trellis-quant on the final decision only
  4. rd_opt_trellis_all

if rd_opt_level < rd_opt_basic>, then use VP8EncLoop

Reference

Official-Webp-Doc
React Native using Webp
Webp Tools
VP8 Encode Parameter Guide
VP8 Tech Overview
Webp Compression Techniques

nni是微软的一个AutoML工具,可以用来做一个自动化特征选择,自动剪枝和量化,神经网络架构搜索以及超参数调优。本文重点介绍下使用nni做超参数调优部分

支持的算法

nni中包含了好多种超参数调优算法,大概有三类搜索策略,

Exhaustive seach

  1. Grid Search
  2. Random
  1. Anneal
  2. Evolution
  3. Hyperband
  4. PBT

Bayesian optimization

  1. BOHB
  2. DNGO
  3. GP
  4. Metis
  5. SMAC
  6. TPE

使用方式

使用nni需要准备三个文件,包括

  1. nni_search.py: 计算每个参数的score
1
2
3
4
5
6
7
8
9
10
11
import nni

def main():
para = nni.get_next_parameter()
x = para['x']
# square(x) = 2
score = abs(x*x - 2)
nni.report_final_result(score)

if __name__ == '__main__':
main()
  1. search_space.json: 定义参数搜索的空间

    1
    2
    3
    4
    {
    "x": {"_type": "uniform", "_value": [1, 2]},
    }

  2. config.yml: nni配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    experimentName: search_sqrt_2

    # 并行度
    trialConcurrency: 1

    # 最大允许时长,
    maxExecDuration: 1h
    # 最多的运行次数,如果不设置则意味着永远不会停止
    maxTrialNum: 10

    #choice: local, remote
    trainingServicePlatform: local

    # search_space file
    searchSpacePath: search_space.json

    #choice: true, false
    useAnnotation: false

    tuner:
    #choice: TPE, Random, Anneal, Evolution, BatchTuner, MetisTuner
    #SMAC (SMAC should be installed through nnictl)
    builtinTunerName: TPE
    classArgs:
    #choice: maximize, minimize
    optimize_mode: minimize

    trial:
    command: python3 nni_search.py
    codeDir: .
    gpuNum: 0

运行命令:
nnictl create --config config.yml 启动试验,nni会打印出具体配置,并启动一个webserver,可以检查试验结果,以下为上面例子启动后的输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[2023-10-06 21:51:13] WARNING: Config field "authorName" is no longer supported and has been ignored
[2023-10-06 21:51:13] WARNING: You are using legacy config file, please update it to latest format:
================================================================================
experimentName: search_sqrt_2
trialConcurrency: 1
maxExperimentDuration: 1h
maxTrialNumber: 100
searchSpaceFile: search_space.json
useAnnotation: false
trialCommand: python3 nni_search.py
trialCodeDirectory: .
trialGpuNumber: 0
tuner:
name: TPE
classArgs:
optimize_mode: minimize
trainingService:
platform: local
================================================================================
Reference: https://nni.readthedocs.io/en/stable/reference/experiment_config.html
[2023-10-06 21:51:13] Creating experiment, Experiment ID: vl4y6ge3
[2023-10-06 21:51:13] Starting web server...
[2023-10-06 21:51:14] Setting up...
[2023-10-06 21:51:14] Web portal URLs: http://127.0.0.1:8080 http://192.168.50.103:8080
[2023-10-06 21:51:14] To stop experiment run "nnictl stop vl4y6ge3" or "nnictl stop --all"
[2023-10-06 21:51:14] Reference: https://nni.readthedocs.io/en/stable/reference/nnictl.html

运行完成以后,通过
nnictl stop -all来停止试验

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

0%