YUV420知识
对于所有YUV420图像,它们的Y值排列是完全相同的,区别在于UV的排列。
所以YUV420,分为YUV420sp(semi-Planar 半平面
)与YUV420p(planar 平面
),它们的数据格式在UV排列上是不同的。420p它是先把U或者V存放完后,再存放V或者U,也就是说UV它们是连续的,而420sp是UV交替存放的。
- I420(YU12)和YV12属于YUV420p格式,将YUV分量分别打包,依次存储。
- NV12与NV21类似,Y分量和I420一样,U和V交错排列,属于YUV420sp。
YUV 4:2:0采样,每四个Y共用一组UV分量。所以,计算出一个YUV420在内存中存放的大小,就是下面Y,U,V分量的大小总和,即:Y+U+V => width * height * 3/2
Y = width * hight
U = Y / 4
V = Y / 4
图示:1个像素的YUV存储,每个字母代表一位,
I420: YYYYYYYY UUVV => YUV420P
YV12: YYYYYYYY VVUU => YUV420P
NV12: YYYYYYYY UVUV => YUV420SP
NV21: YYYYYYYY VUVU => YUV420SP
在名称中,“YV”表示平面顺序:Y,然后V(然后U)。12
指像素深度:对于YV12,每像素12位。NV12中的12也表示每像素12位。
灰白色YUV
下面这段代码生产一个1080x720的YUV文件,格式是YUV420 planar,对于planar的YUV格式,先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V。
所以,data的大小是width * height * 3 / 2
,先写width * height
大小的数据,颜色位0xb4,再写width * height / 2
的大小作为uv的数据,这里选择的UV颜色一样,所以一起写了。这样写出来的文件用ubuntu上的YUVView工具查看就是灰白色。
int width = 1080;
int height = 720;
int size = width * height * 3 / 2;
uint8_t *data = (uint8_t *)malloc(size);
memset(data, 0xb4, size);
fwrite(data, width * height, 1, fp); // write y data
memset(data + width*height, 0x80, size);
fwrite(data, width*height/2, 1, fp); //write uv data
fclose(fp);
青色YUV
青色的这个文件定义的大小是8x8的文件,因为发现通过YUVView工具,放大8x8的YUV图像后,就可以很清楚的看到YUV分量在图片里面的分布。
下面代码中先申请data内存,然后分别设置不同的值,最后按大小写入。
int width = 8;
int height = 8;
int size = width * height * 3 / 2;
uint8_t *data = (uint8_t *)malloc(size);
// set Y to 131
memset(data, 0x83/* 131 */, width*height);
// set U to 156
memset(data + width*height, 0x9c/* 156 */, width*height/4);
// set V to 44
memset(data + width*height + width*height/4, 0x2c/* 44 */, width*height/4);
FILE* fp = nullptr;
fp = fopen("/sdcard/h264.yuv", "wb");
// write Y
fwrite(data, width * height, 1, fp);
// write UV
fwrite(data+ width*height, width*height/2, 1, fp);
fclose(fp);
当然,这段代码data已经分别set了YUV分量的值,最后write的时候,一次写入data就可以,分开写fwrite只是为了这个代码上看起来更直观。
生成的文件,通过ubuntu上YUView工具,放大到64倍,就可以看到YUV的分布情况,YUV420四个点有一个UV值:
换成数组的代码
width = 8;
height = 8;
int size = width * height * 3 / 2;
uint8_t *data = (uint8_t *)malloc(size);
memset(data, 0, size);
FILE* fp = nullptr;
fp = fopen("/sdcard/h264.yuv", "wb");
// set Y to 131
for(int i = 0; i < height; i++) {
for(int j = 0; j < width; j++) {
data[i*width + j] = 131;
}
}
// set U to 156
int offset = width * height;
for(int i = 0; i < height/4; i++) {
for(int j = 0; j < width; j++) {
data[offset + i*width + j] = 156;
}
}
// set V to 45
offset = width*height + width*height/4;
for(int i = 0; i < height/4; i++) {
for(int j = 0; j < width; j++) {
data[offset + i*width + j] = 45;
}
}
fwrite(data, width * height, 1, fp);
//write UV
fwrite(data+ width*height, width*height/2, 1, fp);
fclose(fp);
YUView查看,和前面的V值不一样:
前面代码中的颜色表参考
标称范围 | 白色 | 黄色 | 青色 | 绿色 | 红色 | 蓝色 | 黑色 | |
---|---|---|---|---|---|---|---|---|
Y | 16~235 | 180 | 162 | 131 | 112 | 65 | 35 | 16 |
Cb | 16~240 | 128 | 44 | 156 | 72 | 100 | 212 | 128 |
Cr | 16~240 | 128 | 142 | 44 | 58 | 212 | 114 | 128 |