
简介这份资源是面向视频编码研究者、算法工程师及多媒体方向学生的UVG 4K测试视频数据集配套论文聚焦高帧率超高清序列在编解码分析中的实际需求。数据集由坦佩雷大学Ultra Video Group构建包含16个4K3840×2160自然序列帧率为50或120 fps提供8位与10位4:2:0 YUV原始格式并采用非商业CC BY-NC许可发布。论文从空间与时间感知信息、率失真行为及编码复杂度三个维度对全部序列进行刻画并与HEVC/H.265和VVC/H.266参考编解码器展开对比验证其可补充现有4K测试集尤其适合下一代VVC编解码的主观与客观质量评估。资源包为1个PDF文件约931KB内容涵盖数据集总览、序列特征描述与实验评估结果便于读者快速掌握各序列特性并用于编码器性能基准测试。目前已有137人学习适合需要高帧率4K素材开展编码实验与算法验证的读者参考。1. 4K 高帧率测试序列到底解决什么问题从一段跑不动的编码日志说起如果你做过 HEVC 或 VVC 的编码器调优大概率遇到过这种场景手头只有 1080p 的测试序列编码器跑得飞快QP 曲线也漂亮但一上真实 4K 素材码率控制直接崩掉帧间预测的搜索范围完全不够用。问题出在测试素材本身——分辨率不够、帧率不够、纹理复杂度不够编码器在低分辨率下表现出的行为放到 4K 高帧率场景里根本不成立。UVG 数据集就是冲着这个缺口来的。它由某高校超视频研究团队发布包含 16 个 4K3840×2160测试视频序列帧率分 50fps 和 120fps 两档每个序列 600 帧以 8-bit 和 10-bit 4:2:0 YUV 原始格式提供。这些序列用专业电影摄影机拍摄覆盖了从慢速特写到高速运动、从平滑纹理到复杂纹理的多种内容类型。它解决的核心问题是给视频编码器尤其是 VVC/H.266 和 HEVC/H.265的率失真性能评估和编码复杂度分析提供一组真正能压出差异的 4K 高帧率素材。适合做编码器开发、码率控制算法验证、客观质量评估的从业者以及需要高帧率素材做信号处理实验的研究人员。2. 16 个序列怎么选内容特征与编码难度的对应关系2.1 序列分类逻辑与选型依据UVG 的 16 个序列不是随便凑数的。每个序列在拍摄时刻意覆盖了不同的空间感知信息SI和时间感知信息TI组合。SI 反映单帧内的空间细节丰富程度TI 反映相邻帧之间的时间变化剧烈程度。这两个指标直接决定了编码器在帧内预测和帧间预测上的压力分布。从实际使用角度看可以把 16 个序列分成四类第一类是高 SI 低 TI典型代表是 FlowerPan 和 CityAlley。画面内纹理极其复杂但镜头运动缓慢帧间变化小。这类序列主要考验帧内预测和变换编码的效率帧间预测的收益相对有限。第二类是低 SI 高 TI典型代表是 HoneyBee 和 Jockey。画面内纹理相对平滑但运动剧烈帧间变化大。这类序列对运动估计和运动补偿的精度要求极高是测试帧间预测工具的好素材。第三类是高 SI 高 TI典型代表是 FlowerKids 和 RaceNight。纹理复杂且运动剧烈编码难度最高码率曲线在这类序列上区分度最明显。第四类是低 SI 低 TI典型代表是 Lips 和 Beauty。画面简洁运动缓慢编码器在这类序列上容易跑出很低的码率适合做码率控制下限的验证。选序列的时候我一般会先明确测试目标如果是验证帧间预测工具优先选 HoneyBee、Jockey、RaceNight如果是验证帧内预测或变换编码优先选 FlowerPan、CityAlley如果要看整体 RD 曲线的区分度FlowerKids 和 RaceNight 是必选的。2.2 分辨率、帧率与位深的组合策略UVG 的每个序列都提供 8-bit 和 10-bit 两个版本帧率固定为 50fps 或 120fps。这个组合不是随意定的而是对应了实际编码标准测试中的常见配置。50fps 序列有 8 个CityAlley、FlowerFocus、FlowerKids、FlowerPan、RaceNight、ReadySetGo、ShakeNDry、YachtRide。这些序列的时长是 12 秒600 帧适合做中等时长的编码测试。120fps 序列也有 8 个Beauty、Bosphorus、HoneyBee、Jockey、Lips、LuckyRun、Marathon、SunBath。时长 5 秒600 帧帧数相同但时间跨度更短单位时间内的帧间变化更密集。位深的选择直接影响编码器的内部精度。10-bit 序列在 HEVC 和 VVC 的参考编码器中通常能获得更好的压缩效率因为高位深减少了量化误差的累积。但 10-bit 的数据量是 8-bit 的两倍存储和内存带宽压力也翻倍。我一般会先用 8-bit 版本做快速验证确认编码器配置没问题后再用 10-bit 版本跑正式测试。注意10-bit YUV 文件在 FFmpeg 中需要用-pix_fmt yuv420p10le指定像素格式否则默认按 8-bit 处理会导致数据截断。2.3 从原始 YUV 到编码器输入格式转换与验证拿到 UVG 的 YUV 文件后第一步是确认文件的实际格式和参数。原始 YUV 没有文件头所有信息靠命名约定和外部文档。常见的命名格式是序列名_分辨率_帧率_位深.yuv但不同来源的文件命名可能不一致。用 FFmpeg 探测 YUV 文件的基本参数# 探测 YUV 文件的基本信息需要手动指定分辨率、像素格式和帧率 ffprobe -f rawvideo -pix_fmt yuv420p10le -s 3840x2160 -r 120 -i Beauty_3840x2160_120fps_10bit.yuv这条命令里-f rawvideo告诉 FFmpeg 输入是原始视频流-pix_fmt指定像素格式-s指定分辨率-r指定帧率。如果位深是 8-bit-pix_fmt改成yuv420p。ffprobe会输出流的基本信息但不会解码速度很快。确认格式后如果需要转成其他格式比如给某个只支持特定容器的工具用可以用 FFmpeg 转封装# 将原始 YUV 转为无损 FFV1 编码的 MKV便于用播放器逐帧检查 ffmpeg -f rawvideo -pix_fmt yuv420p10le -s 3840x2160 -r 120 \ -i Beauty_3840x2160_120fps_10bit.yuv \ -c:v ffv1 -level 3 -pix_fmt yuv420p10le Beauty_lossless.mkv这里用 FFV1 无损编码保证转换过程不引入额外失真。-level 3是 FFV1 的版本级别支持 10-bit 和 4:2:0。转成 MKV 后可以用播放器逐帧查看方便定位编码器输出的异常帧。如果只是要截取前 N 帧做快速测试可以用-frames:v参数# 只取前 100 帧减少测试时间 ffmpeg -f rawvideo -pix_fmt yuv420p10le -s 3840x2160 -r 120 \ -i Beauty_3840x2160_120fps_10bit.yuv \ -frames:v 100 -c:v ffv1 Beauty_100f.mkv参数说明-frames:v 100表示只输出 100 帧视频。这个操作在调试编码器参数时特别有用因为 4K 120fps 的完整序列编码一次可能要几十分钟甚至几小时截取片段能大幅缩短迭代周期。3. 用 HEVC 和 VVC 参考编码器跑 RD 曲线配置与实测3.1 参考编码器的获取与编译要点HEVC 的参考编码器 HM 和 VVC 的参考编码器 VTM 都是开源项目可以从官方代码托管平台获取。编译过程依赖 CMake 和 C 编译器在 Linux 环境下比较顺畅。以 HM 为例编译步骤大致如下# 克隆 HM 源码此处以通用路径示意实际地址以官方发布为准 git clone HM仓库地址 HM cd HM mkdir build cd build cmake .. -DCMAKE_BUILD_TYPERelease make -j$(nproc)编译完成后bin/目录下会生成TAppEncoderStatic和TAppDecoderStatic。VTM 的编译流程类似但依赖的 C 标准更高建议用 GCC 9 或以上版本。编译时容易踩的坑是内存分配器。HM 和 VTM 默认使用系统 malloc在 4K 序列上频繁分配大块内存会导致性能下降。可以在 CMake 中开启-DUSE_MMAP1或链接 jemalloc实测能提升 10% 到 20% 的编码速度。3.2 配置文件的关键参数与修改方法HM 和 VTM 都使用配置文件cfg来管理编码参数。以 HM 的encoder_intra_main.cfg和encoder_lowdelay_P_main.cfg为例几个关键参数直接决定 RD 曲线的形状参数含义常用值影响QP量化参数22, 27, 32, 37控制码率和质量的权衡IntraPeriod帧内周期32, 64, -1影响随机访问和压缩效率GOPSizeGOP 长度4, 8, 16影响帧间预测的参考结构FrameRate帧率50, 120必须与序列实际帧率一致SourceWidth源宽度3840必须与序列实际分辨率一致SourceHeight源高度2160必须与序列实际分辨率一致InputBitDepth输入位深8, 10必须与 YUV 文件实际位深一致InternalBitDepth内部位深8, 10通常与输入位深一致一个典型的低延迟 P 配置片段# HM 低延迟 P 配置示例片段 SourceWidth : 3840 SourceHeight : 2160 FrameRate : 120 InputBitDepth : 10 InternalBitDepth : 10 QP : 32 IntraPeriod : 32 GOPSize : 4参数说明IntraPeriod设为 32 表示每 32 帧插入一个 I 帧数值越小随机访问性能越好但压缩效率越低。GOPSize设为 4 表示每个 GOP 包含 4 帧低延迟场景下通常用较小的 GOP。QP是核心变量跑 RD 曲线时一般取 22、27、32、37 四个点每个点跑一次完整编码。VTM 的配置文件结构类似但多了些 VVC 特有的工具开关比如MTS多变换选择、LFNST低频不可分变换、CCLM跨分量线性模型等。这些开关默认开启如果要分析某个工具的贡献可以单独关掉做对比。3.3 跑一条完整 RD 曲线的操作流程假设已经编译好 HM序列文件放在/data/uvg/目录下配置文件放在/cfg/目录下。跑一个序列的完整 RD 曲线# 对 Beauty 序列跑 4 个 QP 点输出码流和重建 YUV for qp in 22 27 32 37; do ./bin/TAppEncoderStatic \ -c /cfg/encoder_lowdelay_P_main.cfg \ -i /data/uvg/Beauty_3840x2160_120fps_10bit.yuv \ -wdt 3840 -hgt 2160 -fr 120 \ -f 600 \ -q $qp \ -b /output/Beauty_qp${qp}.bin \ -o /output/Beauty_qp${qp}_rec.yuv \ /output/Beauty_qp${qp}_log.txt 21 done参数说明-c指定配置文件-i指定输入 YUV-wdt和-hgt指定分辨率-fr指定帧率-f指定编码帧数-q指定 QP-b指定输出码流-o指定重建 YUV。日志重定向到文件方便后续提取码率和 PSNR。编码完成后从日志中提取每个 QP 点的码率和 PSNR# 提取每个 QP 点的码率和 Y-PSNR for qp in 22 27 32 37; do echo -n QP$qp grep -E ^\s*POC\s0 /output/Beauty_qp${qp}_log.txt | tail -1 | \ awk {print Bitrate:, $4, kbps Y-PSNR:, $8, dB} done这条命令从日志中抓取最后一帧的汇总行提取码率和 Y-PSNR。不同版本的 HM 日志格式略有差异实际使用时需要根据日志内容调整awk的字段位置。拿到四个 QP 点的码率PSNR数据后可以用 Python 画 RD 曲线import matplotlib.pyplot as plt # 四个 QP 点的实测数据示例值需替换为实际提取结果 qp_points [22, 27, 32, 37] bitrates [45000, 22000, 11000, 5500] # kbps psnrs [42.5, 39.8, 37.2, 34.6] # dB plt.figure(figsize(8, 6)) plt.plot(bitrates, psnrs, o-, labelHM 16.20 LowDelay-P) plt.xlabel(Bitrate (kbps)) plt.ylabel(Y-PSNR (dB)) plt.title(UVG Beauty 120fps 10-bit RD Curve) plt.grid(True) plt.legend() plt.savefig(rd_curve_beauty.png, dpi150)这段代码用 matplotlib 画码率-PSNR 曲线bitrates和psnrs需要替换成实际提取的数据。横轴是码率纵轴是 Y-PSNR曲线越靠左上编码效率越高。3.4 编码复杂度指标的采集方法除了 RD 性能UVG 论文里还用了编码复杂度指标。实际测试中最直接的复杂度指标是编码时间。在日志中HM 会输出每帧的编码耗时和总耗时。提取总编码时间# 提取总编码时间秒 grep Total Time /output/Beauty_qp32_log.txt | awk {print $4}如果要更细粒度地分析可以看每帧的编码时间分布。高 TI 序列如 Jockey、HoneyBee的帧间编码时间波动会明显大于低 TI 序列。这个波动本身就是一个有价值的观察指标——它反映了运动估计模块在不同内容上的计算量差异。另一个常用指标是平均比特数每像素bpp计算方式是总比特数除以宽 × 高 × 帧数。bpp 把码率归一化到像素级别方便跨分辨率比较。4. 避坑与排查YUV 读取、内存和编码器配置的五个血泪教训4.1 现象编码器输出全黑或花屏日志无报错原因YUV 文件的位深与编码器配置不匹配。最常见的情况是 10-bit 文件被当成 8-bit 读取每个像素少读一个字节导致后续所有像素错位。解决先用ffprobe确认文件实际位深再检查编码器配置中的InputBitDepth和InternalBitDepth。如果文件是 10-bitInputBitDepth必须设为 10。另外注意 YUV 的存储顺序是 planarY 平面在前U 平面在后V 平面最后不要按 packed 格式去读。4.2 现象编码到一半进程被 kill系统日志显示 OOM原因4K 120fps 10-bit 序列的单帧数据量是 3840×2160×1.5×2 ≈ 24.9 MB600 帧就是约 15 GB。HM 和 VTM 默认会缓存多帧参考图像内存占用可能超过 32 GB。解决减少同时缓存的参考帧数量在配置文件中调小MaxNumRefFrames或DPBSize。如果内存实在不够可以用-f参数只编码前 100 到 200 帧牺牲序列长度换取可运行性。另外确保系统 swap 分区足够大或者用ulimit -v限制进程内存避免拖垮整个系统。4.3 现象RD 曲线异常高 QP 点的 PSNR 反而比低 QP 点高原因编码器配置中的FrameRate与序列实际帧率不一致。比如 120fps 序列配了 50fps编码器会按 50fps 的时间尺度做运动估计导致帧间预测完全错乱。解决检查配置文件中的FrameRate和命令行-fr参数确保与序列实际帧率一致。UVG 的 50fps 和 120fps 序列在文件名中都有标注不要凭感觉猜。4.4 现象VTM 编码速度极慢单帧超过 10 分钟原因VTM 默认开启了所有 VVC 工具包括一些计算量极大的模块如仿射运动补偿、解码器端运动矢量细化。在 4K 序列上这些模块的搜索范围会爆炸式增长。解决如果只是做快速验证可以在配置文件中关闭部分工具比如设Affine0、DMVR0、BDOF0。正式测试时再全部开启。另外编译时务必用-DCMAKE_BUILD_TYPEReleaseDebug 版本的编码速度可能慢 5 到 10 倍。4.5 现象不同 QP 点的码率差异很小RD 曲线几乎水平原因序列本身的空间和时间复杂度太低编码器在所有 QP 下都能轻松压缩。比如 Lips 和 Beauty 这类低 TI 序列QP 从 22 降到 37码率可能只差两三倍而 FlowerKids 或 RaceNight 能差十倍以上。解决如果测试目标是验证编码器在高难度内容上的表现优先选高 SI 高 TI 的序列。如果必须用低复杂度序列可以把 QP 范围拉大比如从 17 到 42增加区分度。5. 用 SI/TI 指标做序列筛选一个被低估的预处理技巧跑完几轮编码测试后我发现一个很实际的问题16 个序列全跑一遍时间成本太高。4K 120fps 10-bit 序列在 VTM 上跑一个 QP 点可能要几个小时四个 QP 点就是一天。如果只是做初步的工具验证没必要全量跑。我的做法是先算每个序列的 SI 和 TI用这两个指标做快速筛选。SI 的计算方式是对每帧做 Sobel 滤波然后取滤波后像素的标准差最后取所有帧的最大值。TI 是相邻帧差值的标准差的最大值。这两个指标在 Python 里用 OpenCV 和 NumPy 就能算不需要启动编码器。import cv2 import numpy as np def compute_si_ti(yuv_path, width, height, frames, bit_depth10): 计算 YUV 序列的 SI 和 TI 指标。 yuv_path: YUV 文件路径 width, height: 分辨率 frames: 帧数 bit_depth: 位深8 或 10 dtype np.uint16 if bit_depth 10 else np.uint8 frame_size width * height * 3 // 2 # YUV 4:2:0 si_list [] ti_list [] prev_y None with open(yuv_path, rb) as f: for i in range(frames): raw f.read(frame_size) if len(raw) frame_size: break # 只取 Y 平面 y_plane np.frombuffer(raw[:width*height], dtypedtype).reshape(height, width) y_plane y_plane.astype(np.float32) # SI: Sobel 滤波后的标准差 sobel_x cv2.Sobel(y_plane, cv2.CV_32F, 1, 0, ksize3) sobel_y cv2.Sobel(y_plane, cv2.CV_32F, 0, 1, ksize3) sobel_mag np.sqrt(sobel_x**2 sobel_y**2) si_list.append(np.std(sobel_mag)) # TI: 相邻帧差值的标准差 if prev_y is not None: ti_list.append(np.std(y_plane - prev_y)) prev_y y_plane return max(si_list), max(ti_list) if ti_list else 0 # 示例计算 Beauty 序列的 SI/TI si, ti compute_si_ti(/data/uvg/Beauty_3840x2160_120fps_10bit.yuv, 3840, 2160, 600, bit_depth10) print(fSI{si:.2f}, TI{ti:.2f})这段代码逐帧读取 YUV 文件只取 Y 平面做计算。compute_si_ti返回 SI 和 TI 的最大值。参数说明bit_depth决定用uint16还是uint8读取10-bit 数据必须用uint16否则会溢出。frame_size按 4:2:0 计算Y 平面占 2/3U 和 V 各占 1/6。算完 SI/TI 后可以画一张散点图横轴 SI纵轴 TI每个点代表一个序列。这样能直观看到哪些序列落在高难度区域哪些落在低难度区域。如果测试目标是验证编码器在极端条件下的表现直接选右上角的序列如果只是做功能验证选左下角的序列能快速跑完。这个预处理步骤帮我省了很多时间。以前拿到新数据集习惯性从第一个序列开始跑跑到一半发现内容太简单编码器根本压不出差异时间全浪费了。现在先花几分钟算 SI/TI筛出两三个目标序列再动手效率高得多。从那以后我每次拿到新的测试序列集都强制先跑一遍 SI/TI 筛选再决定编码测试的序列清单。希望这个习惯也能帮到你。本文还有配套的精品资源点击获取