BYTE* pV = pY + (((Height + 15) & ~15) * Stride);
BYTE* pU = pY + (((((Height * 3) / 2) + 15) & ~15) * Stride);
其中 pY 是指向内存数组开头的字节指针,如下图所示。
此格式与 IMC1 相同,只是交换了 you 和 V 平面,如下图所示。
建议使用以下 FOURCC 代码使用四种 4:2:0 12-bpp 格式:
在所有这些格式中,色度通道在水平和垂直维度中都按 2 的因子进行子采样。
此格式与 IMC1 相同,区别如下:V (Cr) 和 U (Cb) 线交错在半步幅边界处。 换句话说,色度区域中的每个全步幅线都以一行 V 样本开头,然后是一条 U 样本线,从下一个半步幅边界开始 (图 7) 。 此布局比 IMC1 更有效地利用地址空间。 它将色度地址空间减半,从而使总地址空间减少 25%。 在 4:2:0 格式中,IMC2 是仅次于 NV12 的第二首选格式。 下图演示了此过程。
此格式与 IMC2 相同,只是交换了 U (Cb) 和 V (Cr) 行,如下图所示。
所有 Y 样本首先在内存中显示为无符号 字符 值的数组。 此数组紧跟所有 V (Cr) 示例。 V 平面的步幅是 Y 平面步幅的一半;V 平面包含的线条数是 Y 平面的一半。 V 平面紧跟所有 U (Cb) 样本,其步幅和行数与 V 平面相同,如下图所示。
所有 Y 样本首先在内存中显示为具有偶数行数的无符号 字符 值的数组。 Y 平面后面紧跟一组无符号 字符 值,其中包含打包的 U (Cb) 和 V (Cr) 样本。 当组合的 U-V 数组作为 little-endian WORD 值的数组进行寻址时,LSB 包含 U 值,而 MSB 包含 V 值。 NV12 是 DirectX VA 的首选 4:2:0 像素格式。 这是支持 4:2:0 视频的 DirectX VA 加速器的中期要求。 下图显示了 Y 平面以及包含打包的和 V 样本的数组。
颜色空间和色度采样率转换
本部分提供有关在 YUV 和 RGB 之间进行转换以及在某些不同 YUV 格式之间进行转换的准则。 本部分将考虑两种 RGB 编码方案: 8 位计算机 RGB(也称为 sRGB 或“全刻度”RGB)和 工作室视频 RGB,或“带头部房间和足部房间的 RGB”。这些定义如下:
计算机 RGB 对红色、绿色和蓝色的每个样本使用 8 位。 黑色由 R = G = B = 0 表示,白色由 R = G = B = 255 表示。
对于红色、绿色和蓝色的每个样本,工作室视频 RGB 使用一定数量的位 N,其中 N 为 8 或更多。 工作室视频 RGB 使用与计算机 RGB 不同的比例系数,并且具有偏移量。 黑色由 R = G = B = 16*2^ (N-8) 表示,白色由 R = G = B = 235*2^ (N-8) 表示。 但是,实际值可能超出此范围。
工作室视频 RGB 是 Windows 中视频的首选 RGB 定义,而计算机 RGB 是非视频应用程序的首选 RGB 定义。 在 RGB 的任一形式中,色度坐标均按照 RGB-R BT.709 中为 RGB 颜色原色的定义所指定。 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 YUV 转换为 RGB。 AYUV 格式是 4:4:4 格式,对于 Y、U 和 V 样本,各使用 8 位。 对于某些应用程序,还可以使用每个样本超过 8 位来定义 YUV。
已为数字视频定义了两个基于 RGB 的主导 YUV 转换。 两者都基于称为ITU-R建议BT.709的规范。 第一个转换是为 BT.709 中的 50-Hz 使用定义的旧 YUV 形式。 它与ITU-R建议BT.601中指定的关系相同,也称为其旧名称CCIR 601。 应将其视为标准清晰度电视分辨率 (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。 对于工作室视频 RGB,Z 等于 16*2^ (N-8) ,其中 N 是每个 RGB 样本的位数 (N >= 8) 。
S 是缩放变量。 对于计算机 RGB,S 等于 255。 对于工作室视频 RGB,S 等于 219*2^ (N-8) 。
函数下限 (x) 返回小于或等于 x 的最大整数。 函数 clip3 (x、y、z) 定义如下:
clip3(x, y, z) = ((z < x) ? x : ((z > y) ? y : z))
clip3 应实现为函数而不是预处理器宏;否则,将多次计算参数。
Y 样本表示亮度,you 和 V 样本分别表示蓝色和红色的颜色偏差。 Y 的标称范围为 16*2^ (M-8) 到 235*2^ (M-8) 。 黑色表示为 16*2^ (M-8) ,白色表示为 235*2^ (M-8) 。 你和 V 的名义范围为 16*2^ (M-8) 到 240*2^ (M-8) ,值 128*2^ (M-8) 表示中性色度。 但是,实际值可能超出这些范围。
对于工作室视频 RGB 形式的输入数据,需要执行剪辑操作,以便将 you 和 V 值保持在 0 到 (2^M) -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 公式中,可以派生 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 或 4:2:2 YUV 转换为 RGB,建议将 YUV 数据转换为 4:4:4 YUV,然后从 4:4:4 YUV 转换为 RGB。 以下各节介绍了将 4:2:0 和 4:2:2 格式转换为 4:4:4 的一些方法。
将 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 的问题。
让输入色度样本的每一条垂直线都是一个介于 0 到 N - 1 之间的数组 Cin[]
。 输出图像上的相应垂直线将是一个介于 0 到 2N - 1 的数组 Cout[]
。 若要转换每条垂直线,请执行以下过程:
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] 范围。
处理边缘的公式可以从数学上简化。 它们以这种形式显示,以说明图片边缘的固定效果。
实际上,此方法通过在四个相邻像素上内插曲线来计算每个缺失值,并加权到两个最接近的像素的值 (图 11) 。 此示例中使用的特定内插方法使用一种称为Catmull-Rom内插(也称为三次卷积内插)的已知方法,在半整数位置生成缺失样本。
在信号处理术语中,垂直上转换理想情况下应包括相移补偿,以考虑相对于输出 4:2:2 采样网格) 的输出 4:2:0 采样线和其他 4:2:2 采样线的位置之间的半像素垂直 (偏移量。 但是,引入此偏移量会增加生成样本所需的处理量,并且无法从向上采样的 4:2:2 图像中重建原始的 4:2:0 样本。 这也使得无法将视频直接解码为 4:2:2 图面,然后将这些图面用作参考图片来解码流中的后续图片。 因此,此处提供的方法不考虑样本的精确垂直对齐。 在相当高的图片分辨率下,这样做在视觉上可能并不有害。
如果从使用 H.261 中定义的采样网格的 4:2:0 视频开始, H.263 或 MPEG-1 视频,输出 4:2:2 色度样本的相位也将相对于光度采样网格上的间距进行半像素水平偏移, (相对于 4:2:2 色度采样网格) 间距的四分之一像素偏移。 但是,MPEG-2 形式的 4:2:0 视频可能更常用在电脑上,并且不会遇到此问题。 此外,在相当高的图片分辨率下,这种区别在视觉上可能并不有害。 尝试更正此问题将创建与垂直相位偏移讨论的相同类型问题。
将 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。 还可以切换两个上转换过程的顺序,因为操作顺序对结果的视觉质量并不重要。
其他一些不太常见的 YUV 格式包括:
AI44 是一种淡化 YUV 格式,每个样本 8 位。 每个样本在 4 个最有效位 (MSB) 中包含一个索引, (LSB) 包含 4 个最小有效位中的 alpha 值。 索引引用 YUV 调色板条目的数组,这些条目必须在格式的媒体类型中定义。 此格式主要用于亚图片图像。
NV11 是 4:1:1 平面格式,每像素 12 位。 Y 示例首先显示在内存中。 Y 平面后跟一组打包的 U (Cb) 和 V (Cr) 样本。 当组合的 U-V 数组作为小端 WORD 值的数组进行寻址时,U 样本包含在每个 WORD 的 LSB 中,V 样本包含在 MSB 中。 (此内存布局类似于 NV12,尽管色度采样不同。)
Y41P 是 4:1:1 打包格式,每四个像素水平采样一次你和 V。 每个宏像素包含 8 个像素(以 3 个字节为单位),并具有以下字节布局: U0 Y0 V0 Y1 U4 Y2 V4 Y3 Y4 Y5 Y6 Y7
Y41T 与 Y41P 相同,但每个 Y 样本的最小有效位指定色度键 (0 = 透明,1 = 不透明) 。
Y42T 与 UYVY 相同,但每个 Y 样本的最小有效位指定色度键 (0 = 透明,1 = 不透明) 。
YVYU 等效于 YUYV,但交换了 you 和 V 样本。
本文所述的每种 YUV 格式都有分配的 FOURCC 代码。 FOURCC 代码是通过串联四个 ASCII 字符创建的 32 位无符号整数。
可通过各种 C/C++ 宏更轻松地在源代码中声明 FOURCC 值。 例如, MAKEFOURCC 宏在 Mmsystem.h 中声明, FCC 宏在 Aviriff.h 中声明。 按如下所示使用它们:
DWORD fccYUY2 = MAKEFOURCC('Y','U','Y','2');
DWORD fccYUY2 = FCC('YUY2');
还可以直接通过反转字符顺序将 FOURCC 代码声明为字符串文本。 例如:
DWORD fccYUY2 = '2YUY'; // Declares the FOURCC 'YUY2'
由于 Windows 操作系统使用小端体系结构,因此必须反转顺序。 “Y”= 0x59,“U”= 0x55,“2”= 0x32,因此“2YUY”为0x32595559。
在 Media Foundation 中,格式由主要类型 GUID 和子类型 GUID 标识。 计算机视频格式的主要类型始终MFMediaType_Video 。 可以通过将 FOURCC 代码映射到 GUID 来构造子类型,如下所示:
XXXXXXXX-0000-0010-8000-00AA00389B71
其中 XXXXXXXX
是 FOURCC 代码。 因此,YUY2 的子类型 GUID 为:
32595559-0000-0010-8000-00AA00389B71
最常见的 YUV 格式 GUID 的常量在头文件 mfapi.h 中定义。 有关这些常量的列表,请参阅 视频子类型 GUID。
关于 YUV 视频
视频媒体类型