图像的颜色信息:sRGB、CICP 与那些转换的坑

做图像处理的同学都知道色彩有 RGB,也有 YUV,图像或者视频压缩中常用的是 YUV,但显示用的是 RGB;另外在 JPEG 图片的属性中通常会看到 sRGB,在 AVIF 的图片中通常会看到 CICP 的值;这些都是什么,为什么看着简单,实际却有一些坑在,本文尝试讲清楚。

颜色信息的几个维度

颜色信息主要包含三个维度——color primaries、transfer characteristics、matrix coefficients——另外还有一个独立的 range 标志。

Color primaries (CP)

定义这个色彩空间能表示的颜色范围(色域)。要理解 CP,先理解两个 CIE 1931 概念:

  • CIE XYZ:一个绝对的颜色空间,设备无关——一个颜色对应的 XYZ 值只取决于光谱和人眼模型,跟用什么屏幕/相机无关。不同 RGB 色彩空间之间的转换,本质上都是先转到 XYZ,再从 XYZ 转到目标空间。比如 sRGB → Display P3 走的是 sRGB → XYZ → Display P3 两步。ICC 把这个中转枢纽叫 PCS (Profile Connection Space)。
  • CIE 1931 chromaticity diagram (xy 色度图):那张马蹄形图,用来可视化色域。每个色彩空间(sRGB、Display P3、BT.2020)的色域是图上的一个三角形——三角形越大,能表示的颜色越多。参见 Wikipedia 上的 sRGB chromaticity diagram

CP 干的事就是在 xy 色度图上选 3 个点作为 R/G/B 原色 + 1 个点作为 white point——三角形和白点定了,色彩空间就定了。常见的 sRGB / Display P3 / BT.2020 共享 D65 white point 但 R/G/B 三角形依次变大。

一个实际的坑:色域宽不等于看得到。即使你的图片标了 Display P3,如果显示器只支持 sRGB,那超出 sRGB 三角形的颜色你也看不见——OS 的 color management 会把它们映射/clip 回 sRGB 色域。

Transfer characteristics (TC)

描述线性 RGB非线性 RGB 之间的映射曲线。要理解 TC,先理解这两种 RGB:

  • Linear RGB:值正比于真实光强度。相机 sensor 出来就是线性数据(光强 2x → 数字值 2x)。最典型的 linear RGB 载体就是相机 RAW 文件——sensor 经 black level / 白平衡 / demosaic 之后的原始数据,完全没有 gamma 编码,所以专业摄影/调色工作流都用 RAW,保留全部动态范围和后期调整余地。所有"真实光学"运算(blending、缩放、tone mapping 等)也必须在 linear 域做,否则不符合物理——例如在非线性域对两个像素取平均,结果会偏暗发灰,因为加权关系被曲线扭曲过。
  • 非线性 RGB:值经过 TC 编码,码字分布贴合人眼对亮度的感知。为什么需要非线性编码:人眼对暗部远比亮部敏感(近似对数响应),但 8/10-bit 是均匀离散;如果直接用 linear 存,亮部浪费一堆码字,暗部却出现明显 banding。非线性编码把有限的 bit budget 花到人眼最敏感的地方。

举个例子,sRGB gamma 大致是 V_非线性 ≈ V_linear^(1/2.2),这条曲线在暗部斜率陡,亮部斜率缓——linear 的暗部 [0, 0.1] 被"拉伸"到非线性的 [0, 0.35],linear 的亮部 [0.5, 1.0] 反而被"压缩"到非线性的 [0.73, 1.0]。映射到 8-bit 量化后,linear 暗部那 10% 拿到了约 90 个码字,亮部那 50% 反而只拿到约 70 个码字——刚好和人眼敏感度分布吻合。

TC 就是这两域之间的转换曲线,有两个方向——OETF(光→电,相机端)和 EOTF(电→光,显示器端)。注意"非线性"≠"gamma":gamma (power law) 只是一种,用于 SDR (sRGB / BT.709);HDR 还有 PQ、HLG 等,曲线形状完全不同但都由 TC 这个维度统一描述。

工程上的典型套路:存储/传输用非线性,运算回 linear。例如 sharpyuv 在做迭代下采样时就是在 linear 域 downsample,再转回非线性域;严格说 alpha blending、image resize 都该在 linear 域做。

Matrix coefficients (MC)

YUV ↔ 非线性 RGB 之间的转换矩阵——注意作用对象是 TC 编码后的非线性 RGB(SDR 下俗称 gamma RGB),不是 linear RGB。

常见 enum 值:

  • MC=1:BT.709(HDTV / 桌面视频默认)
  • MC=5/6:BT.601(SD 标清电视 / JPEG / WebP 默认)
  • MC=9:BT.2020-NCL(HDR / UHD 默认)
  • MC=0Identity——三个 plane 里存的不是 YUV,而是未经矩阵转换的 RGB 数据,顺序是 GBR(不是 RGB),常见于无损 RGB 编码场景(如 AVIF lossless RGB)

为什么需要这么多矩阵:YUV 里 Y 是亮度,需要按人眼对 R/G/B 的敏感度做加权(绿权重最大、蓝最小),不同标准的加权系数不同——BT.601 沿用模拟电视时代的系数,BT.709 重新测量后修正,BT.2020 因色域扩大再次调整。同一份 YUV 数据用错矩阵解码会出现色偏,这也是为什么 CICP 必须把 MC 显式写出来。

Range

YUV 的数值范围。full range 是 [0, 255],limited range 是 [16, 235],把 16 当全黑、235 当全白;[0,16][235,255] 是 headroom / footroom——历史上源于模拟视频时代给信号 overshoot/undershoot 留的余量("blacker than black" / "whiter than white"),现代数字 codec (H.264 / H.265 / AV1) 沿用了这个约定,余量同时用来吸收编码过程中的 ringing 类 overshoot,最后在 RGB 域统一 clip。JPEG 用 full range,WebP / H.26x / AV1 默认 limited range——所以不能直接把 WebP 解出来的 YUV 平面塞给 JPEG 编码器,必须先转回 RGB(比如经 PNG 中转)。

两套描述体系:ICC 与 CICP

描述这套信息有两套体系,并不是新老替代关系,而是各自主导不同的生态:

  • ICC profile(International Color Consortium):表达力强、格式灵活,主导图像与印刷工作流;JPEG / PNG / AVIF 都可以携带 ICC profile。
  • CICP(Coding-Independent Code Points):ITU-T H.273 定义的三元组 (CP/TC/MC),每个值都是查表的 enum,紧凑且自描述,主导视频和新一代 codec 生态。

引用 libavif wiki 对三者的精确定义:

CP defines a specific set of color primaries with a single enumerated value from Table 2 of H.273. TC defines a transfer function with a single enumerated value from Table 3 of H.273. MC defines a specific set of matrix coefficients as a single enumerated value from Table 4 of H.273.

一个常见坑:CICP 的三个值在 AVIF 文件中按 CP/TC/MC 顺序存储,但 ffprobe 输出时是 MC/CP/TC(MC 被挪到最前),抄数的时候很容易搞错。

AVIF 同时支持 CICP 和 ICC。如果文件携带了 ICC profile,它会覆盖 CP 和 TC(用 ICC 作为 RGB 颜色空间的 source of truth),但 MC 不被覆盖——因为 MC 描述的是编码前 YUV↔RGB 的转换方式,跟 ICC 描述的 RGB 色彩空间是正交的两件事。

Reference