3. HDR10技术原理:静态元数据、PQ曲线与BT.2020

HDR10,说白了就是目前最主流的HDR格式。你打开Netflix、Amazon Prime上的HDR内容,绝大多数都是它。我最早接触HDR10是在2016年,那时候刚拿到第一台支持HDR的消费级显示器,心想这玩意儿到底跟普通视频有啥区别?后来自己动手编码了几条片子,才真正理解它背后的设计逻辑。

这一章,咱们把HDR10的三个核心拆开揉碎:静态元数据、PQ曲线、BT.2020色彩空间。搞懂这三样,你就掌握了HDR10的命脉。

3.1 静态元数据:MaxFALL与MaxCLL

元数据,就是描述视频数据的数据。HDR10的元数据是“静态”的——整部影片从头到尾只带一组参数。这跟Dolby Vision的动态元数据不同,后者可以逐帧调整。静态的好处是简单、兼容性好,坏处嘛……后面我会讲坑。

两个关键指标:

  • MaxCLL(Maximum Content Light Level):整部影片中最亮的一个像素的亮度值。
  • MaxFALL(Maximum Frame Average Light Level):整部影片中,单帧平均亮度的最大值。

我举个例子你就明白了。一部科幻片,有个镜头是激光炮爆炸,画面中心一个像素达到10000尼特,但周围全是暗部。这时候MaxCLL就是10000尼特,但MaxFALL可能只有200尼特。为什么?因为MaxFALL算的是整帧的平均值,那个超亮像素被周围暗部“稀释”了。

核心理解: MaxCLL告诉你“最亮能亮到哪”,MaxFALL告诉你“整体亮度水平”。显示设备根据这两个值做色调映射(Tone Mapping)。

我曾经在一个项目里遇到过这样的问题:客户提供的HDR源文件MaxCLL标的是4000尼特,但实际播放时暗部细节全丢了。后来一查,是编码器自动计算MaxCLL时把噪点也算进去了——一个暗场景里的热噪点被误判为“最亮像素”。从那以后,我建议团队在生成元数据前先做一次降噪预处理。

元数据生成实战

用x265编码器生成HDR10元数据,命令大概长这样:

ffmpeg -i input.mkv -c:v libx265 \
  -x265-params "hdr10=1:colorprim=bt2020:transfer=smpte2084:colormatrix=bt2020nc:\
  master-display=G(13250,34500)B(7500,3000)R(34000,16000)WP(15635,16450)L(10000000,1):\
  max-cll=1000,400" \
  -c:a copy output.mkv

参数拆解:

  • master-display:定义显示器的色域和亮度范围。括号里的数字是CIE 1931色度坐标,单位是万分之几。L(10000000,1)表示峰值亮度10000尼特,黑电平0.0001尼特。
  • max-cll=1000,400:前面是MaxCLL(1000尼特),后面是MaxFALL(400尼特)。

注意: 很多编码器默认不计算MaxCLL/MaxFALL,或者算得不准。我习惯先用ffmpeg的signalstats滤镜逐帧分析,再手动填入。别偷懒,否则播放端可能映射出奇怪的效果。

3.2 PQ曲线(ST 2084):人眼感知的数学拟合

为什么HDR不用传统的Gamma曲线?因为Gamma是为CRT显示器设计的,它假设人眼对亮度的感知是对数的。但现实是,人眼在暗部的分辨能力远高于亮部。Gamma曲线在暗部浪费了太多码值,亮部又不够用。

PQ曲线(Perceptual Quantizer,感知量化器),由SMPTE ST 2084标准定义。它的核心思想是:让每个码值对应人眼刚好能分辨的亮度差。说白了,就是“把有限的码值用在刀刃上”。

PQ曲线覆盖0.0001尼特到10000尼特,用12位整数编码(0-4095)。但注意,它不是线性的。举个例子:

码值(12-bit) 亮度(尼特) 感知差异
0 0.0001 黑电平
512 约0.5 暗部细节
1024 约5 室内灯光
2048 约100 普通SDR峰值
3072 约1000 HDR入门亮度
4095 10000 极限峰值

