使用 Excel 绘制逐帧 VMAF 画质图表的数据可视化教程

A@NAZOrip
A@NAZOrip 3天前
  • 在其它设备中阅读本文章

VMAF 简介

VMAF 是一种结合了主观绝对评价、客观全参考指标、以及客观部分参考指标的多模型体系。基于人眼视觉对于失真的判断与处理特性,并结合了机器学习模型与传统算法的部分参考加全参考指标。相比其它统计画质的算法,VMAF 将原画的清晰度纳入考量(同时使用滤镜预处理原画和压缩结果)。详见 AV1 视频压制教程——画质指标Netflix tech blog

VMAF 评分

VMAF 的分数取值范围为 0~100,主要分为 4k、1080p 和 phone 3 种评分方式。

  • VMAF 4k:播放画质,远距离观看/客厅电视/影院
  • VMAF 1080p:播放画质,中近距离观看/桌面电脑,相比 VMAF 4K 更加苛刻
  • 视觉无损:大于等于 95
  • 优秀:大于等于 90
  • 良好:大于等于 80
  • 达标:大于等于 70

注:压缩视频时,控制平均 VMAF 在 90~95 之间即可。


下载工具

ffmpeg

时间基对齐工具

下载 GCDLCMCalculator.7z(谷歌盘)(百度云)目录同急用版 x264、x265、AV1 教程下载地址;或(GitHub 项目地址中手动下载)。
这是一个计算最大公约数、最小公倍数、以及分数的最大公约数的脚本/包,其中有 PowerShell、Java(.jar)、Python 和 Bash 的选项,选择适合的版本即可。


对齐时间基 Timebase

若源与压缩结果两个视频流的时间基不对齐,ffmpeg 就会在开始跑分前警告“DTS 未单调增加(DTS is not increasing monotonically)”:

[Parsed_xpsnr_0 @ 000001e437db6e80]
not matching timebases found between first input: 1/90000 and second input 1001/24000, results may be incorrect!

最终画质衡量分数大概率会远低于正常(XPSNR、VMAF 等跑分受影响):

  • 未对齐(画质 -659%):
XPSNR average, 14315 frames  y: 33.8192  u: 41.5568  v: 42.3078 (minimum: 33.8192)
  • 对齐:
XPSNR average, 14315 frames  y: 41.9978  u: 44.6105  v: 45.2615  (minimum: 41.9978)

对齐方法

先运行普通版本:

ffmpeg -i ".\原画源.mkv" -i ".\压缩源.ivf" -lavfi ^
libvmaf=log_fmt=xml:log_path="X\\:/导出VMAF数据文件夹/vmaf.xml":model=version=vmaf_4k_v0.6.1\\:name=vmaf_4k|version=vmaf_v0.6.1\\:name=vmaf_1080p" ^
-f null -

一旦看到 not matching timebases found between first input: ??? and second input ??? 提醒时按 Ctrl+C 停止跑分,然后使用 GCDLCMCalculator 中的任意脚本/包算出共同时间基。

计算共同时间基

根据 ffmpeg not matching ... 反馈是否含有小数/分数:

  • 如果有就使用 GCDLCMCalculator 的分数的最大公约数(fracGCD)算法
  • 否则使用 GCDLCMCalculator 的最小公倍数(lcm)算法
PowerShell 脚本(Windows、可能支持 Linux)
# 参数名用法(支持乱序参数指定)
.\gcdlcm.ps1 -Operation gcd -A 24 -B 1000
.\gcdlcm.ps1 -Operation lcm -A 24 -B 1000
.\gcdlcm.ps1 -Operation fracgcd -Num1 1 -Denom1 90000 -Num2 1001 -Denom2 24000

# 仅参数用法(仅支持顺序参数指定)
.\gcdlcm.ps1 -Operation gcd 24 1000
.\gcdlcm.ps1 -Operation lcm 24 1000
.\gcdlcm.ps1 -Operation fracgcd 1 90000 1001 24000

# 显示帮助信息
.\gcdlcm.ps1
Java 包(需安装 Java 环境)
# 仅参数用法(支持乱序参数指定)
java -jar .\GCDLCMCalculator.jar gcd
java -jar .\GCDLCMCalculator.jar lcm
java -jar .\GCDLCMCalculator.jar fracgcd

# 显示帮助信息
java -jar .\GCDLCMCalculator.jar
Python 脚本(需安装 Python 环境)
# 参数名用法(支持乱序参数指定)
python .\gcdlcm.py -o gcd -a 24 -b 1000
python .\gcdlcm.py -o lcm -a 24 -b 1000
python .\gcdlcm.py -o fracgcd -num1 1 -denom1 90000 -num2 1001 -denom2 24000

# 仅参数用法(仅支持顺序参数指定)
python .\gcdlcm.py gcd 24 1000
python .\gcdlcm.py lcm 24 1000
python .\gcdlcm.py fracgcd 1 90000 1001 24000

