YUV420转YUV444
在最近的CCP测试中,需要使用YUV444的测试序列,而平时使用的全都是YUV420的序列,因此自己尝试用C写了一个YUV420转YUV444的程序。
1、YUV分三种采样方式:
YUV444:对于每一个像素都对应一个Y分量、一个U分量、一个V分量。
YUV422:对于一个像素都对应一个Y分量,但是每两个像素(或者说Y分量)对应一个U分量和一个V分量。
YUV420:对于一个像素都对应一个Y分量,但是每四个像素(或者说Y分量)对应一个U分量和一个V分量。
2、YUV的存储格式:
YUV在存储时是以数组的形式存储的,可以看做连续的三个数组。三个数组分别单独存储Y、U、V分量。
以一副1920*1080的YUV444图像为例,如图,YUV分量分别存储在大小为1920*1080的数组中,因此对于数据的操作十分简单。
同理对于YUV420和YUV422,只是U和V的数组大小的不同而已。
总数据量来看,YUV444需要存储1920*1080*3个值,YUV422需要存储1920*1080*2个值,YUV420需要存储1920*1080*3/2个值。
3、YUV420转YUV444主要思路:
以U分量为例,其序列如下
对U进行插值,每个田字格四个位置使用一个U值。
在代码中,可以直接通过对数组循环赋值即可完成。
对V分量的操作相同。
4、代码实现
YUV420转YUV444函数:
- #include "YUV.h"
- #include<malloc.h>
- #include<memory.h>
- typedef unsigned char UINT8;
- typedef signed short INT16;
- typedef signed int INT32;
- #define Y_SIZE (PIC_W*PIC_H)
- #define YUV420_SIZE (Y_SIZE*3/2) //4:2:0格式
- #define YUV422_SIZE (Y_SIZE*2) //4:2:2格式
- #define YUV444_SIZE (Y_SIZE*3) //4:4:4格式
- int YUV2YUV(unsigned char *yuv_buff,unsigned char *yuv2_buff, int PIC_W, int PIC_H)
- {
- UINT8 *y_buf=NULL;
- y_buf = (UINT8*)malloc(YUV444_SIZE);
- if(y_buf == NULL)
- {
- printf("Error: malloc buf.\n");
- exit(1);
- }
- int h,v;
- //Y分量转换;
- for(v=0; v<PIC_H; v++)
- {
- for(h=0; h<PIC_W; h++)
- {
- y_buf[v*PIC_W+h] = yuv_buff[(v*PIC_W+h)];
- }
- }
- //UV分量转换
- for(v=0; v<PIC_H; v+=2)
- {
- for(h=0; h<PIC_W; h+=2)
- {
- y_buf[PIC_H*PIC_W+v*PIC_W+h] = yuv_buff[PIC_H*PIC_W+v/2*PIC_W/2+h/2];
- y_buf[PIC_H*PIC_W+v*PIC_W+h+1] = yuv_buff[PIC_H*PIC_W+v/2*PIC_W/2+h/2];
- y_buf[PIC_H*PIC_W+(v+1)*PIC_W+h] = yuv_buff[PIC_H*PIC_W+v/2*PIC_W/2+h/2];
- y_buf[PIC_H*PIC_W+(v+1)*PIC_W+h+1] = yuv_buff[PIC_H*PIC_W+v/2*PIC_W/2+h/2];
- y_buf[2*PIC_H*PIC_W+v*PIC_W+h] = yuv_buff[5*PIC_H*PIC_W/4+v/2*PIC_W/2+h/2];
- y_buf[2*PIC_H*PIC_W+v*PIC_W+h+1] = yuv_buff[5*PIC_H*PIC_W/4+v/2*PIC_W/2+h/2];
- y_buf[2*PIC_H*PIC_W+(v+1)*PIC_W+h] = yuv_buff[5*PIC_H*PIC_W/4+v/2*PIC_W/2+h/2];
- y_buf[2*PIC_H*PIC_W+(v+1)*PIC_W+h+1] = yuv_buff[5*PIC_H*PIC_W/4+v/2*PIC_W/2+h/2];
- }
- }
- memcpy(yuv2_buff,y_buf,YUV444_SIZE);
- free(y_buf);
- y_buf=NULL;
- return 1;
- }
main函数如下,主要完成对YUV数据的读取和存储:
- #include "YUV.h"
- #define _CRT_SECURE_NO_WARNINGS
- #define IMAGEWIDTH 1024 //图像的宽
- #define IMAGEHEIGHT 768 //高
- int main(int argc, char *argv[])
- {
- //读入参数,共有3个参数
- if( argc != 3 )
- {
- printf ("Please input infile and outfile!\n");
- exit(-1);
- }
- FILE * input_yuvfile; //输入YUV420图像
- FILE * output_yuvfile; //输出YUV444图像
- if(NULL == (input_yuvfile = fopen(argv[1], "rb")))
- {
- printf("File input is can't open!\n");
- return -1;
- }
- if(NULL == (output_yuvfile = fopen(argv[2], "wb")))
- {
- printf("File output is can't open!\n");
- return -1;
- }
- int readsize;
- unsigned char *in_buff;
- in_buff = (unsigned char *)malloc(IMAGEWIDTH*IMAGEHEIGHT*sizeof(unsigned char)*3/2);
- unsigned char *out_buff,*yuv_buff;
- out_buff=(unsigned char *)malloc(IMAGEWIDTH*IMAGEHEIGHT*sizeof(unsigned char)*3);
- memset (out_buff,0,IMAGEWIDTH*IMAGEHEIGHT*sizeof(unsigned char)*3);
- yuv_buff=(unsigned char *)malloc(IMAGEWIDTH*IMAGEHEIGHT*sizeof(unsigned char)*3/2);
- memset (yuv_buff,0,IMAGEWIDTH*IMAGEHEIGHT*sizeof(unsigned char)*3/2);
- while(1)
- {
- readsize=fread(in_buff,1 , IMAGEWIDTH*IMAGEHEIGHT*3/2,input_yuvfile);
- if(readsize<IMAGEWIDTH*IMAGEHEIGHT*3/2) //读取的数据量少于IMAGEWIDTH*IMAGEHEIGHT*3/2时跳出
- break;
- memcpy(yuv_buff,in_buff,IMAGEWIDTH*IMAGEHEIGHT*sizeof(unsigned char)*3/2);
- YUV2YUV(yuv_buff, out_buff,IMAGEWIDTH,IMAGEHEIGHT);
- fwrite(out_buff,1,IMAGEWIDTH*IMAGEHEIGHT*3,output_yuvfile);
- }
- free(in_buff);
- in_buff=NULL;
- free(out_buff);
- out_buff=NULL;
- free(yuv_buff);
- yuv_buff=NULL;
- fclose(input_yuvfile);
- fclose(output_yuvfile);
- }