转码后颜色发灰、过饱和?聊聊色彩空间那点坑
你有没有遇到:原片在剪辑软件里颜色很正,导出之后发灰、发暗,或者 HDR 转成普通片之后颜色"怪怪的"?这多半不是你眼睛的问题,是 色彩空间(color space)/ 传递函数(transfer)/ 色域(primaries) 在转码时丢了或者配错了。这篇文章把最常见的坑捋一遍。
三个概念
- 色域(primaries / matrix):红绿蓝三点落在哪,决定能显示哪些颜色。常见 BT.601(标清)、BT.709(高清)、BT.2020(4K/UHD)。
- 传递函数(transfer):线性的光强怎么映射到屏幕上的亮度值。SDR 用 gamma,HDR 用 PQ 或 HLG。
- 范围(range):亮度取值是 limited(16–235,电视标准)还是 full(0–255,PC 标准)。
转码时这三样只要有一样没带走,播放器就会"猜",猜错就发灰或过饱和。
ffmpeg 怎么带色彩信息
关键参数:
ffmpeg -i in.mp4 -c:v libx264 \
-color_primaries bt709 \
-color_trc bt709 \
-colorspace bt709 \
-color_range tv \
out.mp4
如果是 HDR 源,要带上 PQ/HLG 和 BT.2020:
ffmpeg -i hdr.mkv -c:v libx265 \
-color_primaries bt2020 \
-color_trc smpte2084 \
-colorspace bt2020nc \
out.mkv
常见翻车现场
| 现象 | 原因 | 解法 |
|---|---|---|
| 整体发灰、对比低 | limited/full 范围搞反 | 明确 -color_range 并和播放端一致 |
| 颜色"塑料感"、过饱和 | 把 SDR 当 HDR 解,或反之 | 确认 trc 是 bt709 还是 smpte2084 |
| HDR 变暗一片 | 没带 PQ 信息,被当 SDR | 保留 smpte2084 + bt2020 |
| 不同播放器颜色不一样 | 播放器忽略元数据自己猜 | 封装时把元数据写进容器(上面参数即做这事) |
SDR 和 HDR 互转别忘了
把 HDR 压成 SDR(在普通显示器看)不能在命令行里"只改标签",得真正做色调映射(tone mapping):
ffmpeg -i hdr.mkv -vf zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p -c:v libx264 sdr.mp4
tonemap 滤镜才是真正把高动态压回低动态,光改 metadata 只会越改越怪。
常见问题
Q:为什么原片没事,转完就有事? 原片容器里自带色彩元数据,重新封装/转码时没用参数带出来,播放器就丢了。
Q:手机看和电脑看不一样? 两端对"缺省色彩"的假设不同,尽量把元数据写死进文件最稳。
Q:yuv420p 和颜色有关系吗? 有,420 是色度抽样,影响清晰度但不直接决定颜色;但 HDR 常需 yuv420p10le 这类 10bit 格式,强行降 8bit 也会变色带。