# 显示帮助信息
python .\gcdlcm.py
Bash 脚本(Linux、Git Bash、可能支持 Mac OS)
# 参数名用法(支持乱序参数指定)
./gcdlcm.sh -o gcd -a 24 -b 1000
./gcdlcm.sh -o lcm -a 24 -b 1000
./gcdlcm.sh -o fracgcd -num1 1 -denom1 90000 -num2 1001 -denom2 24000

# 仅参数用法(仅支持顺序参数指定)
./gcdlcm.sh gcd 24 1000
./gcdlcm.sh lcm 24 1000
./gcdlcm.sh fracgcd 1 90000 1001 24000

# 显示帮助信息
./gcdlcm.sh

最后运行时间基对齐版本的 VMAF 跑分:

set "alignedTbase=输入最小公倍数lcm,如1234、或分数小数的最大公约数fracGCD结果,如1/360000"
ffmpeg -i ".\原画源.mkv" -i ".\压缩源.ivf" -lavfi ^
"[0:v]setpts=N*(%alignedTbase%)[src]; ^
[1:v]setpts=N*(%alignedTbase%)[enc]; ^
[src][enc]libvmaf=log_fmt=xml:log_path="X\\:/导出VMAF数据文件夹/vmaf.xml":model=version=vmaf_4k_v0.6.1\\:name=vmaf_4k|version=vmaf_v0.6.1\\:name=vmaf_1080p" ^
-f null -
  • 注:此例使用 CMD 批处理实现,因此使用 :: 做注释符,^ 做换行符,可以根据常用的命令行工具修改。

示例——大批量 VMAF 跑分命令

对于一整季的视频,源视频的时间基不变、压制结果的时间基也不变,因此可以直接预置一些 setpts 变量,然后根据第一个视频运行普通跑分的结果决定所有视频的时间基对其方案。


chcp 65001

:: 配置导入和导出路径

set "sourcePath=X:\源视频文件夹\"
set "lossyPath=X:\压缩视频文件夹\"
set "outTxtPath=X:\导出ffmpeg进度与报错,以及 VMAF 最终导出目录文件夹\"

:: 时间基对齐参数(无需修改,如有必要则添加新项目)

set "setptsCmdA=[0:v]setpts=N*(1/24000)[src]; [1:v]setpts=N*(1/24000)[enc]; [src][enc]"
set "setptsCmdB=[0:v]setpts=N*(1/6000)[src]; [1:v]setpts=N*(1/6000)[enc]; [src][enc]"
set "setptsCmdC=[0:v]setpts=N*(1/360000)[src]; [1:v]setpts=N*(1/360000)[enc]; [src][enc]"

:: 导出 VMAF 4k、1080p 分数到 XML,也可使用 CSV(log_fmt=csv:log_path="X\\:/xxx.csv")
:: 使用缓存文件夹是因为为每个视频重写 vmafCmd 变量的出错概率大,因此使用先创建,再重命名和移动的方法更稳定可靠

set "vmafCmd=libvmaf=log_fmt=xml:log_path="X\\:/导出VMAF数据文件夹/vmaf.xml":model=version=vmaf_4k_v0.6.1\\:name=vmaf_4k|version=vmaf_v0.6.1\\:name=vmaf_1080p"

::

title VMAF 测试——源视频文件名

::

:: 修改 sourceFile 和 lossyFile,outTxtFile 会直接复制前者的文件名,但替换的后缀名需要与 lossyFile 一致
:: sourceFile 和 lossyFile 只填文件名,路径已在 sourcePath、lossyPath 中定义

set "sourceFile=源视频文件1.mkv"
set "lossyFile=压缩视频文件1.ivf"
set "outTxtFile=%lossyFile%.txt"
ffmpeg -hide_banner -an -i "%sourcePath%%sourceFile%" ^
-i "%lossyPath%%lossyFile%" ^
-lavfi "%setptsCmdB%%vmafCmd%" -f null - > "%outTxtPath%%outTxtFile%" 2>&1

:: 重命名为压缩视频文件名,并移动到导出路径

if exist "X:\vmaf结果缓存文件夹\vmaf.xml" (
    ren "X:\vmaf结果缓存文件夹\vmaf.xml" "%lossyFile%.vmaf.xml"
    move "%lossyFile%.vmaf.xml" "%outTxtPath%"
)

:: lossyFile 只填文件名,路径已在 sourcePath、lossyPath 中定义

set "lossyFile=压缩视频文件2.mp4"
set "outTxtFile=%lossyFile%.txt"
ffmpeg -hide_banner -an -i "%sourcePath%%sourceFile%" ^
-i "%lossyPath%%lossyFile%" ^
-lavfi "%setptsCmdB%%vmafCmd%" -f null - > "%outTxtPath%%outTxtFile%" 2>&1

:: 重命名为压缩视频文件名,并移动到导出路径