你看,从0到512覆盖了0.0001到0.5尼特,码值跨度很大。而从3072到4095,覆盖了1000到10000尼特,码值跨度反而小。为什么?因为人眼在暗部更敏感,需要更多码值来区分细微差别;亮部人眼已经“饱和”了,少给点码值也看不出来。

我记得第一次用PQ曲线做调色时,犯了个低级错误:把SDR素材直接映射到PQ空间,结果暗部一片死黑。后来才意识到,PQ曲线的暗部码值分布跟Gamma完全不同,必须做专门的转换LUT。

避坑指南: 我曾经在编码时把transfer=smpte2084写成了transfer=arib-std-b67(HLG的曲线)。结果播放器识别成HLG,画面整体发灰。检查了半小时才发现是transfer参数写错了。嗯,这种低级错误,犯过一次就再也不会忘了。

3.3 色彩空间:BT.2020的广阔天地

BT.2020是ITU为UHDTV(4K/8K)定义的色彩空间。它比BT.709(SDR用的)大了不少。直接看数据:

色彩空间 覆盖CIE 1931色域 典型应用
BT.709 约35.9% SDR、HDTV
DCI-P3 约53.6% 数字影院、Display P3
BT.2020 约75.8% HDR、UHDTV

BT.2020的红色和绿色原色比BT.709更“纯”,能显示更鲜艳的红色和更深的绿色。但现实是,目前几乎没有消费级显示器能完全覆盖BT.2020。你想想看,连顶级的OLED面板也只能覆盖80%左右的BT.2020。那怎么办?

答案是:内容制作时用BT.2020,显示设备用自己的色域映射。编码时把色彩原色信息写在元数据里,播放器根据显示器的能力做转换。

我在项目中遇到过一个问题:用BT.2020编码的HDR视频,在只支持DCI-P3的显示器上播放,红色过饱和得像塑料花。后来查标准才发现,BT.2020的红色原色坐标(0.708, 0.292)比DCI-P3的(0.680, 0.320)更靠外。映射算法如果只是简单裁剪,就会导致色相偏移。正确的做法是用3D LUT做色域映射,保留色相的同时压缩饱和度。

色彩矩阵与量化

HDR10使用BT.2020非恒定亮度色彩矩阵(bt2020nc)。为什么是“非恒定亮度”?因为恒定亮度矩阵(bt2020cl)虽然理论上更准确,但计算复杂,而且对压缩不友好。实际编码中,99%的HDR10内容都用bt2020nc。

量化方面,HDR10要求10位色深。8位色深在PQ曲线上会出现明显的色带(banding),因为PQ曲线在暗部码值分布太密,8位不够用。10位色深有1024个码值,基本能消除肉眼可见的色带。

一句话总结: HDR10 = BT.2020色彩空间 + PQ曲线 + 10位色深 + 静态元数据(MaxCLL/MaxFALL)。

3.4 知识体系结构图

下面这张图把HDR10的三个核心模块以及它们之间的关系画清楚了。你可以看到,从输入信号到最终显示,每一步都依赖这些参数的正确传递。

HDR10 技术原理结构图 输入视频信号 静态元数据 MaxCLL MaxFALL PQ曲线 (ST 2084) 感知量化 0.0001 ~ 10000 尼特 色彩空间 BT.2020 75.8% CIE 1931 10位色深 HDR10 编码输出 HDR 显示设备(色调映射) 三个核心模块共同决定HDR10的画质表现,缺一不可

3.5 本章小结

HDR10的三个技术支柱,每个都有它的设计哲学:

  • 静态元数据:用最少的开销告诉显示器“这片子有多亮”。简单,但够用。
  • PQ曲线:把有限的码值按人眼感知分配,暗部细腻、亮部不浪费。
  • BT.2020色彩空间:为HDR提供足够大的“调色盘”,让红色更红、绿色更绿。

我个人觉得,理解HDR10的关键不在于记住那些参数,而在于理解“为什么这么设计”。你想想看,如果PQ曲线还是用Gamma,那10000尼特的亮度得用多少位才能避免色带?如果色彩空间还用BT.709,那HDR的鲜艳感从哪来?