存在問(wèn)題
移動(dòng)端錄像在yuv數(shù)據(jù)上存在如下問(wèn)題:
- 無(wú)論android還是ios都不能直接從攝像頭取出顏色空間為i420的數(shù)據(jù),所以在編碼前需要進(jìn)行格式轉(zhuǎn)換弥奸。
- 而且由于所取圖像得分辨率必須是攝像頭所提供分辨率中得一組榨惠,所以有可能需要裁剪。
- 另外由于
(1)想讓無(wú)論用戶哪個(gè)方向拿手機(jī)所錄的視頻內(nèi)容永遠(yuǎn)“頭朝上”
( 2)攝像頭默認(rèn)返回圖像為橫屏圖像(寬大于長(zhǎng))
所以需要旋轉(zhuǎn)。 - 前置攝像頭需要鏡像赠橙。
算法實(shí)現(xiàn)
1.格式轉(zhuǎn)換
nv21轉(zhuǎn)成i420耽装。可以通過(guò)攝像頭設(shè)置將所采集數(shù)據(jù)設(shè)置為YUVNV21格式期揪。
void NV21ToI420(uint8_t* dstyuv,uint8_t* data, int imageWidth, int imageHeight) { int Ustart =imageWidth*imageHeight; int i,j; int uWidth = imageWidth/2; int uHeight = imageWidth/2; //y memcpy(dstyuv,data,imageWidth*imageHeight); int tempindex = 0 ; int srcindex= 0; //u for(i= 0 ;i <uHeight;i++) { for(j = 0;j <uWidth ;j++ ) { dstyuv[Ustart+tempindex+j]= data[Ustart+(srcindex<<1)+1]; srcindex++; } tempindex+= uWidth; } //v for (i = 0; i < uHeight;i++) { for (j = 0; j < uWidth;j++) { dstyuv[Ustart+tempindex + j] = data[Ustart + (srcindex << 1 )]; srcindex++; } tempindex+= uWidth; } }
其實(shí)就是改變了uv的位置掉奄。
I420: YYYYYYYY UU VV
NV21: YYYYYYYY VUVU
2.裁剪
//crop yuv data int crop_yuv (char* data, char*dst, intwidth, intheight, int goalwidth, int goalheight) { int i, j; int h_div = 0, w_div = 0; w_div= (width - goalwidth) / 2; if (w_div % 2) w_div--; h_div= (height - goalheight) / 2; if (h_div % 2) h_div--; //u_div = (height-goalheight)/4; int src_y_length = width *height; int dst_y_length =goalwidth * goalheight; for (i = 0; i <goalheight; i++) for (j = 0; j <goalwidth; j++) { dst[i* goalwidth + j] = data[(i + h_div) * width + j + w_div]; } int index = dst_y_length; int src_begin =src_y_length + h_div * width / 4; int src_u_length =src_y_length / 4; int dst_u_length =dst_y_length / 4; for (i = 0; i <goalheight / 2; i++) for (j = 0; j <goalwidth / 2; j++) { int p = src_begin + i *(width >> 1) + (w_div >> 1) + j; dst[index]= data[p]; dst[dst_u_length+ index++] = data[p + src_u_length]; } return 0; }
3.** 旋轉(zhuǎn)**
分為四個(gè)方向 。以順時(shí)針270度為例作圖横侦。
旋轉(zhuǎn)前:
旋轉(zhuǎn)后:
u值的第i 行j列 對(duì)應(yīng)原 數(shù)據(jù)的下標(biāo)為:ustart+uw*j-i;
去除index的乘除法運(yùn)算后算法:
(1) i420 順時(shí)針 270度
int rotateYUV420Degree270(uint8_t* dstyuv,uint8_t* srcdata, int imageWidth, int imageHeight) { int i = 0, j = 0; int index = 0; int tempindex = 0; int div = 0; for (i = 0; i <imageHeight; i++) { div= i +1; tempindex= 0; for (j = 0; j <imageWidth; j++) { tempindex+= imageWidth; dstyuv[index++]= srcdata[tempindex-div]; } } int start =imageWidth*imageHeight; int udiv = imageWidth *imageHeight / 4; int uWidth = imageWidth /2; int uHeight = imageHeight /2; index= start; for (i = 0; i < uHeight;i++) { div= i +1; tempindex= start; for (j = 0; j < uWidth;j++) { tempindex += uWidth; dstyuv[index]= srcdata[tempindex-div]; dstyuv[index+udiv]= srcdata[tempindex-div+udiv]; index++; } } return 0; }
(2)i420 順時(shí)針旋轉(zhuǎn) 180
int rotateYUV420Degree180(uint8_t* dstyuv,uint8_t* srcdata, int imageWidth, int imageHeight) { int i = 0, j = 0; int index = 0; int tempindex = 0; int ustart = imageWidth \*imageHeight; tempindex= ustart; for (i = 0; i <imageHeight; i++) { tempindex-= imageWidth; for (j = 0; j <imageWidth; j++) { dstyuv[index++] = srcdata[tempindex + j]; } } int udiv = imageWidth *imageHeight / 4; int uWidth = imageWidth /2; int uHeight = imageHeight /2; index= ustart; tempindex= ustart+udiv; for (i = 0; i < uHeight;i++) { tempindex-= uWidth; for (j = 0; j < uWidth;j++) { dstyuv[index]= srcdata[tempindex + j]; dstyuv[index+ udiv] = srcdata[tempindex + j + udiv]; index++; } } return 0; }
(3)順時(shí)針 90度
int rotateYUV420Degree90(uint8_t* dstyuv,uint8_t* srcdata, int imageWidth, int imageHeight) { int i = 0, j = 0; int index = 0; int tempindex = 0; int div = 0; int ustart = imageWidth *imageHeight; for (i = 0; i <imageHeight; i++) { div= i; tempindex= ustart; for (j = 0; j <imageHeight; j++) { tempindex-= imageWidth; dstyuv[index++]= srcdata[tempindex + div]; } } int udiv = imageWidth *imageHeight / 4; int uWidth = imageWidth /2; int uHeight = imageHeight /2; index= ustart; for (i = 0; i < uHeight;i++) { div= i ; tempindex= ustart+udiv; for (j = 0; j < uWidth;j++) { tempindex-= uWidth; dstyuv[index]= srcdata[tempindex + div]; dstyuv[index+ udiv] = srcdata[tempindex + div + udiv]; index++; } } return 0; }
4.** 鏡像**
//mirro 原址的 void Mirror(uint8_t\* yuv_temp, int nw, int nh, int w, int h) { int deleteW = (nw - h) / 2; int deleteH = (nh - w) / 2; int i, j; int a, b; uint8_ttemp; //mirror y for (i = 0; i < h; i++){ a= i \* w; b= (i + 1) \* w - 1; while (a < b) { temp= yuv_temp[a]; yuv_temp[a]= yuv_temp[b]; yuv_temp[b]= temp; a++; b--; } } //mirror u int uindex = w * h; for (i = 0; i < h / 2;i++) { a = i\ * w / 2; b= (i + 1) \* w / 2 - 1; while (a < b) { temp= yuv_temp[a + uindex]; yuv_temp[a+ uindex] = yuv_temp[b + uindex]; yuv_temp[b+ uindex] = temp; a++; b--; } } //mirror v uindex= w * h / 4 * 5; for (i = 0; i < h / 2;i++) { a= i\* w / 2; b= (i + 1) \* w / 2 - 1; while (a < b) { temp= yuv_temp[a + uindex]; yuv_temp[a+ uindex] = yuv_temp[b + uindex]; yuv_temp[b+ uindex] = temp; a++; b--; } } }
算法優(yōu)化
如果從攝像頭取出數(shù)據(jù)挥萌,這樣一步步的歷遍,在低配手機(jī)上是滿足不了需求的枉侧。其實(shí)這三個(gè)步驟中有很多中間步驟是可以省去的引瀑,比如:將a放到b 位置,再將b位置上的數(shù)據(jù)取出放到c位置榨馁,那么可以直接將a放到c位置憨栽。
所以將旋轉(zhuǎn)、裁剪翼虫、格式轉(zhuǎn)換三個(gè)問(wèn)題所用的算法整合(未整合進(jìn)去鏡像)屑柔。結(jié)果如下:
**(1)處理不用旋轉(zhuǎn)的圖像,格式轉(zhuǎn)換加裁剪
void detailPic0(uint8_t\* d, uint8_t\* yuv_temp, int nw, int nh, int w, int h) { int deleteW = (nw - w) / 2; int deleteH = (nh - h) / 2; //處理y 旋轉(zhuǎn)加裁剪 int i, j; int index = 0; for (j = deleteH; j < nh- deleteH; j++) { for (i = deleteW; i < nw- deleteW; i++) yuv_temp[index++]= d[j \* nw + i]; } //處理u index= w \* h; for (i = nh + deleteH / 2;i < nh / 2 \* 3 - deleteH / 2; i++) for (j = deleteW + 1; j< nw - deleteW; j += 2) yuv_temp[index++]= d[i \* nw + j]; //處理v 旋轉(zhuǎn)裁剪加格式轉(zhuǎn)換 for (i = nh + deleteH / 2;i < nh / 2 \* 3 - deleteH / 2; i++) for (j = deleteW; j < nw- deleteW; j += 2) yuv_temp[index++]= d[i \* nw + j]; }
**(2)格式轉(zhuǎn)換珍剑、裁剪加旋轉(zhuǎn)90度
void detailPic90(uint8_t\* d, uint8_t\* yuv_temp, int nw, int nh, int w, int h) { int deleteW = (nw - h) / 2; int deleteH = (nh - w) / 2; int i, j; for (i = 0; i < h; i++){ for (j = 0; j < w; j++){ yuv_temp[(h- i) \* w - 1 - j] = d[nw \* (deleteH + j) + nw - deleteW -i]; } } int index = w \* h; for (i = deleteW + 1; i< nw - deleteW; i += 2) for (j = nh / 2 \* 3 -deleteH / 2; j > nh + deleteH / 2; j--) yuv_temp[index++]= d[(j - 1) \* nw + i]; for (i = deleteW; i < nw- deleteW; i += 2) for (j = nh / 2 \* 3 -deleteH / 2; j > nh + deleteH / 2; j--) yuv_temp[index++]= d[(j - 1) \* nw + i]; }
(3)格式轉(zhuǎn)換掸宛、裁剪加旋轉(zhuǎn)180度
void detailPic180(uint8_t\* d, uint8_t\* yuv_temp, int nw, int nh, int w, int h) { int deleteW = (nw - w) / 2; int deleteH = (nh - h) / 2; //處理y 旋轉(zhuǎn)加裁剪 int i, j; int index = w \* h; for (j = deleteH; j < nh- deleteH; j++) { for (i = deleteW; i < nw- deleteW; i++) yuv_temp[--index]= d[j \* nw + i]; } //處理u index= w \* h \* 5 / 4; for (i = nh + deleteH / 2;i < nh / 2 \* 3 - deleteH / 2; i++) for (j = deleteW + 1; j< nw - deleteW; j += 2) yuv_temp[--index]= d[i \* nw + j]; //處理v index= w \* h \* 3 / 2; for (i = nh + deleteH / 2;i < nh / 2 \* 3 - deleteH / 2; i++) for (j = deleteW; j < nw- deleteW; j += 2) yuv_temp[--index]= d[i \* nw + j]; }
(4)格式轉(zhuǎn)換、裁剪加旋轉(zhuǎn)270度
void detailPic270(uint8_t\* d, uint8_t\* yuv_temp, int nw, int nh, int w, int h) { int deleteW = (nw - h) / 2; int deleteH = (nh - w) / 2; int i, j; //處理y 旋轉(zhuǎn)加裁剪 for (i = 0; i < h; i++){ for (j = 0; j < w; j++){ yuv_temp[i\* w + j] = d[nw \* (deleteH + j) + nw - deleteW - i]; } } //處理u 旋轉(zhuǎn)裁剪加格式轉(zhuǎn)換 int index = w \* h; for (i = nw - deleteW - 1;i > deleteW; i -= 2) for (j = nh + deleteH / 2;j < nh / 2 \* 3 - deleteH / 2; j++) yuv_temp[index++]= d[(j) \* nw + i]; //處理v 旋轉(zhuǎn)裁剪加格式轉(zhuǎn)換 for (i = nw - deleteW - 2;i >= deleteW; i -= 2) for (j = nh + deleteH / 2;j < nh / 2 \* 3 - deleteH / 2; j++) yuv_temp[index++]= d[(j) \* nw + i]; }
注:以上算法消除index的乘法后效果肯定會(huì)更好招拙。