if exist "X:\vmaf结果缓存文件夹\vmaf.xml" (
    ren "X:\vmaf结果缓存文件夹\vmaf.xml" "%lossyFile%.vmaf.xml"
    move "%lossyFile%.vmaf.xml" "%outTxtPath%"
)
  • 注:此例使用 CMD 批处理实现,因此使用 :: 做注释符,^ 做换行符,可以根据常用的命令行工具修改。
  • 注:此例使用 Windows 路径,这在 ffmpeg lavfi 中完全由滤镜自己定义路径格式,导致路径写法五花八门,ChatGPT 等大模型非常容易误判,修改时应特别注意。

导入 Excel 并处理

ffmpeg 导出的 libvmaf XML 大概上长这样:

<VMAF version="b9ac69e6">
  <params qualityWidth="4096" qualityHeight="2160" />
  <fyi fps="4.88" />
  <frames>
    <frame frameNum="0" integer_adm2="0.993714" integer_adm_scale0="0.981137" integer_adm_scale1="0.985518" integer_adm_scale2="0.998496" integer_adm_scale3="0.995591" integer_motion2="0.000000" integer_motion="0.000000" integer_vif_scale0="0.832475" integer_vif_scale1="0.980369" integer_vif_scale2="0.984571" integer_vif_scale3="0.984265" vmaf_4k="97.730270" vmaf_1080p="93.998455" />
    // ..... 省略几十万字 ......
    <frame frameNum="299" integer_adm2="0.986481" integer_adm_scale0="0.971787" integer_adm_scale1="0.968352" integer_adm_scale2="0.989743" integer_adm_scale3="0.992741" integer_motion2="6.875413" integer_motion="6.875413" integer_vif_scale0="0.760069" integer_vif_scale1="0.960388" integer_vif_scale2="0.974629" integer_vif_scale3="0.978447" vmaf_4k="99.637538" vmaf_1080p="100.000000" />
  </frames>
  <pooled_metrics>
    <metric name="integer_adm2" min="0.985634" max="0.993714" mean="0.988010" harmonic_mean="0.988009" />
    <metric name="integer_adm_scale0" min="0.971787" max="0.981927" mean="0.975181" harmonic_mean="0.975179" />
    // ..... 省略几百字 ......
    <metric name="vmaf_4k" min="97.730270" max="100.000000" mean="99.841975" harmonic_mean="99.841396" />
    <metric name="vmaf_1080p" min="93.998455" max="100.000000" mean="99.953442" harmonic_mean="99.952122" />
  </pooled_metrics>
  <aggregate_metrics />
</VMAF>
  1. 打开 Excel,不用建立新工作簿,直接将 xml 拖入其中
  2. 选择“作为 xml 表打开”,点确定
    • 1-importXML.png
  3. 忽略“指定的 xml 源没有引用架构...”提示,点确定
    • 2-importXML.png
    • 3-importXML.png
  4. 需要用到的列只有 frameNumvmaf_4kvmaf_1080p,其他列可以删除(通过拖选顶部的字母列,按右键删除)
    • 4-deleteExcessCols.png
    • 5-deleteExcessCols2.png
  5. 拖选 frameNumvmaf_4kvmaf_1080p 3 列,选择插入→图表→散点图,选择第一个图即可创建图表
    • 6-insertPlotChart.png
  6. 填写图表标题(文件名或压制参数)
    • 7-chartNaming.png
    • 注:此时可以选择一个好看的图表样式
  7. 选中 Y 轴的数据框,右键→设置坐标轴格式→坐标轴选项,设定最小值:75、最大值:100
    • 8-setYAxis.png
    • 注:设定最小值是为了防止数据点挤在一起看不清
  8. 选中 X 轴的数据框,右键→设置坐标轴格式→坐标轴选项,设定最大值:视频最大帧数
    • 视频的最大帧数可以通过选中 frameNum 列,按 Ctrl+↓ 查找
    • 9-setXAxis.png
  9. 选中图表中的数据点,右键→设置数据系列格式→填充(油漆桶)→标记→标记选项中,选择使用面积更小、颜色不同的数据点
    • 10-configureChartPlot.png
    • 先点击标记选项中的“自动”,再点击“内置”才能全选所有的数据点,否则只会先前选中的一个数据点
    • 11-configureChartPlot2.png

趋势线

  1. 选中图表中的数据点,右键→添加趋势线
  2. 默认会出现线性趋势线,根据需要的分析方法选择需要的趋势线:
    • 线性:单纯分析码率增长速度
    • 指数:对码率增长敏感时,看指数线曲度是否足够直
    • 对数:log,对增长不敏感,但初始变化敏感的分析
    • 多项式,阶数6:分析哪部分视频占平均码率更大
    • 移动平均,周期10:额外添加一条更平均的码率变化曲线

这样就能得到可视化的跑分数据了,那么就这样。

打赏信息

在线丢人,求个打赏,支持一下饭费 T_T

bmc_qr.png
pp_tip_qr.png
——Buy me a coffee 链接
——PayPal 链接