YUV和RGB格式分析
寫的比較詳細。
正文:
做嵌入式專案的時候,涉及到YUV視訊格式到RGB影象的轉換,雖然之前有接觸到RGB到都是基於opencv的處理,很多東西並不需要我們過多深入的去探討,現在需要完全拋棄現有的演算法程式,需要從記憶體中一個位元組一個位元組的處理,這就涉及到各個視訊格式和圖片格式是如何儲存的。看了網上的很多資料,一下資料幫助蠻大。
YUV資料整理:
一 YUV
做視訊採集與處理,自然少不了要學會分析YUV資料。因為從採集的角度來說,一般的視訊採集晶片輸出的碼流一般都是YUV資料流的形式,而從視訊處理(例如H.264、MPEG視訊編解碼)的角度來說,也是在原始YUV碼流進行編碼和解析,所以,瞭解如何分析YUV資料流對於做視訊領域的人而言,至關重要。YUV是指亮度參量和色度參量分開表示的畫素格式,而這樣分開的好處就是不但可以避免相互干擾,還可以降低色度的取樣率而不會對影象質量影響太大。
人眼對色度的敏感程度要低於對亮度的敏感程度。
YUV,分為三個分量,“Y”表示明亮度(Luminance或Luma),也就是灰度值;而“U”和“V” 表示的則是色度(Chrominance或Chroma),作用是描述影像色彩及飽和度,用於指定畫素的顏色。與我們熟知的RGB類似,YUV也是一種顏色編碼方法,主要用於電視系統以及模擬視訊領域,它將亮度資訊(Y)與色彩資訊(UV)分離,沒有UV資訊一樣可以顯示完整的影象,只不過是黑白的,這樣的設計很好地解決了彩色電視機與黑白電視的相容問題。並且,YUV不像RGB那樣要求三個獨立的視訊訊號同時傳輸,所以用YUV方式傳送佔用極少的頻寬。
YUV碼流有多種不同的格式,要分析YUV碼流,就必須搞清楚你面對的到底是哪一種格式,並且必須搞清楚這種格式的YUV取樣和分佈情況。
YUV格式有兩大類:planar和packed。
對於packed的YUV格式,每個畫素點的Y,U,V是連續交叉儲存的。
1. 取樣方式
YUV碼流的儲存格式其實與其取樣的方式密切相關,主流的取樣方式有三種,YUV4:4:4,YUV4:2:2,YUV4:2:0,如何根據其取樣格式來從碼流中還原每個畫素點的YUV值,因為只有正確地還原了每個畫素點的YUV值,才能通過YUV與RGB的轉換公式提取出每個畫素點的RGB值,然後顯示出來。
用三個圖來直觀地表示採集的方式吧,以黑點表示取樣該畫素點的Y分量,以空心圓圈表示採用該畫素點的UV分量。
先記住下面這段話,以後提取每個畫素的YUV分量會用到。
YUV 4:4:4取樣,每一個Y對應一組UV分量,每畫素32位
YUV 4:2:2取樣,每兩個Y共用一組UV分量,每畫素16位
YUV 4:2:0取樣,每四個Y共用一組UV分量,每畫素16位
平常所講的YUV A:B:C的意思一般是指基於4個象素來講,其中Y取樣了A次,U取樣了B次,V取樣了C次.
YUV 格式可以分為打包格式packed format和平面格式planar format。打包格式將YUV分量存放在同一個陣列中,通常是幾個相鄰的畫素組成一個巨集畫素(macro-pixel);而平面格使用三個陣列分開存放YUV三個分量,就像是一個三維平面一樣。Packed format和planner format的區別在於,packed format中的YUV是混合在一起的,因此就有了UYVY、YUYV等等,他們在碼流中排列的方式有所不同。而對於planner format每一個Y分量,U分量和V分量都是以獨立的平面組織的,也就是說所有的U分量都在Y分量之後出現,而V分量在所有的U分量之後。就像三個大色塊一樣。
2. 儲存方式
下面用圖的形式給出常見的YUV碼流的儲存方式,並在儲存方式後面附有取樣每個畫素點的YUV資料的方法,其中,Cb、Cr的含義等同於U、V。因為我們在實驗中芷使用到YUV422的格式,這裡只介紹這個,其他的可以去其他博文找。
(1) YUYV 格式 (屬於YUV422)
YUYV(YUY2)為YUV422取樣的儲存格式中的一種,相鄰的兩個Y共用其相鄰的兩個Cb(U)、Cr(V),分析,對於畫素點Y’00、Y’01 而言,其Cb、Cr的值均為 Cb00、Cr00,其他的畫素點的YUV取值依次類推。YVYU(YVY2)也一樣,只是UV的位置調換了一下,先V後U。
(2) UYVY 格式 (屬於YUV422)
UYVY格式也是YUV422取樣的儲存格式中的一種,只不過與YUYV不同的是UV的排列順序不一樣而已,還原其每個畫素點的YUV值的方法與上面一樣。
(3) YUV422P(屬於YUV422)
YUV422P也屬於YUV422的一種,它是一種Plane模式,即打包模式,並不是將YUV資料交錯儲存,而是先存放所有的Y分量,然後儲存所有的U(Cb)分量,最後儲存所有的V(Cr)分量,如上圖所示。其每一個畫素點的YUV值提取方法也是遵循YUV422格式的最基本提取方法,即兩個Y共用一個UV。比如,對於畫素點Y’00、Y’01 而言,其Cb、Cr的值均為 Cb00、Cr00。
以YUV420 planar資料為例, 以720×480大小圖象YUV420 planar為例,
其儲存格式是: 共大小為(720×480×3>>1)位元組,
分為三個部分:Y,U和V
Y分量: (720×480)個位元組
U(Cb)分量:(720×480>>2)個位元組
V(Cr)分量:(720×480>>2)個位元組
三個部分內部均是行優先儲存,三個部分之間是Y,U,V 順序儲存。
即YUV資料的0--720×480位元組是Y分量值,
720×480--720×480×5/4位元組是U分量
720×480×5/4 --720×480×3/2位元組是V分量。
這裡Y分量其實就是我們常說的灰度值,所以需要對圖片進行灰度處理的話,可以直接提取出圖片的Y分量。這點對我們後面的處理很重要。
YUV4:4:4
下面的四個畫素為: [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
存放的碼流為: Y0 U0 V0 Y1 U1 V1 Y2 U2 V2 Y3 U3 V3
映射出畫素點保持原樣
YUV4:2:2
每個色差通道的抽樣率是亮度通道的一半,所以水平方向的色度抽樣率只是4:4:4的一半。對非壓縮的8位元量化的影象來說,每個由兩個水平方向相鄰的畫素組成的巨集畫素需要佔用4位元組記憶體。
下面的四個畫素為:[Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
存放的碼流為:Y0 U0 Y1 V1 Y2 U2 Y3 V3
映射出畫素點為:[Y0 U0 V1] [Y1 U0 V1] [Y2 U2 V3] [Y3 U2 V3]
YUV 4:1:1
4:1:1的色度抽樣,是在水平方向上對色度進行4:1抽樣。對於低端使用者和消費類產品這仍然是可以接受的。對非壓縮的8位元量化的視訊來說,每個由4個水平方向相鄰的畫素組成的巨集畫素需要佔用6位元組記憶體。
原來四個畫素為: [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
存放的碼流為: Y0 U0 Y1 Y2 V2 Y3
還原出畫素點為:[Y0 U0 V2] [Y1 U0 V2] [Y2 U0 V2] [Y3 U0 V2]
用6個YUV分量描述了原來的12個YUV分量,因此壓縮比為1/2,平均來講,就是用了12bit表示了一個象素點,原來YUV(8bit*3)是24bit。
YUV4:2:0
4:2:0並不意味著只有Y,Cb而沒有Cr分量。它指得是對每行掃描線來說,只有一種色度分量以2:1的抽樣率儲存。相鄰的掃描行儲存不同的色度分 量,也就是說,如果一行是4:2:0的話,下一行就是4:0:2,再下一行是4:2:0…以此類推。對每個色度分量來說,水平方向和豎直方向的抽樣率 都是2:1,所以可以說色度的抽樣率是4:1。對非壓縮的8位元量化的視訊來說,每個由2x2個2行2列相鄰的畫素組成的巨集畫素需要佔用6位元組記憶體。
下面八個畫素為:[Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3] [Y5 U5 V5] [Y6 U6 V6] [Y7U7 V7] [Y8 U8 V8]
存放的碼流為:Y0 U0 Y1 Y2 U2 Y3 Y5 V5 Y6 Y7 V7 Y8
映射出的畫素點為:[Y0 U0 V5] [Y1 U0 V5] [Y2 U2 V7] [Y3 U2 V7] [Y5 U0 V5] [Y6 U0 V5] [Y7U2 V7] [Y8 U2 V7]
二 RGB
計算機彩色顯示器顯示色彩的原理與彩色電視機一樣,都是採用R(Red)、G(Green)、B(Blue)相加混色的原理:通過發射出三種不同強度的電子束,使螢幕內側覆蓋的紅、綠、藍磷光材料發光而產生色彩。這種色彩的表示方法稱為RGB色彩空間表示(它也是多媒體計算機技術中用得最 多的一種色彩空間表示方法)。根據色度學的介紹,不同波長的單色光會引起不同的彩色感覺,但相同的彩色感覺卻可以來源於不同的光譜成分組合。自然界中幾乎所有的顏色都能用三種基本彩色混合配出,在彩色電視技術中選擇紅色、綠色、和藍色作為三基色。其他的顏色都可以用紅色、綠色和藍色按照不同的比例混合而成。所選取的紅色、綠色和藍色三基色空間。簡稱為RGB顏色空間。
RGB565 每個畫素用16位表示,RGB分量分別使用5位、6位、5位
RGB555 每個畫素用16位表示,RGB分量都使用5位(剩下1位不用)
RGB24 每個畫素用24位表示,RGB分量各使用8位
RGB32 每個畫素用32位表示,RGB分量各使用8位(剩下8位不用)
ARGB32 每個畫素用32位表示,RGB分量各使用8位(剩下的8位用於表示Alpha通道值)
RGB565(我們使用的格式)
使用16位表示一個畫素,這16位中的5位用於R,6位用於G,5位用於B。
程式中通常使用一個字(WORD,一個字等於兩個位元組)來操作一個畫素。當讀出一個畫素後,這個字的各個位意義如下:
高位元組 低位元組
R R R R R G G G G G G B B B B B
可以組合使用遮蔽字和移位操作來得到RGB各分量的值:
#define RGB565_MASK_RED 0xF800
#define RGB565_MASK_GREEN 0x07E0
#define RGB565_MASK_BLUE 0x001F
R = (wPixel & RGB565_MASK_RED) >> 11; // 取值範圍0-31
G = (wPixel & RGB565_MASK_GREEN) >> 5; // 取值範圍0-63
B = wPixel & RGB565_MASK_BLUE; // 取值範圍0-31
#define RGB(r,g,b) (unsigned int)( (r|0x08 << 11) | (g|0x08 << 6) | b|0x08 )
#define RGB(r,g,b) (unsigned int)( (r|0x08 << 10) | (g|0x08 << 5) | b|0x08 )
該程式碼可以解決24位與16位相互轉換的問題
RGB555
是另一種16位的RGB格式,RGB分量都用5位表示(剩下的1位不用)。
使用一個字讀出一個畫素後,這個字的各個位意義如下:
高位元組 低位元組
X R R R R G G G G G B B B B B (X表示不用,可以忽略)
RGB24使用24位來表示一個畫素,RGB分量都用8位表示,取值範圍為0-255
RGB32使用32位來表示一個畫素,RGB分量各用去8位,剩下的8位不用
RGB24
RGB24使用24位來表示一個畫素,RGB分量都用8位表示,取值範圍為0-255。注意在記憶體中RGB各分量的排列順序為:BGR BGR BGR…。通常可以使用RGBTRIPLE資料結構來操作一個畫素,它的定義為:
typedef struct tagRGBTRIPLE {
BYTE rgbtBlue; // 藍色分量
BYTE rgbtGreen; // 綠色分量
BYTE rgbtRed; // 紅色分量
} RGBTRIPLE;
RGB32
RGB32使用32位來表示一個畫素,RGB分量各用去8位,剩下的8位用作Alpha通道或者不用。(ARGB32就是帶Alpha通道的RGB24。)注意在記憶體中RGB各分量的排列順序為:BGRA BGRA BGRA…。通常可以使用RGBQUAD資料結構來操作一個畫素,它的定義為:
typedef struct tagRGBQUAD {
BYTE rgbBlue; // 藍色分量
BYTE rgbGreen; // 綠色分量
BYTE rgbRed; // 紅色分量
BYTE rgbReserved; // 保留位元組(用作Alpha通道或忽略)
} RGBQUAD。