| |
http://blog.21ic.com/more.asp?name=sclarkca&id=20453
本节提供了在 YUV 和 RGB 之间进行转换的指南,以及在某些不同 YUV 格式之间进行转换的指南。在本节中,我们会以两个 RGB 编码方案为例:8 位计算机 RGB 和 studio 视频 RGB,前者也称为 sRGB 或“全范围”RGB,后者也称为“带有头空间和脚空间的 RGB”。这两个方案的定义如下:
• |
计算机 RGB 对于每个红色、绿色和蓝色样例都使用 8 位。黑色表示为 R = G = B = 0,白色则表示为 R = G = B = 255。 |
• |
Studio 视频 RGB 对于每个红色、绿色和蓝色样例使用一定的位数,即 N 位,其中 N 为 8 或更大的数字。Studio 视频 RGB 使用的缩放系数与计算机 RGB 使用的缩放系数不同,它具有一个偏移量。黑色表示为 R = G = B = 16*2N-8,白色则表示为 R = G = B = 235*2N-8。但是,实际的值可能不在此范围之内。 |
Studio 视频 RGB 是 Windows 中视频的首选 RGB 定义,而计算机 RGB 则是非视频应用的首选 RGB 定义。在这两种形式的 RGB 中,色度座标都与在 RGB 原色定义的 ITU-R BT.709 中指定的一样。R、G 和 B 的 (x,y) 座标分别为 (0.64, 0.33)、(0.30, 0.60) 和 (0.15, 0.06)。基准白色为 D65,座标为 (0.3127, 0.3290)。标称灰度系数为 1/0.45(大约为 2.2),精确的灰度系数在 ITU-R BT.709 中进行了详细定义。
RGB 和 4:4:4 YUV 之间的转换
我们首先讲述 RGB 和 4:4:4 YUV 之间的转换。要将 4:2:0 或 4:2:2 YUV 转换为 RGB,我们建议首先将 YUV 数据转换为 4:4:4 YUV,然后再将 4:4:4 YUV 转换为 RGB。AYUV 格式是一个 4:4:4 格式,它对于每个 Y、U 和 V 样例都使用 8 位。对于某些应用,还可以使用每样例多于 8 位的位数定义 YUV。
对于数字视频,定义了从 RGB 到两个主要 YUV 的转换。这两个转换都基于称为 ITU-R Recommendation BT.709 的规范。第一个转换是 BT.709 中定义用于 50-Hz 的较早的 YUV 格式。它与在 ITU-R Recommendation BT.601 中指定的关系相同, ITU-R Recommendation BT.601 也被称为它的旧名称 CCIR 601。这种格式应该被视为用于标准定义 TV 分辨率 (720 x 576) 和更低分辨率视频的首选 YUV 格式。它的特征由下面两个常量 Kr 和 Kb 的值来定义:
Kr = 0.299 Kb = 0.114
第二个转换为 BT.709 中定义用于 60-Hz 的较新 YUV 格式,应该被视为用于高于 SDTV 的视频分辨率的首选格式。它的特征由下面两个不同的常量值来定义:
Kr = 0.2126 Kb = 0.0722
从 RGB 到 YUV 转换的定义以下列内容开始:
L = Kr * R + Kb * B + (1 – Kr – Kb) * G
然后,按照下列方式获得 YUV 值:
Y = floor(2^(M-8) * (219*(L–Z)/S + 16) + 0.5) U = clip3(0, 2^M-1, floor(2^(M-8) * (112*(B-L) / ((1-Kb)*S) + 128) + 0.5)) V = clip3(0, 2^M-1, floor(2^(M-8) * (112*(R-L) / ((1-Kr)*S) + 128) + 0.5))
其中
• |
M 为每个 YUV 样例的位数 (M >= 8)。 |
• |
Z 为黑电平变量。对于计算机 RGB,Z 等于 0。对于 studio 视频 RGB,Z 等于 16*2N-8,其中 N 为每个 RGB 样例的位数 (N >= 8)。 |
• |
S 为缩放变量。对于计算机 RGB,S 等于 255。对于 studio 视频 RGB,S 等于 219*2N-8。 |
函数 floor(x) 返回大于或等于 x 的最大整数。函数 clip3(x, y, z) 的定义如下所示:
clip3(x, y, z) = ((z < x) ? x : ((z > y) ? y : z))
Y 样例表示亮度,U 和 V 样例分别表示偏向蓝色和红色的颜色偏差。Y 的标称范围为 16*2M-8 到 235*2M-8。黑色表示为 16*2M-8,白色表示为 235*2M-8。U 和 V 的标称范围为 16*2M-8 到 240*2M-8,值 128*2M-8 表示中性色度。但是,实际的值可能不在这些范围之内。
对于 studio 视频 RGB 形式的输入数据,要使得 U 和 V 值保持在 0 到 2M-1 范围之内,必需进行剪辑操作。如果输入为计算机 RGB,则不需要剪辑操作,这是因为转换公式不会生成超出此范围的值。
这些都是精确的公式,没有近似值。本文后面的所有内容均派生自这些公式。
• |
示例:将 RGB888 转换为 YUV 4:4:4 |
• |
示例:将 8 位 YUV 转换为 RGB888 |
• |
将 4:2:0 YUV 转换为 4:2:2 YUV |
• |
将 4:2:2 YUV 转换为 4:4:4 YUV |
• |
将 4:2:0 YUV 转换为 4:4:4 YUV |
示例:将 RGB888 转换为 YUV 4:4:4
在输入为计算机 RGB,输出为 8 位 BT.601 YUV 的情况下,我们相信前面一节中给出的公式可以按照下列公式进行合理近似计算:
Y = ( ( 66 * R + 129 * G + 25 * B + 128) >> 8) + 16 U = ( ( -38 * R - 74 * G + 112 * B + 128) >> 8) + 128 V = ( ( 112 * R - 94 * G - 18 * B + 128) >> 8) + 128
这些公式使用精确度不大于 8 位(不带正负号)的系数计算出 8 位结果。中间结果需要最多 16 位的精确度。
示例:将 8 位 YUV 转换为 RGB888
从原始的 RGB 到 YUV 公式,您可以为 YUV 的 8 位 BT.601 定义派生出下列关系:
Y = round( 0.256788 * R + 0.504129 * G + 0.097906 * B) + 16 U = round(-0.148223 * R - 0.290993 * G + 0.439216 * B) + 128 V = round( 0.439216 * R - 0.367788 * G - 0.071427 * B) + 128
因此,假设:
C = Y - 16 D = U - 128 E = V - 128
将 YUV 转换为计算机 RGB 的公式可以按照下列方式进行派生:
R = clip( round( 1.164383 * C + 1.596027 * E ) ) G = clip( round( 1.164383 * C - (0.391762 * D) - (0.812968 * E) ) ) B = clip( round( 1.164383 * C + 2.017232 * D ) )
其中 clip() 表示剪辑为范围 [0..255]。这些公式可以由下列公式进行合理近似计算:
R = clip(( 298 * C + 409 * E + 128) >> 8) G = clip(( 298 * C - 100 * D - 208 * E + 128) >> 8) B = clip(( 298 * C + 516 * D + 128) >> 8)
这些公式使用精确度必需大于 8 位的一些系数计算出每个 8 位结果,中间结果需要多于 16 位的精确度。
将 4:2:0 YUV 转换为 4:2:2 YUV
将 4:2:0 YUV 转换为 4:2:2 YUV 需要系数为 2 的垂直上转换。本节讲述了一个执行上转换的方法示例。该方法假设视频图片为逐行扫描。
注 4:2:0 到 4:2:2 隔行扫描转换过程会出现不常见的问题,难以实现。本文不会对转换从 4:2:0 到 4:2:2 的隔行扫描时出现的问题进行解决。
让输入色度样例的每个垂直行都成为一个数组 Cin[],其范围为从 0 到 N - 1。输出图像上相应的垂直行则会成为数组 Cout[],其范围为从 0 到 2N - 1。要转换每个垂直行,请执行下列过程:
Cout[0] = Cin[0]; Cout[1] = clip((9 * (Cin[0] + Cin[1]) – (Cin[0] + Cin[2]) + 8) >> 4); Cout[2] = Cin[1]; Cout[3] = clip((9 * (Cin[1] + Cin[2]) - (Cin[0] + Cin[3]) + 8) >> 4); Cout[4] = Cin[2] Cout[5] = clip((9 * (Cin[2] + Cin[3]) - (Cin[1] + Cin[4]) + 8) >> 4); ... Cout[2*i] = Cin[i] Cout[2*i+1] = clip((9 * (Cin[i] + Cin[i+1]) - (Cin[i-1] + Cin[i+2]) + 8) >> 4); ... Cout[2*N-3] = clip((9 * (Cin[N-2] + Cin[N-1]) - (Cin[N-3] + Cin[N-1]) + 8) >> 4); Cout[2*N-2] = Cin[N-1]; Cout[2*N-1] = clip((9 * (Cin[N-1] + Cin[N-1]) - (Cin[N-2] + Cin[N-1]) + 8) >> 4);
其中 clip() 表示剪辑范围为 [0..255]。
注 用于处理边缘的等式在计算上可以进行简化。这些等式以这种形式显示,是为了说明图片边缘的附着效果。
实际上,这种方法会通过在四个相邻像素上插入曲线,并趋向两个最近的像素值进行加权,来计算每个缺少的值(图 14)。此示例中使用的这个特定插入方法使用一个众所周知的方法来计算半整数位置缺少的样例,这个方法称为 Catmull-Rom 插入,也称为立方回旋插入。
对于信号处理过程,理想情况下,垂直上转换应该包括一个相移补偿,以将 4:2:0 样例行位置和每隔一个 4:2:2 样例行位置之间的半像素垂直偏移量(与输出 4:2:2 采样网格相比较)考虑在内。但是,引入此偏移量会提高生成样例所需的处理量,并且会导致无法从上采样 4:2:2 图像重新构造原始的 4:2:0 样例。引入此偏移量还会导致无法将视频直接解码到 4:2:2 表面,也就无法将这些表面用作解码流中后续图片的参考图片。因此,此处提供的这种方法不会考虑样例的精确垂直对齐。这样做在合理的高图片分辨率下可能不会影响视觉效果。
如果您首先从一个 4:2:0 视频开始,并且该视频使用在 H.261、H.263 和 MPEG-1 视频中定义的采样网格,输出 4:2:2 色度样例的相也会相对于灯光采样网格间隔而产生半个像素的水平 偏移量(相对于 4:2:2 色度采样网格间隔则为四分之一像素偏移量)。但是,4:2:0 视频的 MPEG-2 形式在 PC 上可能更经常使用,不会出现上述问题。而且,这种偏差在合理的高图片分辨率下可能不会影响视觉效果。尝试更正此问题会产生与垂直相偏移相同种类的问题。
将 4:2:2 YUV 转换为 4:4:4 YUV
将 4:2:2 YUV 转换为 4:4:4 YUV 需要系数为 2 的水平上转换。前面讲述的用于垂直上转换的方法也适用于水平上转换。对于 MPEG-2 和 ITU-R BT.601 视频,此方法会生成带有正确相对齐的样例。
将 4:2:0 YUV 转换为 4:4:4 YUV
要将 4:2:0 YUV 转换为 4:4:4 YUV,按照前面讲述的两个方法进行操作即可。首先将 4:2:0 图像转换为 4:2:2,然后将 4:2:2 图像转换为 4:4:4。您还可以切换两个上转换过程的顺序,因为操作顺序对于结果的视觉质量不会产生真正的影响。