yuv 格式详解,已经 yuv 与 rgb 格式间的转换。
RGB vs YUV
RGB
颜色编码是使用红色(Red)、绿色(Green)和蓝色(Blue)三原色以不同比例混合,用来表示其他各种颜色。其中,每种颜色占8bit,一个像素点占24bit,也就是3byte。
人体生命科学研究表明,人眼对于明暗的感知要比对色彩的感知更加敏感。
因此,可以在颜色编码中将亮度
与色度
分离开,再将部分色度
息忽略,进而压缩图像,这就是YUV
颜色编码。其中,Y表示亮度,U、V表示色度。
由于图像在采集后和显示时均使用RGB
模型表示,而为了方便传输,对图像进行压缩传输时需使用YUV
模型表示。这就需要图像在采集后由RGB
模型转换为YUV
模型,传输到显示端后(假设需要传输),再由YUV
模型转换为RGB
模型。二者相互转换的公式如下所示:
YUV采样格式
YUV4:4:4
采样假如图像像素为:[Y0 U0 V0]、[Y1 U1 V1]、[Y2 U2 V2]、[Y3 U3 V3]
那么采样的码流为:Y0 U0 V0 Y1 U1 V1 Y2 U2 V2 Y3 U3 V3
最后映射出的像素点依旧为 [Y0 U0 V0]、[Y1 U1 V1]、[Y2 U2 V2]、[Y3 U3 V3]
YUV4:2:2
采样假如图像像素为:[Y0 U0 V0]、[Y1 U1 V1]、[Y2 U2 V2]、[Y3 U3 V3]
那么采样的码流为:Y0 U0 Y1 V1 Y2 U2 Y3 V3
其中,每采样过一个像素点,都会采样其 Y 分量,而 U、V 分量就会间隔一个采集一个。
最后映射出的像素点为 [Y0 U0 V1]、[Y1 U0 V1]、[Y2 U2 V3]、[Y3 U2 V3]
YUV4:2:0
采样假设图像像素为:
[Y0 U0 V0]、[Y1 U1 V1]、 [Y2 U2 V2]、 [Y3 U3 V3]
[Y5 U5 V5]、[Y6 U6 V6]、 [Y7 U7 V7] 、[Y8 U8 V8]
那么采样的码流为:Y0 U0 Y1 Y2 U2 Y3 Y5 V5 Y6 Y7 V7 Y8
其中,每采样过一个像素点,都会采样其 Y 分量,而 U、V 分量就会间隔一行按照 2 : 1 进行采样。
最后映射出的像素点为:
[Y0 U0 V5]、[Y1 U0 V5]、[Y2 U2 V7]、[Y3 U2 V7]
[Y5 U0 V5]、[Y6 U0 V5]、[Y7 U2 V7]、[Y8 U2 V7]
YUV存储格式
YUV 的存储格式有三种:
planar
三平面格式- 指先连续存储所有像素点的 Y 分量,然后存储 U 分量,最后是 V 分量。
semi-planar
两平面格式- 先存储所有的 Y 分量,然后交替存储 U、V 分量。
packed
打包模式- 指每个像素点的 Y、U、V 分量是连续交替存储的。
基于YUV4:2:2
的存储格式
YUYV格式
为packed
打包模式每两个像素点使用 4 个分量存储,排列顺序如下:
Y0 U0 Y1 V1 ···
UYVY格式
为packet
打包模式每两个像素点使用 4 个分量存储,排列顺序如下:
U0 Y0 V1 Y1 ···
YUV422p格式
为planar
三平面格式先存储所有的 Y 分量,再存储所有的 U 分量,再存储所有的 V 分量。
基于YUV4:2:0
的存储格式
YUV420p格式
为planar
三平面格式先存储所有的 Y 分量,再存储所有的 U,V 分量,根据存储 U、V 分量的先后顺序分为
YU12格式
(又名I420格式
) 和YV12格式
。YUV420sp格式
为semi-planar
两平面格式先存储所有的 Y 分量,再交替存储 U,V 分量,存储 U、V 分量时,使用
UVUVUV···
方式存储的格式被称为NV12格式
,使用VUVUVU···
方式存储的格式被称为NV21格式
。
更加全面的存储格式 ,见表格总结:
采样形式 | 像素组织形式 | 格式名称 | 第一平面 | 第二平面 | 第三平面 |
---|---|---|---|---|---|
YUV4:4:4 | Planar | I444 | YYYYYY… | UUUUU… | VVVVVV… |
YUV4:2:2 | YUYV | ||||
YUV4:2:2 | UYVY | ||||
YUV4:2:2 | YUV422p | ||||
YUV4:2:0 | YU12 / I420 | ||||
YUV4:2:0 | YV12 | ||||
YUV4:2:0 | NV12 | ||||
YUV4:2:0 | NV21 |
YUV像素数据处理
分离
YU12/I420
存储格式文件的 Y、U、V 分量1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36/**
* 将 YUV420P(YU12/I420) 文件中的Y、U、V分量分离出来
*/
void yuv420p_splite (const char *filename, int height, int width, int frames)
{
FILE *f1 = fopen(filename, "rb+");
FILE *f2 = fopen("yuv420_y.y", "wb+");
FILE *f3 = fopen("yuv420_u.y", "wb+");
FILE *f4 = fopen("yuv420_v.y", "wb+");
int size_y = height * width;
int size_u = height * width / 4;
int size_v = height * width / 4;
int size_frame = size_y + size_u + size_v;
unsigned char *buffer = (unsigned char *)malloc(size_frame);
if (buffer == NULL)
return;
for (int i = 0; i < frames; i++)
{
fread(buffer, 1, size_frame, f1);
fwrite(buffer, 1, size_y, f2);
fwrite(buffer + size_y, 1, size_u, f3);
fwrite(buffer + size_y + size_u, 1, size_v, f4);
}
free(buffer);
fclose(f1);
fclose(f2);
fclose(f3);
fclose(f4);
}分离
I444
存储格式文件的 Y、U、V 分量1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36/**
* 将 YUV444P 文件中的 Y、U、V 分量分离出来
*/
void yuv444p_splite (const char* filename, int height, int width, int frames)
{
FILE* f1 = fopen(filename, "rb+");
FILE* f2 = fopen("yuv444p_y.y", "wb+");
FILE* f3 = fopen("yuv444p_u.y", "wb+");
FILE* f4 = fopen("yuv444p_v.y", "wb+");
int size_y = height * width;
int size_u = height * width;
int size_v = height * width;
int size_f = size_y + size_u + size_v;
unsigned char* buffer = (unsigned char*)malloc(size_f);
if (buffer == NULL)
{
return;
}
for (int i = 0; i < frames; i++)
{
fread(buffer, 1, size_f, f1);
fwrite(buffer, 1, size_y, f2);
fwrite(buffer + size_y, 1, size_u, f3);
fwrite(buffer + size_y + size_u, 1, size_v, f4);
}
free(buffer);
fclose(f1);
fclose(f2);
fclose(f3);
fclose(f4);
}