YUV
YUV
相对于
YUV 采样
对于一个w * h
个像素。我们把这个像素叫做图形像素。如果用

如上图中所示,左侧一列,每一个小矩形是图形像素表示,黑框矩形是色度像素表示,小黑点是表示色度像素值
4:4:0 水平方向是1/1 ,垂直方向是1/2 ,表示一个色度像素对应了两个图形像素。4:2:2 水平方向是1/2 ,垂直方向是1/1 ,表示一个色度像素对应了两个图形像素。4:2:0 水平方向是1/2 ,垂直方向是1/2 ,表示一个色度像素对应了四个图形像素。
右侧一列是二次采样模式记号表示
- 4:4:
0 参考块第一行包含四个色度样本,第二行没有包含色度样本。 - 4:2:
2 参考块第一行包含两个色度样本,第二行也包含两个色度样本,他们是交替出现。 - 4:2:
0 参考块第一行包含两个色度样本,第二行没有包含色度样本。
现在我们发现
- 4:4:
4 表示没有对色度通道进行缩减采样。 - 4:2:
2 意味着2 :1 的水平缩减采样,没有垂直下采样。每扫描一行,每两个U 或V 采样包含四个Y 采样。 - 4:2:
0 表示2 :1 水平缩减采样,2:1 垂直缩减采样。 - 4:1:
1 表示4 :1 水平缩减采样,没有垂直下采样。每个扫描线对于每个U 或V 采样包含四个Y 采样。4:1:1 采样比其他格式少见。
换句话说来说,可以理解为:
YUV 4:4:4 采样,每一个Y 对应一组UV 分量。YUV 4:2:2 采样,每两个Y 共用一组UV 分量。YUV 4:2:0 采样,每四个Y 共用一组UV 分量。
下面三个图分别是各自的采集方式:

YUV 存储
下面的图给出了常见的
YUVY 格式(属于YUV422 )

UYVY 格式(属于YUV422 )

YUV422P(属于YUV422 )

平面格式是指用三个不同的数组来表示
如果用1280 * 720
的图片,需要占用多少存储空间呢?
- 每一个像素都需要一个
luma 值,即y 。那么总共需要1280 * 720 = 921600 bytes
。 - 每四个像素需要一个
chroma u 值,那么总共需要1280 * 720 / 4 = 230400 bytes
。 - 每四个像素需要一个
chroma v 值,那么总共需要1280 * 720 / 4 = 230400 bytes
。
把
YV12,YU12 格式(属于YUV420 )

NV12、NV21(属于YUV420 )

