yuv420p转jpg linux(纯C语言实现)

author : quinncy(博客园)

最近在做一个关于图像采集的项目,需要将yuv420p的图像转换成jpg格式进行传输

在网上看到了很多方法,如利用libjpeg,ffmpeg等

偶然在论坛上看到了有人的博客里面写了利用纯C语言完成图像转换,

如果移植到arm板上就不需要安装各种库支持,因此打算试试

操作平台:ubuntu16.04

这是从论坛上下载的源代码

链接:https://pan.baidu.com/s/17loJTI4uLmRDeR4sLYm6_A 密码:3f0v

由于我在项目中采集到的图片是yuv420p格式的,因此需要对代码进行修改

先说test.c

里面有一个get_Y_U_V()函数

实现的功能是将图像文件的在一个数组里面的yuv数据分别读取到in_y,in_u,in_v里面

核心部分是下列代码:

 1     int i = 0;
 2     int y_n =0;
 3     int u_n =0;
 4     int v_n =0;
 5     int u = 0;
 6     int v = 2;
 7 
 8     while(i<size){
 9         if(i%2 != 0){
10             in_Y[y_n]= rData[i];
11             y_n++;        
12         }
13         else if(i == u){
14             in_U[u_n]= rData[i];
15             u += 4;    
16             u_n++;    
17         }
18         else if(i == v){
19             in_V[v_n] = rData[i];
20             v += 4;
21             v_n++;
22         }
23         i++;
24     }

不难看出,图像文件中的存储格式为UYUV格式的!

但是由于我在项目中生成的是YUV420p格式的图像,因此需要修改这部分的代码

yuv420p文件存储格式如下:

因此,我更改了get_Y_U_V函数的核心代码部分如下:

 1     while(i < 1280*720)
 2     {
 3         in_Y[y_n] = rData[i];
 4         i++;
 5         y_n++;
 6     }
 7     while(i < 1280*720*5/4)
 8     {
 9         in_U[u_n] = rData[i];
10         i++;
11         u_n++;
12     }
13     while(i < 1280*720*3/2)
14     {
15         in_V[v_n] = rData[i];
16         i++;
17         v_n++;
18     
19     }

这样就可以分别把yuv的数据存放到in_Y,in_U,in_V里面了。

然后再说YUV2Jpg函数里面

1     unsigned char* pYBuf;
2     unsigned char* pUBuf;
3     unsigned char* pVBuf;
4     int nYLen = nStride  * height;
5 
6     pYBuf = (unsigned char*)malloc(nYLen);    
7     pUBuf = (unsigned char*)malloc(nYLen);
8     pVBuf = (unsigned char*)malloc(nYLen);        

定义了三个指针分别指向了nYLen大小的空间

可以得到需要将每个像素点的yuv数据分别存放在pYBuf,pUBuf,pVBuf中

比如第一个像素点的yuv数据,应该是放在pYBuf[0],pUBuf[0],pVBuf[0],依次类推

因为yuv420p的数据格式中,y的数据刚好等于图像大小,最容易处理

而u,v数据是4个像素点共用一组数据,因此需要对应拓展存储到数组的相应位置

拓展的函数在processUV()

1     int i=0;
2     while(i<nStride*height){
3             pUVBuf[i] = pTmpUVBuf[i/2];
4             i++;
5     }

这是论坛源代码里面的处理函数

由于UYUV格式里面的数据是两个像素点共用一组uv数据,因此上面的处理应该很容易理解

而我所用的是yuv420p,需要四个像素点共用,因此修改函数为如下:

 1     int i,j;
 2     int count = 0;
 3     for(i = 0; i < height; i+=2)
 4     {
 5         for(j = 0; j < nStride; j+=2)
 6         {
 7             pUVBuf[i * width + j] = pTmpUVBuf[count];
 8             pUVBuf[i * width + j + 1] = pTmpUVBuf[count];
 9             pUVBuf[i * width + j + width] = pTmpUVBuf[count];
10             pUVBuf[i * width + j + width + 1] = pTmpUVBuf[count];    
11             count++;
12         }
13     }

这样处理之后,就可以将uv数据分别与像素点对应起来!

下面将main函数贴出来:

 1 int main()
 2 {
 3     unsigned char* in_Y = (unsigned char*)malloc(1280*720);//
 4     unsigned char* in_U = (unsigned char*)malloc(1280* 720/4);//
 5     unsigned char* in_V = (unsigned char*)malloc(1280* 720 / 4);//
 6     unsigned char* pData = (unsigned char*)malloc(1280 * 720);//
 7     unsigned char* rData = (unsigned char*)malloc(1280*720*3/2);
 8     
 9     unsigned long dwSize = 0;
10     FILE *rfp = fopen("./00001.yuv","rb");
11     if(NULL == rfp)
12         fprintf(stderr,"fopen fp error:%s
",strerror(errno));
13     fread(rData,1280*720*3/2,1,rfp);
14     get_Y_U_V(rData,in_Y,in_U,in_V,1280,720);
15     
16 
17     YUV2Jpg(in_Y,in_U,in_V,1280,720,100,1280,pData,&dwSize);
18     FILE *fp = fopen("2.jpg","wb");
19     fwrite(pData,dwSize,1,fp);
20     fclose(fp);
21         
22     free(rData);
23     free(in_Y);
24     free(in_U);
25     free(in_V);
26     free(pData);
27 
28     return 0;
29 }

修改00001.yuv部分为你的yuv图像路径与文件名

再将图像的大小改为你的图像数据(我用的是1280x720的)

应该就可以生成2.jpg格式的图像了

注:

这个在linux(ubuntu16.04)上面使用gcc编译后已经验证成功,可以将00001.yuv格式图像转换为2.jpg

但是使用arm-linux-gcc编译后,在arm板上执行不了,会出现内存不够的现象

尝试使用malloc后,内存问题解决,但是又发现执行时间太长(大概1个半小时左右),

打算用在arm板上的慎用!!!

完整代码已经上传到github上面,里面包含测试文件00001.yuv文件,以及生成的2.jpg图像,链接如下:

https://github.com/quinncy/yuv420p_jpg_linux_C/tree/master 

现在已经通过libjpeg实现arm板上面的yuv420p到jpg的图像转换,后面会写相关的博客~~

原文地址:https://www.cnblogs.com/zhq-blog/p/8832157.html