共计 1210 个字符,预计需要花费 4 分钟才能阅读完成。
公式
RGB to YCbCr
Y = a * R + b * G + c * B
Cb = (B - Y) / d
Cr = (R - Y) / e
YCbCr to RGB
R = Y + e * Cr
G = Y - (a * e / b) * Cr - (c * d / b) * Cb
B = Y + d * Cb
BT.601/JPEG | BT.709 | BT.2020 | |
---|---|---|---|
a | 0.299 | 0.2126 | 0.2627 |
b | 0.587 | 0.7152 | 0.6780 |
c | 0.114 | 0.0722 | 0.0593 |
d | 1.772 | 1.8556 | 1.8814 |
e | 1.402 | 1.5748 | 1.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