yuv420p 和yuv420 的区别
二者在存储格式上有区别:
- yuv420p:yyyyyyyy uuuuuuuu vvvvv
- yuv420:yuv yuv yuv
yuv420P,Y,U,
I420: YYYYYYYY UU VV =>YUV420P
YV12: YYYYYYYY VV UU =>YUV420P
NV12: YYYYYYYY UVUV =>YUV420SP
NV21: YYYYYYYY VUVU =>YUV420SP
YUV 格式与RGB 格式的换算
RGB 转换成YUV
Y = (0.257 * R) + (0.504 * G) + (0.098 * B) + 16
Cr = V = (0.439 * R) - (0.368 * G) - (0.071 * B) + 128
Cb = U = -( 0.148 * R) - (0.291 * G) + (0.439 * B) + 128
YUV 转换成RGB
B = 1.164(Y - 16) + 2.018(U - 128)
G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128)
R = 1.164(Y - 16) + 1.596(V - 128)
RGB 转YUV
Y = 0.299R + 0.587G + 0.114B
U'= (BY)*0.565
V'= (RY)*0.713
YUV 转RGB
R = Y + 1.403V'
G = Y - 0.344U' - 0.714V'
B = Y + 1.770U'
压缩格式(Packed formats)
压缩格式是指用一个数组表示
ffmpeg 中对yuv420p 像素格式大小计算
173 static const AVPixFmtDescriptor av_pix_fmt_descriptors[AV_PIX_FMT_NB] = {
174 [AV_PIX_FMT_YUV420P] = {
175 .name = "yuv420p", // 像素格式名称
176 .nb_components = 3, // 表示有三个 component,也是三个 plane
177 .log2_chroma_w = 1, // 表示色度(chroma) 像素和图形像素的水平比例关系
178 .log2_chroma_h = 1, // 表示色度(chroma) 像素和图形像素的垂直比例关系
179 .comp = {
180 { 0, 1, 0, 0, 8, 0, 7, 1 }, /* Y 平面,step 是 1,位深度是8 bit */
181 { 1, 1, 0, 0, 8, 0, 7, 1 }, /* U 平面,step 是 1,位深度是8 bit */
182 { 2, 1, 0, 0, 8, 0, 7, 1 }, /* V 平面,step 是 1,位深度是8 bit */
183 },
184 .flags = AV_PIX_FMT_FLAG_PLANAR,
185 },
所有的
53 static inline
54 int image_get_linesize(int width, int plane,
55 int max_step, int max_step_comp,
56 const AVPixFmtDescriptor *desc)
57 {
58 int s, shifted_w, linesize;
59
60 if (!desc)
61 return AVERROR(EINVAL);
62
63 if (width < 0)
64 return AVERROR(EINVAL);
// max_step_comp 的取值:0:y,1:u,2:v。对于 y 平面,每一个图形像素需要一个亮度值,
// 所以这里比例因子是 0;对于 u、v 平面来说,色度像素和图形像素在水平和垂直方向都是 2/1 的关系,
// 所以计算行的时候,比例因子取像素格式描述表中的 log2_chroma_w。对于 yuv420p 来说,取值是 1,
// 因为是通过移位运算完成的,右移 1 位,相当于是除以 2。
65 s = (max_step_comp == 1 || max_step_comp == 2) ? desc->log2_chroma_w : 0;
66 shifted_w = ((width + (1 << s) - 1)) >> s;
67 if (shifted_w && max_step > INT_MAX / shifted_w)
68 return AVERROR(EINVAL);
69 linesize = max_step * shifted_w;
70
// 如果像素描述表中的单位是 bit,那么这里转换成 bytes,右移 3 位,就是除以 8。
71 if (desc->flags & AV_PIX_FMT_FLAG_BITSTREAM)
72 linesize = (linesize + 7) >> 3;
73 return linesize;
74 }
111 int av_image_fill_pointers(uint8_t *data[4], enum AVPixelFormat pix_fmt, int height,
112 uint8_t *ptr, const int linesizes[4])
113 {
114 int i, total_size, size[4] = { 0 }, has_plane[4] = { 0 };
115
116 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
117 memset(data , 0, sizeof(data[0])*4);
118
119 if (!desc || desc->flags & AV_PIX_FMT_FLAG_HWACCEL)
120 return AVERROR(EINVAL);
121
122 data[0] = ptr;
123 if (linesizes[0] > (INT_MAX - 1024) / height)
124 return AVERROR(EINVAL);
125 size[0] = linesizes[0] * height;
126
127 if (desc->flags & AV_PIX_FMT_FLAG_PAL ||
128 desc->flags & FF_PSEUDOPAL) {
129 data[1] = ptr + size[0]; /* palette is stored here as 256 32 bits words */
130 return size[0] + 256 * 4;
131 }
132
133 for (i = 0; i < 4; i++)
134 has_plane[desc->comp[i].plane] = 1;
135
136 total_size = size[0];
137 for (i = 1; i < 4 && has_plane[i]; i++) {
// i 的取值:0:y,1:u,2:v。对于 y 平面,每一个图形像素需要一个亮度值,
// 所以这里比例因子是 0;对于 u、v 平面来说,色度像素和图形像素在水平和垂直方向都是 2/1 的关系,
// 所以计算列的时候,比例因子取像素格式描述表中的 log2_chroma_h。对于 yuv420p 来说,取值是 1,
// 因为是通过移位运算完成的,右移 1 位,相当于是除以 2。
138 int h, s = (i == 1 || i == 2) ? desc->log2_chroma_h : 0;
139 data[i] = data[i-1] + size[i-1];
140 h = (height + (1 << s) - 1) >> s;
141 if (linesizes[i] > INT_MAX / h)
142 return AVERROR(EINVAL);
// 每一平面的行和列做乘法,就是像素总数。
143 size[i] = h * linesizes[i];
144 if (total_size > INT_MAX - size[i])
145 return AVERROR(EINVAL);
// 每一个平面的像素数相加,就是图片占用的像素总数。
146 total_size += size[i];
147 }
148
149 return total_size;
150 }