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的图像转换,后面会写相关的博客~~