兩年多以前,寫了一個生成UYVY格式的函數。記得那時我們部門4、5個人在「小黑屋」裏開發新平臺,我要在「踢啊」某個晶元上實現屏幕的顯示,其格式是UYVY,由於無實際的圖像,於是就動手自己寫了一個。雖然我們大費周折實現視頻的顯示、菜單功能,但可惜未使用。

YUV444格式沒有進行壓縮,佔用空間為with*height*3,與RGB佔用空間相同。在轉換上也很方便,但很多編碼器似乎不太支持該格式。或許和其佔用空間有莫大的關聯吧。

YUYV、YVYU、UYVY、VYUY,都是YUV422的打包格式——即在內存中,Y、U、V都是挨著排序的。它們的名稱就表示了Y、U、V的順序。像YUYV,就是Y、U、Y、V、Y、U、Y、V。在填充這些格式時,就顯得很容易、簡單了。

另外一種常見的格式是YUV420。從本文章開始,會集中寫一下關於YUV的格式轉換的文字,但又沒有研究很多,像採樣的具體過程也沒有很瞭解。這裡放一張上,可以直觀瞭解YUV422、YUV420的樣子。

回到主題上,本文代碼是從兩年前寫的函數基本上修改得來的。填充的內容是不同的顏色條。先上代碼,如下:

void init_yuv_buf(YUV_TYPE type, unsigned char* buf, int width, int height){ unsigned char *src = buf; int i, j; /* unsigned int rainbow_rgb[] = { 0xFF0000, 0xFF6100, 0xFFFF00, 0x00FF00, 0x00FFFF, 0x0000FF, 0xA020F0, 0x000000, 0xFFFFFF, 0xF4A460}; */ // 由上數組轉換而成 unsigned int rainbow_yuv[] = { 0x4c54ff, 0x8534d6, 0xe10094, 0x952b15, 0xb2ab00, 0x1dff6b, 0x5dd2af, 0xbb1654, 0x9c4bc5, 0xb450ad}; unsigned char *p_y = src; unsigned char *p_u = src+width*height; unsigned char *p_v = src+2*width*height; int slice = height / 10; for (i = 0; i < height; i++) // h { int index = i / slice; unsigned char y = (rainbow_yuv[index] & 0xff0000 ) >> 16; unsigned char u = (rainbow_yuv[index] & 0x00ff00) >> 8; unsigned char v = (rainbow_yuv[index] & 0x0000ff); if (type == FMT_YUV444) { for (j=0;j<width;j++) // w { *p_y++ = y; *p_u++ = u; *p_v++ = v; } } else { for (j=0; j<width*2; j+=4) // w { if (type == FMT_YUYV) { src[i*width*2+j+0] = y; // Y0 src[i*width*2+j+1] = u; // U src[i*width*2+j+2] = y; // Y1 src[i*width*2+j+3] = v; // V } if (type == FMT_YVYU) { src[i*width*2+j+0] = y; // Y0 src[i*width*2+j+1] = v; // V src[i*width*2+j+2] = y; // Y1 src[i*width*2+j+3] = u; // U } else if (type == FMT_UYVY) { src[i*width*2+j+0] = u; // U src[i*width*2+j+1] = y; // Y0 src[i*width*2+j+2] = v; // V src[i*width*2+j+3] = y; // Y1 } else if (type == FMT_VYUY) { src[i*width*2+j+0] = v; // V src[i*width*2+j+1] = y; // Y0 src[i*width*2+j+2] = u; // U src[i*width*2+j+3] = y; // Y1 } } } }}

其中rainbow_yuv是由rainbow_rgb數組值轉換而成的。因為我確定顏色是使用RGB空間的。使用的轉換函數如下:

int rgb2YCbCr(unsigned int rgbColor, int* Y, int* Cb, int* Cr){ unsigned char r, g, b; int y, cb, cr; r = (rgbColor&0x00ff0000) >> 16; g = (rgbColor&0x0000ff00) >> 8; b = rgbColor & 0xff; y = (int)( 0.299 * r + 0.587 * g + 0.114 * b); cb = (int)(-0.16874 * r - 0.33126 * g + 0.50000 * b + 128); if (cb < 0) cb = 0; cr = (int)( 0.50000 * r - 0.41869 * g - 0.08131 * b + 128); if (cr < 0) cr = 0; *Y = y; *Cb = cb; *Cr = cr; return 0;}

對於YUV444而言,Y、U、V佔用的空間均為width*height,故在填充時,就分別指定3個分量的指針,然後依次填充即可。

對於YUYV、YVYU、UYVY、VYUY這四種格式,實際上是YUV422採集空間,2個Y對應1個U和1個U。Y、U、V3個分量連續存儲,根據順序,就得到4個格式了。從代碼看到,就是根據不同的格式宏定義來調整3個分量。

總之,本文代碼沒什麼技術含量,但對於我的學習,還是有幫助的,姑且寫之,姑且用之。

李遲 2015.8.5 晚上

推薦閱讀:

相關文章