前面一個博客中寫到用純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