libjpeg实现arm板上yuv420p转jpg
前面一个博客中写到用纯c语言的转换代码只能在linux(ubuntu16.04)下面完成转换
链接:http://www.cnblogs.com/zhq-blog/p/8832157.html
但是现在又需要在arm板上面执行,emmmm···
在网上查找了下资料,有使用ffmpeg,libjpeg,libjpeg-trubo的
这里选用的是libjpeg来进行实现
首先就是在Linux上面进行libjpeg库的交叉编译
这里是参考了一个博客(亲测可用):
https://blog.csdn.net/lihui126/article/details/43057147
然后将编译完成的动态链接库拷贝到arm板上
搭载好编译环境之后,然后就要编写libjpeg的转换程序了
这个也是参考了一篇博客:
https://blog.csdn.net/yixianfeng41/article/details/52181578
主要是利用了里面的yuv420p_to_jpeg()这个函数
1 int yuv420p_to_jpeg(const char * filename, const char* pdata,int image_width,int image_height, int quality) 2 { 3 struct jpeg_compress_struct cinfo; 4 struct jpeg_error_mgr jerr; 5 cinfo.err = jpeg_std_error(&jerr); 6 jpeg_create_compress(&cinfo); 7 8 FILE * outfile; // target file 9 if ((outfile = fopen(filename, "wb")) == NULL) { 10 fprintf(stderr, "can't open %s\n", filename); 11 exit(1); 12 } 13 jpeg_stdio_dest(&cinfo, outfile); 14 15 cinfo.image_width = image_width; // image width and height, in pixels 16 cinfo.image_height = image_height; 17 cinfo.input_components = 3; // # of color components per pixel 18 cinfo.in_color_space = JCS_YCbCr; //colorspace of input image 19 jpeg_set_defaults(&cinfo); 20 jpeg_set_quality(&cinfo, quality, TRUE ); 21 22 ////////////////////////////// 23 // cinfo.raw_data_in = TRUE; 24 cinfo.jpeg_color_space = JCS_YCbCr; 25 cinfo.comp_info[0].h_samp_factor = 2; 26 cinfo.comp_info[0].v_samp_factor = 2; 27 ///////////////////////// 28 29 jpeg_start_compress(&cinfo, TRUE); 30 31 JSAMPROW row_pointer[1]; 32 33 unsigned char *yuvbuf; 34 if((yuvbuf=(unsigned char *)malloc(image_width*3))!=NULL) 35 memset(yuvbuf,0,image_width*3); 36 37 unsigned char *ybase,*ubase; 38 ybase=pdata; 39 ubase=pdata+image_width*image_height; 40 int j=0; 41 while (cinfo.next_scanline < cinfo.image_height) 42 { 43 int idx=0; 44 for(int i=0;i<image_width;i++) 45 { 46 yuvbuf[idx++]=ybase[i + j * image_width]; 47 yuvbuf[idx++]=ubase[j/2 * image_width+(i/2)*2]; 48 yuvbuf[idx++]=ubase[j/2 * image_width+(i/2)*2+1]; 49 } 50 row_pointer[0] = yuvbuf; 51 jpeg_write_scanlines(&cinfo, row_pointer, 1); 52 j++; 53 } 54 jpeg_finish_compress(&cinfo); 55 jpeg_destroy_compress(&cinfo); 56 fclose(outfile); 57 return 0; 58 }
解释一下部分参数的含义:
filename -- 输出文件的名字(看清楚,是输出!!!也就是xxx.jpg)
pData -- 输入文件的数据(yuv420sp格式的!!别被函数名迷惑了!!!)
由于我拍摄的图片是yuv420格式的,不能直接利用上面那个函数
有两种方案可以选择:
1.选择上面博客中的yuv420p_to_yuv420sp()函数来进行格式转换后,再调用yuv420p_to_jpeg()这个函数
2.就是在yuv420p_to_jpeg()这个函数内部修改读取数据的方式(由原来的yuv420sp格式修改为yuv420p格式)
鉴于本人特别懒,因此选用了第一种方案:
但是在使用上面博客中的yuv420p_to_yuv420sp()这个函数时,发现转换过程有问题
原来的代码是:
1 int yuv420p_to_yuv420sp(unsigned char * yuv420p,unsigned char* yuv420sp,int width,int height) 2 { 3 if(yuv420p==NULL) 4 return; 5 int i=0,j=0; 6 //Y 7 for(i=0;i<width*height;i++) 8 { 9 yuv420sp[i]=yuv420p[i]; 10 } 11 12 int m=0,n=0; 13 for(int j=0;j<width*height/2;j++) 14 { 15 if(j%2==0) 16 yuv420sp[j+width*height]=yuv420p[m++]; 17 else 18 yuv420sp[j+width*height]=yuv420p[n++]; 19 } 20 }
YUV420SP YUV420P
上面的m和n的取值不对,因为对于YUV420SP和YUV420P来说,前面Y的数据都是相同的,所以可以通过前面一个循环直接赋值
但是后面在修改UV格式数据的顺序时,按照上面的写法,一开始m=0,n=0,因此需要把yuv420p[0]赋值,但是yuv420p[0]明显是Y部分的数据,不可以赋值给UV的
因此需要修改该部分代码如下:
1 int yuv420p_to_yuv420sp(unsigned char * yuv420p,unsigned char* yuv420sp,int width,int height) 2 { 3 if(yuv420p==NULL) 4 return 0; 5 int i=0,j=0; 6 //Y 7 for(i=0;i<width*height;i++) 8 { 9 yuv420sp[i]=yuv420p[i]; 10 } 11 12 int m=0,n=0; 13 for(j=0;j<width*height/2;j++) 14 { 15 if(j%2==0) 16 { 17 yuv420sp[j+width*height]=yuv420p[width*height+m]; 18 m++; 19 } 20 else 21 { 22 yuv420sp[j+width*height]=yuv420p[width*height*5/4+n]; 23 n++; 24 } 25 } 26 }
这样修改后就没有问题了
然后在主函数文件里面添加#include <jpeglib.h>,编译的时候记得最后加上-ljpeg参数
就可以对YUV420p格式的数据文件进行转换了
注:
由于我使用的arm板上面不能执行可执行文件(连普通的helloworld程序交叉编译后都不能执行)
因此我在编译的时候使用了-static参数进行静态编译
但是在静态编译遇到了问题:arm-none-linux-gnueabi/bin/ld: cannot find -ljpeg collect2: ld returned 1 exit status(动态编译的时候没有)
这个问题其实是由于在交叉编译的路径里面没有包含libjpeg库的静态链接库文件libjpeg.a
将你编译生成的静态链接库libjpeg.a拷贝到arm-none-linux-gcc的lib目录下就再次进行静态编译就可以了
代码已经上传到github上面(包含完整代码以及测试文件),链接如下:
https://github.com/quinncy/yuv2jpg_linux_libjpeg