YUV,是一種顏色編碼方法。常使用在各個視頻處理組件中。 YUV在對照片或視頻編碼時,考慮到人類的感知能力,容許下降色度的帶寬。 git
YUV是編碼true-color時使用的顏色空間(color space)之一. 像Y'UV, YUV, YCbCr, YPbPr等均可以稱爲YUV, 彼此之間有重疊。github
YUV和Y'UV: 一般用來編碼電視的模擬信號 (Y'表示伽瑪校訂)
YCbCr: 用來描述數字的視頻信號,適合視頻與圖片壓縮以及傳輸,例如MPEG、JPEG編程
YUV Formats分紅兩個格式:ide
緊湊格式對於YUV4:4:4比較適合,而平面格式適用於採樣,它有I420(4:2:0), YV12, IYUV等。svg
Y'UV的發明是因爲彩色電視與黑白電視的過渡時期[1]。黑白視頻只有Y(Luma,Luminance)視頻,也就是灰階值。到了彩色電視規格的制定,是以YUV/YIQ的格式來處理彩色電視圖像,把UV視做表示彩度的C(Chrominance或Chroma),若是忽略C信號,那麼剩下的Y(Luma)信號就跟以前的黑白電視頻號相同,這樣一來便解決彩色電視機與黑白電視機的兼容問題。Y'UV最大的優勢在於只需佔用極少的帶寬。測試
由於UV分別表明不一樣顏色信號,因此直接使用R與B信號表示色度的UV。 也就是說UV信號告訴了電視要偏移某象素的的顏色,而不改變其亮度。 或者UV信號告訴了顯示器使得某個顏色亮度依某個基準偏移。 UV的值越高,表明該像素會有更飽和的顏色。ui
彩色圖像記錄的格式,常見的有RGB、YUV、CMYK等。彩色電視最先的構想是使用RGB三原色來同時傳輸。這種設計方式是原來黑白帶寬的3倍,在當時並非很好的設計。RGB訴求於人眼對色彩的感應,YUV則着重於視覺對於亮度的敏感程度,Y表明的是亮度,UV表明的是彩度(所以黑白電影可省略UV,相近於RGB),分別用Cr和Cb來表示,所以YUV的記錄一般以Y:UV的格式呈現。編碼
爲節省帶寬起見,大多數YUV格式平均使用的每像素位數都少於24位。主要的抽樣(subsample)格式有YCbCr 4:2:0、YCbCr 4:2:二、YCbCr 4:1:1和YCbCr 4:4:4。YUV的表示法稱爲A:B:C表示法:spa
最經常使用Y:UV記錄的比重一般1:1或2:1,DVD-Video是以YUV 4:2:0的方式記錄,也就是咱們俗稱的I420,YUV4:2:0並非說只有U(即Cb), V(即Cr)必定爲0,而是指U:V互相援引,時見時隱,也就是說對於每個行,只有一個U或者V份量,若是一行是4:2:0的話,下一行就是4:0:2,再下一行是4:2:0...以此類推。至於其餘常見的YUV格式有YUY二、YUYV、YVYU、UYVY、AYUV、Y41P、Y4十一、Y2十一、IF0九、IYUV、YV十二、YVU九、YUV4十一、YUV420等。設計
YUY2(和YUYV)格式爲像素保留Y,而UV在水平空間上相隔二個像素採樣一次(Y0 U0 Y1 V0),(Y2 U2 Y3 V2)…其中,(Y0 U0 Y1 V0)就是一個macro-pixel(宏像素),它表示了2個像素,(Y2 U2 Y3 V2)是另外的2個像素。 以此類推,再如:Y41P(和Y411)格式爲每一個像素保留Y份量,而UV份量在水平方向上每4個像素採樣一次。一個宏像素爲12個字節,實際表示8個像素。圖像數據中YUV份量排列順序以下:(U0 Y0 V0 Y1 U4 Y2 V4 Y3 Y4 Y5 Y6 Y7)…
YVYU, UYVY格式跟YUY2相似,只是排列順序有所不一樣。Y211格式是Y每2個像素採樣一次,而UV每4個像素採樣一次。AYUV格式則有一Alpha通道。
YV12格式與IYUV相似,每一個像素都提取Y,在UV提取時,將圖像2 x 2的矩陣,每一個矩陣提取一個U和一個V。YV12格式和I420格式的不一樣處在V平面和U平面的位置不一樣。在YV12格式中,V平面緊跟在Y平面以後,而後纔是U平面(即:YVU);但I420則是相反(即:YUV)。NV12與YV12相似,效果同樣,YV12中U和V是連續排列的,而在NV12中,U和V就交錯排列的。
排列舉例: 2*2圖像YYYYVU; 4*4圖像YYYYYYYYYYYYYYYYVVVVUUUU
$$ Y = 0.299 \times R + 0.587 \times G + 0.114 \times B \\\ U = -0.169 \times R - 0.331 \times G + 0.5 \times B + 128 \\\ V = 0.5 \times R - 0.419 \times G - 0.081 \times B + 128 $$
YUV的取值範圍:
$$ Y \in [0,255] \\\ U \in [0,255] \\\ V \in [0,255] $$
反過來,從YUV獲得RGB,公式以下
$$ \begin{align} &R = Y + 1.13983 \times (V-128) \\\ &G = Y - 0.39465 \times (U-128) - 0.58060 \times (V-128) \\\ &B = Y + 2.03211 \times (U-128) \end{align} $$
用矩陣表示法,表示以下:
$$ \begin{bmatrix} Y \\ U \\ V \end{bmatrix}=\begin{bmatrix} 0.299&0.587&0.114 \\ -0.169&-0.331&0.5 \\ 0.5&-0.419&-0.081 \end{bmatrix}\begin{bmatrix} R \\ G \\ B \end{bmatrix}+\begin{bmatrix} 0 \\ 128 \\ 128 \end{bmatrix} $$
$$ \begin{bmatrix} R \\ G \\ B \end{bmatrix}=\begin{bmatrix} 1&-0.00093&1.401687 \\ 1&-0.3437&-0.71417 \\ 1&1.77216&0.00099 \end{bmatrix}\begin{bmatrix} Y \\ U-128 \\ V-128 \end{bmatrix} $$
SDTV(standard-definition television) with BT.601定義公式以下:
$$ \begin{bmatrix} Y' \\ U \\ V \end{bmatrix}=\begin{bmatrix} 0.299&0.587&0.114 \\ -0.14713&-0.28886&0.436 \\ 0.615&-0.51499&-0.10001 \end{bmatrix}\begin{bmatrix} R \\ G \\ B \end{bmatrix} $$
$$ \begin{bmatrix} R \\ G \\ B \end{bmatrix}=\begin{bmatrix} 1&0&1.13983 \\ 1&-0.39465&-0.58060 \\ 1&2.03211&0 \end{bmatrix}\begin{bmatrix} Y' \\ U \\ V \end{bmatrix} $$
HDTV with BT.709定義公式以下:
$$ \begin{bmatrix} Y' \\ U \\ V \end{bmatrix}=\begin{bmatrix} 0.2126&0.7152&0.0722 \\ -0.09991&-0.33609&0.436 \\ 0.615&-0.55861&-0.05639 \end{bmatrix}\begin{bmatrix} R \\ G \\ B \end{bmatrix} $$
$$ \begin{bmatrix} R \\ G \\ B \end{bmatrix}=\begin{bmatrix} 1&0&1.28033 \\ 1&-0.21482&-0.38059 \\ 1&2.12798&0 \end{bmatrix}\begin{bmatrix} Y' \\ U \\ V \end{bmatrix} $$
$ Y' \in [16,235]$
$ U/V \in [16,240]$
step 1
$$ \begin{bmatrix} Y' \\ U \\ V \end{bmatrix}=\begin{bmatrix} 66&129&25 \\ -38&-74&112 \\ 112&-94&-18 \end{bmatrix}\begin{bmatrix} R \\ G \\ B \end{bmatrix} $$
step 2
$$ Yt' = (Y' + 128) >> 8 \\\ Ut = (U + 128) >> 8 \\\ Vt = (V + 128) >> 8 $$
step 3
$$ Yu' = Yt' + 16 \\\ Uu = Ut + 128 \\\ Vu = Vt + 128 $$
$Y'/U/V \in [0,255]$
step 1
$$ \begin{bmatrix} Y' \\ U \\ V \end{bmatrix}=\begin{bmatrix} 77&150&29 \\ -43&-84&127 \\ 127&-106&-21 \end{bmatrix}\begin{bmatrix} R \\ G \\ B \end{bmatrix} $$
step 2
$$ Yt' = (Y' + 128) >> 8 \\\ Ut = (U + 128) >> 8 \\\ Vt = (V + 128) >> 8 $$
step 3
$$ Yu' = Yt' + 16 \\\ Uu = Ut + 128 \\\ Vu = Vt + 128 $$
$$ Y' = 0.299 \times R + 0.587 \times G + 0.114 \times B \\\ U = -0.147 \times R - 0.289 \times G + 0.436 \times B \\\ V = 0.615 \times R - 0.515 \times G - 0.100 \times B $$
轉成定點:
$$ Y' = ((66 \times R + 129 \times G + 25 \times B +128) >> 8) + 16 \\\ U = ((-38 \times R - 74 \times G + 112 \times B +128) >> 8) + 128 \\\ V = ((112 \times R - 94 \times G - 18 \times B + 128) >> 8 ) +128 $$
定點方法:
clmap 表示限定值在[0,255]之間
$$ C = Y' - 16 \\\ D = U - 128 \\\ E = V - 128 \\\ R = clamp( (298 \times C + 408 \times E + 128) >> 8 ) \\\ G = clamp( (298 \times C - 100 \times D - 208 \times E +128) >> 8 ) \\\ B = clamp( (298 \times C + 516 \times D +128) >> 8 ) $$
Y'UV422在內存中的存儲方式以下:
因此讀取4bytes, 輸出6bytes(2 pixels)
y0 = yuv[0]; u = yuv[1]; y1 = yuv[2]; v = yuv[3]; rgb0 = Y'UV444toRGB888(y0, u, v); rgb1 = Y'UV444toRGB888(y1, u, v);
Y'UV420p的採樣方式以下:
獲取座標爲(x,y)像素點的y,u,v方法以下:
total_pixel = width * height; y = yuv[y*width + x]; u = yuv[(y/2) * (width/2) + (x/2) + total_pixel] v = yuv[(y/2) * (widith/2) + (x/2) + total_pixel + (total_pixel/4)] rgb = Y'uv444toRGB(y,u,v)
Y'V12 和Y'UV420p類似,只是UV數據反轉, Y'後是V,而後是U。
// // Created by : Harris Zhu // Filename : rgb2I420.cpp // Avthor : Harris Zhu // Created On : 2018-09-17 04:33:02 // Last Modified : 2018-09-17 04:33:02 // Update Count : 1 // Tags : // Description : // Conclusion : // //======================================================================= #include <stdint.h> #include <stddef.h> void Rgb2Yuv420p(uint8_t *destination, uint8_t *rgb, size_t width, size_t height) { size_t image_size = width * height; size_t upos = image_size; size_t vpos = upos + upos / 4; size_t i = 0; for( size_t line = 0; line < height; ++line ) { if( !(line % 2) ) { for( size_t x = 0; x < width; x += 2 ) { uint8_t r = rgb[3 * i]; uint8_t g = rgb[3 * i + 1]; uint8_t b = rgb[3 * i + 2]; uint8_t yt = ((66*r + 129*g + 25*b + 128) >> 8) + 16; uint8_t ut = (((-38*r) + (-74*g) + 112*b + 128) >> 8) + 128; uint8_t vt = ((112*r + (-94*g) + (-18*b) + 128) >> 8) + 128; destination[i++] = yt; destination[upos++] = ut; destination[vpos++] = vt; r = rgb[3 * i]; g = rgb[3 * i + 1]; b = rgb[3 * i + 2]; yt = ((66*r + 129*g + 25*b + 128) >> 8) + 16; destination[i++] = yt; } } else { for( size_t x = 0; x < width; x += 1 ) { uint8_t r = rgb[3 * i]; uint8_t g = rgb[3 * i + 1]; uint8_t b = rgb[3 * i + 2]; uint8_t yt = ((66*r + 129*g + 25*b + 128) >> 8) + 16; destination[i++] = yt; } } } }
測試文件:
// // Created by : Harris Zhu // Filename : test.cpp // Author : Harris Zhu // Created On : 2018-09-17 04:40:06 // Last Modified : 2018-09-17 04:40:06 // Update Count : 1 // Tags : // Description : // Conclusion : // //======================================================================= #include <stdio.h> #include <stdlib.h> #include "rgb2i420.h" #include "i4202rgb.h" int main(int argc, char**argv) { char * din = argv[1]; char * dout = argv[2]; int width = atoi(argv[3]); int height = atoi(argv[4]); size_t imagesize=width*height; uint8_t bufin[imagesize*3]; uint8_t bufout[size_t(imagesize*1.5)]; size_t nread; FILE * fin=fopen(din, "r"); FILE * fout=fopen(dout, "w+"); if(fin) { while((nread = fread(bufin, 1, sizeof(bufin), fin)) > 0) { Rgb2Yuv420p(bufout, bufin, width, height); fwrite(bufout, 1, sizeof(bufout), fout); } } fclose(fin); fclose(fout); return 0; }
makefile文件:
GENFILE = yuv.in b build: torgb toyuv torgb: g++ -o torgb torgb.cpp -I./ -g toyuv: g++ -o toyuv toyuv.cpp -I./ -g g1 gen1: ./toyuv rgb.in yuv.out 60 40 g2 gen2: ./torgb yuv.in rgb.out 720 480 p1 play1: cat yuv.out| ffplay -i pipe:0 -f rawvideo -pix_fmt yuv420p -video_size 60x40 p2 play2: cat rgb.out | ffplay -i pipe:0 -f rawvideo -pix_fmt rgb24 -video_size 720x480 .PHONY: torgb toyuv
上面的makefile包含了下面yuv2rgb的部分
// // Created by : Harris Zhu // Filename : rgb2I420.cpp // Avthor : Harris Zhu // Created On : 2018-09-17 04:33:02 // Last Modified : 2018-09-17 04:33:02 // Update Count : 1 // Tags : // Description : // Conclusion : // //======================================================================= #include <stdint.h> #include <stddef.h> void Yuv420p2Rgb888(uint8_t *destination, uint8_t *yuv, size_t width, size_t height) { size_t image_size = width * height; size_t upos = image_size; size_t vpos = upos + upos / 4; size_t i = 0; for( size_t line = 0; line < height; ++line ) { for( size_t col = 0; col < width; col += 1 ) { uint8_t y = yuv[line*width+col]; uint8_t u = yuv[(line/2)*(width/2)+(col/2)+image_size]; uint8_t v = yuv[(line/2)*(width/2)+(col/2)+image_size+(image_size/4)]; int16_t C = y-16; int16_t D = u-128; int16_t E = v-128; int16_t rt = (int16_t)((298*C+408*E+128)>>8); int16_t gt = (int16_t)((298*C-100*D-208*E+128)>>8); int16_t bt = (int16_t)((298*C+516*D+128)>>8); destination[i++] = rt>255?255:rt<0?0:rt; destination[i++] = gt>255?255:gt<0?0:gt; destination[i++] = bt>255?255:bt<0?0:bt; } } }
測試用例:
// // Created by : Harris Zhu // Filename : test.cpp // Author : Harris Zhu // Created On : 2018-09-17 04:40:06 // Last Modified : 2018-09-17 04:40:06 // Update Count : 1 // Tags : // Description : // Conclusion : // //======================================================================= #include <stdio.h> #include <stdlib.h> #include "rgb2i420.h" #include "i4202rgb.h" int main(int argc, char**argv) { char * din = argv[1]; char * dout = argv[2]; int width = atoi(argv[3]); int height = atoi(argv[4]); size_t imagesize=width*height; uint8_t bufin[(size_t)(imagesize*1.5)]; uint8_t bufout[imagesize*3]; size_t nread; FILE * fin=fopen("yuv.in", "r"); FILE * fout=fopen(dout, "w+"); if(fin) { while((nread = fread(bufin, 1, sizeof(bufin), fin)) > 0) { Yuv420p2Rgb888(bufout, bufin, width, height); fwrite(bufout, 1, sizeof(bufout), fout); fflush(fout); } } fclose(fin); fclose(fout); return 0; }
注:以上代碼均可以在這裏找到, 包括yuv和rgb文件
YUV相比RGB的優勢是和黑白兼容,並且它的變種容易壓縮帶寬,它被普遍用於目前各類圖像和視頻編碼中。掌握它是開始圖像和視頻編程的基礎
注:本文大部份內容是來自YUV的wiki, 你們也能夠自行查看原文。