公式

RGB to YCbCr

Y = a * R + b * G + c * BCb = (B - Y) / dCr = (R - Y) / e

YCbCr to RGB

R = Y + e * CrG = Y - (a * e / b) * Cr - (c * d / b) * CbB = Y + d * Cb
BT.601/JPEGBT.709BT.2020
a0.2990.21260.2627
b0.5870.71520.6780
c0.1140.07220.0593
d1.7721.85561.8814
e1.4021.57481.4746

d和e这两个参数是为了让Cb和Cr落在-0.5~0.5区间才引入的(使之具备与明度信号Y雷同的幅值,便于工程实现),计算方法如下:
d = (1 - c) / 0.5
e = (1 - a) / 0.5

这些系数是怎么来的,为什么要取这些值?
a, b, c这三个系数是在ITU规范中定义的,表明了RGB三原色须要以何种比例混合来生成明度信号Y,这个比例与三原色的选取无关(因为不同波长的光对亮度的奉献也不同),参考标准文档:
https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.601-7-20...
https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.709-6-20...
https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2020-2-2...

量化

通过上述公式计算出的Y, Cb, Cr并不是最终后果,ITU规定明度与色度信号并不能占满量程,比方0~1v电压范畴,明度信号取值范畴是0.06v~0.86v,其无效幅值为满量程的80%,色度信号取值范畴稍大,其作用是防止滤波器导致的过冲。

对于数字信号,ITU给出的量化公式如下:

\( D_Y' = INT[(219E_Y' + 16) \cdotp 2^{n-8}] \)
\( D_{CB}' = INT[(224E_{CB}' + 128) \cdotp 2^{n-8}]\)
\( D_{CR}' = INT[(224E_{CR}' + 128) \cdotp 2^{n-8}] \)

上述公式中,\( E_Y' \), \( E_{CB}' \), \( E_{CR}' \)为取值范畴[0, 1]的浮点数,n为位深(个别为8bit,10bit和12bit),INT为四舍五入取整(round),\( D_Y' \), \( D_{CB}' \), \( D_{CR}' \)为最终要送编码器的YUV数据。

留神:JPEG并不会将Y, Cb, Cr做量化,即应用满量程[0, 255]。

C++实现:

  • ffmpeg: https://github.com/FFmpeg/FFmpeg/blob/master/libavutil/colorspace.h
  • 我本人的实现: https://github.com/zhanwang-sky/jpeg_helper/tree/main/jpeg_helper/yuv_helper