今天遇到一個問題,我的攝像頭採集到的數據是yuyv格式(屬於yuv422),而X264在進行編碼的時候需要標準的YUV(4:2:0)。所以有一個yuv422toyuv420的轉換。在網上找了半天找到的方法拿過來轉換了查看都很花。於是自己看了一下yuv格式的解釋,準備寫一個轉換代碼。以下許多解釋都是按我的理解:
一、yuv
yuv格式通常有兩大類:打包(packed)和平面(planar)格式。前者在碼流裏是yuv挨一起,比如我的yuyv就是 Y0 U0 Y1 V1 Y2 U2 …. 每一個 Y對應一組UV分量。後者存儲y u v分量是分開存儲的,這種方式一般後面帶P, 比如y uv420p就是Y0 Y1 Y2 … U0 U1 U2 … V0 V1 V2 … uv分量的多少根據格式來,yuv420也就是每四個 Y共用一組UV分量。
二、轉換
理解了yuyv即yuv422與yuv420p中分量的排布,就要進行轉換了。網上查到的資料說yuv422->yuv420p時 丟棄偶數行的uv分量。
三、編碼
定義:
unsignedchar *y = out;
unsignedchar *u = out + width*height;
unsigned char*v = out + width*height + width*height/4;
y u v分別指向yuv420buf中存儲y u v分量的數組,這裏out的類型爲char型數組,按yuv420p的定義,4個y共用一對uv,那麼一個y對應1/4個uv,一個分量佔一個byte,out的大小爲:總共的y分量(width*height) + 總共的u分量(width*height/4) + 總共的v分量(width*height/4) = width*height*3/2。通過上面的轉換也可以得到yuyv(yuv422)一個像素佔用2個字節,yuv420p一個像素佔1.5個字節,rgb24的話佔用3個字節,還是節約了一點點空間的。。。
獲取y分量並存儲到yuv420buf中:
for(i=0; i<yuv422_length; i+=2){
*(y+y_index) = *(in+i);
y_index++;
}
這裏的yuv422_length爲width*height*2;y_index初始爲0,存儲一個y就自加一次。
獲取uv分量並存儲到yuv420buf中:
for(i=0; i<height; i+=2){
base_h = i*width*2;
for(j=base_h+1;j<base_h+width*2; j+=2){
if(is_u){
*(u+u_index)= *(in+j);
u_index++;
is_u = 0;
}
else{
*(v+v_index)= *(in+j);
v_index++;
is_u = 1;
}
}
}
總結:初入視頻圖像,我還是一個菜鳥,對於很多理解也不深,這個代碼應該還有很多沒考慮,對於我可用了。當然以上都是廢話,直接貼代碼
int yuv422toyuv420(unsigned char *out, const unsigned char *in, unsigned int width, unsigned int height)
{
unsigned char *y = out;
unsigned char *u = out + width*height;
unsigned char *v = out + width*height + width*height/4;
unsigned int i,j;
unsigned int base_h;
unsigned int is_y = 1, is_u = 1;
unsigned int y_index = 0, u_index = 0, v_index = 0;
unsigned long yuv422_length = 2 * width * height;
//序列爲YU YV YU YV,一個yuv422幀的長度 width * height * 2 個字節
//丟棄偶數行 u v
for(i=0; i<yuv422_length; i+=2){
*(y+y_index) = *(in+i);
y_index++;
}
for(i=0; i<height; i+=2){
base_h = i*width*2;
for(j=base_h+1; j<base_h+width*2; j+=2){
if(is_u){
*(u+u_index) = *(in+j);
u_index++;
is_u = 0;
}
else{
*(v+v_index) = *(in+j);
v_index++;
is_u = 1;
}
}
}
return 1;
}