C語言實(shí)現(xiàn)YUV文件轉(zhuǎn)JPEG格式
先取yuv 文件中一個(gè)16×8的塊,跑通全流程
理解與思路:
1.塊分割
YUV 文件分為:YUV444 YUV 422 YUV420。444:就是:12個(gè)char 有4個(gè)Y,4個(gè)U,4個(gè) U,422:8個(gè)char 中有4個(gè)Y ,U,V各兩個(gè),420:意思就是8char里有6個(gè)Y,1個(gè)U,1個(gè)V。444與422 中的三分量多是交錯(cuò)存儲(chǔ)的,420則是先存儲(chǔ)Y,再存儲(chǔ)U,V。
YUV存儲(chǔ)也是線型存儲(chǔ)的,不是平面塊存儲(chǔ)的。
對(duì)應(yīng)到j(luò)peg,也要按YUV的三種格式分別分塊。jpeg協(xié)議中有一概念MCU:最小編碼塊。jpeg就是按MCU 為單位循環(huán)存儲(chǔ)的。MCU中又有若干8×8 的block。這些block 就是Y ,U ,V 分量的8×8 塊。
我理解:對(duì)應(yīng)YUV444, 一行內(nèi)取24字節(jié)(這是指水平行,程序處理同時(shí)要取8個(gè)水平行,意思MCU=8×24字節(jié))。含有Y,U,V各8個(gè)字節(jié)。也就是說:程序一次讀取8行24個(gè)char ,處理成3張8×8的表,直到讀完整個(gè)yuv文件
YUV422:一行 8個(gè)char中有4個(gè)Y,UV各2個(gè)。必須把Y湊成8那就要乘2。那就是一行為16個(gè)字節(jié),MCU=16×8,取8行生成一張8×8的Y,UV 各半張,UV要補(bǔ)0成為8×8。
YUV420:一行8個(gè)字節(jié)中有6個(gè)Y,UV各一個(gè),Y×8成為8的倍數(shù)水平要取64字節(jié),所以,MCU=8×8×8,意思就是有6張Y表,UV 各一張。
2.量化
Jpeg幾乎對(duì)Y分量不壓縮處理,只壓縮UV彩色分量。把幾個(gè)Y拼在一起余弦處理因?yàn)橛嘞肄D(zhuǎn)化是沒有損失的,也不會(huì)讓數(shù)據(jù)失真。真正讓數(shù)據(jù)失真是量化這個(gè)環(huán)節(jié),如果量化表全為1,則是無損量化。
下面的程序?yàn)榻栌贸善穲D片的量化表。
3. Z形排序
采用查表法,先按Z順序生成一個(gè)表,讀數(shù)時(shí)按表的數(shù)值作為讀取位置。
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <linux/videodev2.h> //v4l2 頭文件 #include <string.h> #include <sys/mman.h> #include <linux/fb.h> #include <math.h> #define PI 3.1415926 #define pic_width 1280 //1280*720 640*360 960*540 320*240 424*240 848*480 640*480 #define pic_heigth 720 #define filename "/home/wzpc/Pictures/my.yuv" #define file1 "/home/wzpc/Pictures/my.jpg" //借用成品圖片的量化表 //int fdct(char (*i)[8], char (*o)[8] ); int main(void) { //-----------FDCT()函數(shù)------------------------------------ int fdct(char (*i)[8], char (*o)[8] ) { //i 為輸入 o 為參數(shù)傳入的輸出轉(zhuǎn)換后的數(shù)據(jù) double s; double au; double av; for (int u = 0; u < 8; u++) { for (int v = 0; v < 8; v++) { for (int y = 0; y < 8; y++) { for (int x = 0; x < 8; x++) { s = s + (1.0 / 4) * i[y][x] * cos((2 * y + 1) * u * PI / 16) * cos((2 * x + 1) * v * PI / 16); } } if (u == 0) { au = 1.0 / sqrt(2); } else { au = 1.0; } if (v == 0) { av = 1.0 / sqrt(2); } else { av = 1.0; } s = s * au * av; //-30.1856 int s1 = round(s * 100); //-3019 s = s1 / 100.0; //-30.19 o[u][v] = s; //double 轉(zhuǎn)為char 類型 s = 0; } } return 0; } //--------Z 排序-------------------------------- int zz(char (*i1)[8],char o1[64]){ int zb[64]={0,8,1,2,9,16,24,17,10,3,4,11,18,25,32,40,33,26,19,12,5,6,13,20,27,34,41,48,56, 49,42,35,28,21,14,7,15,22,29,36,43,50,57,58,51,44,37,30,23,31,38,45,52,59,60, 53,46,39,47,54,61,62,55,63 }; char *p1=(char *)i1; for(int a=0;a<64;a++){ int c=zb[a]; o1[a]=p1[c]; } return 0; } // -----量化函數(shù)--------------- int lh(char (*i)[8],char (*lhb)[8], char (*o)[8]){ for(int a=0;a<8;a++){ for(int b=0;b<8;b++){ o[a][b]=round((i[a][b])/(lhb[a][b])); } } return 0; } FILE *f1 = fopen(file1, "rb"); //如用mmap 必須以讀寫方式打開文件 if (f1 == NULL) { puts("filename error"); exit(-1); } fseek(f1, 0, SEEK_END); int len1 = ftell(f1); fseek(f1, 0, SEEK_SET); int fd1 = fileno(f1); unsigned char *mp1 = mmap(NULL, len1, PROT_READ, MAP_SHARED, fd1, 0); //必須要讀,寫 //---------讀量化表------------------------------- char lh00[64]={}; //提取量化表 char lh10[64]={}; for(int a=0;a<len1;a++){ if((mp1[a]==0xff)&&(mp1[a+1]==0xdb)&&(mp1[a+2]==0)){ for(int b=0;b<65;b++){ if(mp1[a+b+4]==0){ memcpy(lh00,&(mp1[a+b+5]),64); } if(mp1[a+b+4]==1){ memcpy(lh10,&(mp1[a+b+5]),64); } } printf("\n"); } } for(int a=0;a<64;a++){ // printf("%d ,",lh00[a]); } printf("\n"); //--------------------------------------------------------------------------- FILE *f = fopen(filename, "rb"); //如用mmap 必須以讀寫方式打開文件 if (f == NULL) { puts("filename error"); exit(-1); } fseek(f, 0, SEEK_END); int len_file = ftell(f); fseek(f, 0, SEEK_SET); int fd = fileno(f); unsigned char *mp = mmap(NULL, len_file, PROT_READ, MAP_SHARED, fd, 0); //必須要讀,寫 int width_pic = 1280; int heigth_pic = 720; //out:1.文件總長(zhǎng)度 len_file // 2.圖片寬度 1280 width_pic // 3.圖片高度 720 heigth_pic // 4.圖片數(shù)據(jù) *mp //-------------------------------------------------------- //因?yàn)閥uv422數(shù)據(jù)是4個(gè)字節(jié)生成2個(gè)像素點(diǎn),所以圖片的width*heigth*2=len_file //攝像頭輸出的分辨率圖片寬度×2都是8的倍數(shù),高度X2也是8的倍數(shù)。所以yuv數(shù)據(jù)都不用補(bǔ)列。最多補(bǔ)文件末尾的空字節(jié) //攝像頭的分辨率末尾數(shù)X2=0,8,6,4,2,就是8的倍數(shù) // 1.----------檢查yuv文件長(zhǎng)度末尾是否有空字節(jié),有,補(bǔ)0------------------- if ((width_pic * heigth_pic * 2) != len_file) { memset(&mp[len_file], 0, (width_pic * heigth_pic * 2 - len_file)); } // 2.----------取16*8-------------------------------------- //YUV422 字節(jié)排列:[0]=Y0 [1]=U0 [2]=Y1 [3]=V0 [4]=Y2 [5]=U1 [6]=Y3 [7]=V1 4個(gè)Y 2個(gè)U 2個(gè)V //所以取16個(gè)字節(jié)的yuv 數(shù)據(jù),含8個(gè)y ,4個(gè)u,4個(gè)v unsigned char (*i_mp)[width_pic] = (unsigned char (*)[width_pic])mp; int m = 0; // 行 取1個(gè)(0,0)開始的16×8 塊 int n = 0; //列 int heigth = heigth_pic; //行 int width = width_pic; //列 int fheigth = 8; int fwidth = 16; unsigned char o[8][16] = {}; //取出16×8的塊 if ((m + fheigth) > heigth) { puts("fheigth error"); exit(-1); } if ((n + fwidth) > width) { puts("fwidth error"); exit(-1); } for (int a = 0; a < fheigth; a++) { for (int b = 0; b < fwidth; b++) { o[a][b] = i_mp[a + m][b + n]; } } munmap(mp, len_file); //----------分離Y--------------------- char y[8][8] = {}; for (int a = 0; a < 8; a++) { for (int b = 0; b < 8; b++) { y[a][b] = o[a][2 * b] - 128; } } //----------分離U----------------- char u[8][8] = {}; memset(&u, 0, 64); //補(bǔ)0 for (int a = 0; a < 8; a++) { for (int b = 0; b < 8; b++) { if ((4 * b + 1) <= 16) { u[a][b] = o[a][4 * b + 1] - 128; } } } //-----------分離v----------------- char v[8][8] = {}; memset(&v, 0, 64); //補(bǔ)0 for (int a = 0; a < 8; a++) { for (int b = 0; b < 8; b++) { if ((4 * b + 3) <= 16) { v[a][b] = o[a][4 * b + 3] - 128; } } } //--------------FDCY Y- U V---------------------------- char y_fdct[8][8] = {}; fdct(y, y_fdct); char u_fdct[8][8] = {}; fdct(u, u_fdct); char v_fdct[8][8] = {}; fdct(v, v_fdct); //----------------量化 Y U V ----------------------------------- //借用成品jpg圖片量化表,lh0,lh1 char (*lh0)[8]=(char (*)[8])lh00; char (*lh1)[8]=(char (*)[8])lh10; char y_lh[8][8]={}; char u_lh[8][8]={}; char v_lh[8][8]={}; lh(y_fdct,lh0,y_lh); lh(u_fdct,lh1,u_lh); lh(v_fdct,lh1,v_lh); //---------Z排序-------------------------- char y_z[64]={}; char u_z[64]={}; char v_z[64]={}; zz(y_lh,y_z); zz(u_lh,u_z); zz(v_lh,v_z); for (int b = 0; b < 64; b++) { printf("%d ,", v_z[b]); } return 0; }
到此這篇關(guān)于C語言實(shí)現(xiàn)YUV文件轉(zhuǎn)JPEG格式的文章就介紹到這了,更多相關(guān)C語言YUV轉(zhuǎn)JPEG內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
OpenCV實(shí)現(xiàn)車牌字符分割(C++)
這篇文章主要為大家詳細(xì)介紹了OpenCV實(shí)現(xiàn)車牌字符分割,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11C++中for循環(huán)與while循環(huán)的區(qū)別總結(jié)
這篇文章主要給大家介紹了關(guān)于C++中for循環(huán)與while循環(huán)的區(qū)別的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10C語言實(shí)現(xiàn)繪制南丁格爾玫瑰圖的示例代碼
玫瑰圖中有一種不等半徑的統(tǒng)計(jì)圖稱為南丁格爾玫瑰圖,網(wǎng)上很熱門,是一很有藝術(shù)感的漂亮的統(tǒng)計(jì)圖,下面我們就來看看如何使用C語言繪制它吧2024-03-03關(guān)于C++出現(xiàn)Bus error問題的排查與解決
項(xiàng)目代碼中經(jīng)常出現(xiàn)莫名其妙的Bus error問題,并且代碼中增加很多try catch 后依然不能將錯(cuò)誤捕獲,一旦Bus erro出現(xiàn),進(jìn)程直接崩潰掉,所以本文給大家介紹了關(guān)于C++出現(xiàn)Bus error問題的排查與解決,需要的朋友可以參考下2024-01-01