C代码生成YUV420 planar格式文件

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值不一样:

在这里插入图片描述

前面代码中的颜色表参考

标称范围白色黄色青色绿色红色蓝色黑色
Y16~235180162131112653516
Cb16~2401284415672100212128
Cr16~2401281424458212114128

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值