您好,登錄后才能下訂單哦!
小編給大家分享一下YUV數(shù)據(jù)格式是什么,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!
YUV 數(shù)據(jù)格式概覽
YUV 的原理是把亮度與色度分離,使用 Y、U、V 分別表示亮度,以及藍(lán)色通道與亮度的差值和紅色通道與亮度的差值。其中 Y 信號(hào)分量除了表示亮度 (luma) 信號(hào)外,還含有較多的綠色通道量,單純的 Y 分量可以顯示出完整的黑白圖像。U、V 分量分別表示藍(lán) (blue)、紅 (red) 分量信號(hào),它們只含有色彩 (chrominance/color) 信息,所以 YUV 也稱為 YCbCr,C 意思可以理解為 (component 或者 color)。
維基百科上的 RGB 轉(zhuǎn) YUV 的公式能更好的反應(yīng) YUV 與 RGB 的關(guān)系,以及為什么稱為 YCbCr:
Y 中含有三元色色信息,且有較多的 G,所以他們一起可以顯示出全彩的圖像。
很顯然我們可以想到是不是會(huì)有 YCgCb、YCgCr 等,針對(duì)不同的應(yīng)用場(chǎng)景,也確實(shí)有相關(guān)應(yīng)用研究。
如下圖,一張從上到下分別為原圖、Y、U 和 V:
采用 YUV 而不是使用 RGB,既有歷史原因:為了兼容老式黑白電視,因?yàn)?YUV 如果只輸出 Y 就成了黑白圖像了。也有 YUV 自己的其他優(yōu)點(diǎn),例如可以根據(jù)需要,采用特定的 YUV 存儲(chǔ)格式,以降低祼碼流的空間占用。
YUV 存儲(chǔ)格式
YUV 存儲(chǔ)格式有兩大類:planar 和 packed。
對(duì)于 planar 的 YUV 格式,先連續(xù)存儲(chǔ)所有像素點(diǎn)的 Y,緊接著存儲(chǔ)所有像素點(diǎn)的 U,隨后是所有像素點(diǎn)的 V。相當(dāng)于將 YUV 拆分成三個(gè)平面 (plane) 存儲(chǔ)。
對(duì)于 packed 的 YUV 格式,每個(gè)像素點(diǎn)的 Y,U,V 是連續(xù)交替存儲(chǔ)的。
YUV 碼流又根據(jù)不同的采樣方式分為 YUV4:4:4、YUV4:2:2、YUV4:2:0、YUV4:1:1 等存儲(chǔ)格式,其中前 3 種較常見(jiàn)。所謂采樣意思就是根據(jù)一定的間隔取值。其中的比例是指 Y、U、V 表示的像素,三者分別占的比值??梢园凑杖缦路绞嚼斫?,實(shí)現(xiàn)存儲(chǔ)和掃描與 DVD 的掃描線有關(guān)。
例如:
YUV4:4:4 是指每個(gè)像素分別有一個(gè) Y、一個(gè) U 和一個(gè) V 組成,即每 4 個(gè) Y 采樣,就對(duì)應(yīng) 4 個(gè) Cb 和 4 個(gè) Cr 采樣,也就是一個(gè)像素占用 8+8+8=24 位,這種存儲(chǔ)方式圖像質(zhì)量最高,但空間占用也最大,空間占用與 RGB 存儲(chǔ)時(shí)一樣。對(duì)于一個(gè) M*N分辨率的圖像,該模式下存儲(chǔ)空間占用字節(jié)數(shù)為 M*N*3。
YUV4:2:2 是指每 4 個(gè) Y 采樣,對(duì)應(yīng) 2 個(gè) Cb 和 2 個(gè) Cr 采樣,這樣在解析時(shí)就會(huì)有一些像素點(diǎn)只有亮度信息而沒(méi)有色度信息,缺失的色度信息就需要在解析時(shí)由相鄰的其他色度信息根據(jù)一定的算法填充。這種方式下平均一個(gè)像素占用空間為 8+4+4=16 位。對(duì)于一個(gè) M*N 分辨率的圖像,空間占用 16/24,即 M*N*3*(16/24) = M*n*2 個(gè)字節(jié)。
YUV4:2:0 是指每 4 個(gè) 4 采樣,對(duì)應(yīng) 2 個(gè) U 采樣或者 2 個(gè) V 采樣,注意其中并不是表示 2 個(gè) U 和 0 個(gè) V,而是指無(wú)論水平下采樣還是垂直下采樣,色度采樣都只有亮度的一半。該存儲(chǔ)格式下,平均每個(gè)像素占用空間為 8+4+0=12 位。對(duì)于一個(gè) M*N 分辨率的圖像來(lái)說(shuō),空間占用為原來(lái)的 12/24,即 M*N*3*(12/24)=M*N*3/2。節(jié)省較多存儲(chǔ)空間,該存儲(chǔ)格式也最常用。
YUV4:1:1 是指每 4 個(gè) Y 采樣,對(duì)應(yīng) 1 個(gè) U 采樣和一個(gè) V 采樣。平均每個(gè)像素占用空間為 8+2+2=12 位。圖像空間占用情況同上。這種存儲(chǔ)格式實(shí)際使用的非常少。
對(duì)于 packed 存儲(chǔ)格式,略。
YV12/I420/YU12/NV12/NV21
YV12/I420/YU12/NV12/NV21 都屬于 YUV 4:2:0。YU12 就是 I420,YV12/I420 也稱為 YUV420P(即平面格式,planar),YV12 與標(biāo)準(zhǔn)模式 I420 的區(qū)別是 UV 順序不同。
YV12 取名來(lái)源是 Y 后面緊跟 V(然后是 U),12 表示它位深為 12,也就是一個(gè)像素占用空間為 12 位。
在 I420(YU12) 格式中,U 平面緊跟在 Y 平面之后,然后才是 V 平面(即:YUV);但 YV12 則是相反(即:YVU)。大部分視頻解碼器的輸出的原始圖像都是 I420 格式(例如安卓下的圖像通常都是 I420 或 NV21),而多數(shù)硬解碼器中使用的都是 NV12 格式(例如 Intel MSDK、NVIDIA 的 cuvid、IOS 硬解碼)。
另一類 YUV420SP, Y 分量平面格式,UV 打包格式,即 NV12。 NV12 與 NV21 類似,U 和 V 交錯(cuò)排列,不同在于 UV 順序。
可理解如下:
I420: YYYYYYYY UU VV => YUV420P
YV12: YYYYYYYY VV UU => YUV420P
NV12: YYYYYYYY UVUV => YUV420SP
NV21: YYYYYYYY VUVU => YUV420SP
維基百科上有兩張 I420 和 NV12 的兩張圖非常好:
I420 的單幀結(jié)構(gòu)示意圖如下(Planar 方式):
這幅圖的上面一幅可以看出 Y1、Y2、Y7、Y8 共用 U1 和 V1。后面的線性數(shù)組為其存儲(chǔ)順序,可以看出 Y、U 和 V 都是順序存儲(chǔ)的,往外寫的時(shí)候,先按順序?qū)?Y 分量寫出,然后再根據(jù) U、V 分別將它們依次寫出即可。
NV12 的單幀結(jié)構(gòu)示意圖如下(Planar 方式):
可以看出與 YV12 不同的時(shí),它的 Y 雖然也是順序存儲(chǔ),但 U、V 卻是交錯(cuò)存儲(chǔ)的,這種方式存儲(chǔ)在往外寫出時(shí)則先直接順序?qū)懗?Y,然后對(duì) UV 分別依次寫出。
Python的實(shí)現(xiàn):將420P轉(zhuǎn)為jpg
from PIL import Image def yuv420_to_rgb888(width, height, yuv): # function requires both width and height to be multiples of 4 if (width % 4) or (height % 4): raise Exception("width and height must be multiples of 4") rgb_bytes = bytearray(width*height*3) red_index = 0 green_index = 1 blue_index = 2 y_index = 0 for row in range(0,height): u_index = width * height + (row//2)*(width//2) v_index = u_index + (width*height)//4 for column in range(0,width): Y = yuv[y_index] U = yuv[u_index] V = yuv[v_index] C = (Y - 16) * 298 D = U - 128 E = V - 128 R = (C + 409*E + 128) // 256 G = (C - 100*D - 208*E + 128) // 256 B = (C + 516 * D + 128) // 256 R = 255 if (R > 255) else (0 if (R < 0) else R) G = 255 if (G > 255) else (0 if (G < 0) else G) B = 255 if (B > 255) else (0 if (B < 0) else B) rgb_bytes[red_index] = R rgb_bytes[green_index] = G rgb_bytes[blue_index] = B u_index += (column % 2) v_index += (column % 2) y_index += 1 red_index += 3 green_index += 3 blue_index += 3 return rgb_bytes def testConversion(source, dest): print("opening file") f = open(source, "rb") yuv = f.read() f.close() print("read file") rgb_bytes = yuv420_to_rgb888(4208,3120, yuv) # cProfile.runctx('yuv420_to_rgb888(1920,1088, yuv)', {'yuv420_to_rgb888':yuv420_to_rgb888}, {'yuv':yuv}) print("finished conversion. Creating image object") img = Image.frombytes("RGB", (4208,3120), bytes(rgb_bytes)) print("Image object created. Starting to save") img.save(dest, "JPEG") img.close() print("Save completed") testConversion("C:/adb1031/yuveffectout/MV_F_Cap1.yuv", "C:/adb1031/yuveffectout/MV_F_Cap1.jpg") testConversion("C:/adb1031/yuveffectout/MV_F_Cap2.yuv", "C:/adb1031/yuveffectout/MV_F_Cap2.jpg")
Python的實(shí)現(xiàn):將NV21轉(zhuǎn)為jpg
from PIL import Image def yuv420_to_rgb888(width, height, yuv): # function requires both width and height to be multiples of 4 if (width % 4) or (height % 4): raise Exception("width and height must be multiples of 4") rgb_bytes = bytearray(width*height*3) red_index = 0 green_index = 1 blue_index = 2 y_index = 0 v_index = width * height for row in range(0,height): v_index = width * height + (row//2)*width u_index = v_index + 1 for column in range(0,width): Y = yuv[y_index] #print(y_index) U = yuv[u_index] V = yuv[v_index] C = (Y - 16) * 298 D = U - 128 E = V - 128 R = (C + 409*E + 128) // 256 G = (C - 100*D - 208*E + 128) // 256 B = (C + 516 * D + 128) // 256 R = 255 if (R > 255) else (0 if (R < 0) else R) G = 255 if (G > 255) else (0 if (G < 0) else G) B = 255 if (B > 255) else (0 if (B < 0) else B) rgb_bytes[red_index] = R rgb_bytes[green_index] = G rgb_bytes[blue_index] = B if column==0: v_index = v_index elif column%2==0: v_index = v_index + 2 u_index = v_index + 1 y_index += 1 red_index += 3 green_index += 3 blue_index += 3 return rgb_bytes def testConversion(source, dest): print("opening file") f = open(source, "rb") yuv = f.read() f.close() print("read file") rgb_bytes = yuv420_to_rgb888(1280,720, yuv) # cProfile.runctx('yuv420_to_rgb888(1920,1088, yuv)', {'yuv420_to_rgb888':yuv420_to_rgb888}, {'yuv':yuv}) print("finished conversion. Creating image object") img = Image.frombytes("RGB", (1280,720), bytes(rgb_bytes)) print("Image object created. Starting to save") img.save(dest, "JPEG") img.close() print("Save completed") testConversion("./test/4.yuv", "4.jpg")
看完了這篇文章,相信你對(duì)“YUV數(shù)據(jù)格式是什么”有了一定的了解,如果想了解更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。