溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

YUV數(shù)據(jù)格式是什么

發(fā)布時(shí)間:2021-03-24 11:19:29 來(lái)源:億速云 閱讀:294 作者:小新 欄目:開(kāi)發(fā)技術(shù)

小編給大家分享一下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:

YUV數(shù)據(jù)格式是什么

Y 中含有三元色色信息,且有較多的 G,所以他們一起可以顯示出全彩的圖像。

很顯然我們可以想到是不是會(huì)有 YCgCb、YCgCr 等,針對(duì)不同的應(yīng)用場(chǎng)景,也確實(shí)有相關(guān)應(yīng)用研究。

如下圖,一張從上到下分別為原圖、Y、U 和 V:

YUV數(shù)據(jù)格式是什么

采用 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 方式):

YUV數(shù)據(jù)格式是什么

這幅圖的上面一幅可以看出 Y1、Y2、Y7、Y8 共用 U1 和 V1。后面的線性數(shù)組為其存儲(chǔ)順序,可以看出 Y、U 和 V 都是順序存儲(chǔ)的,往外寫的時(shí)候,先按順序?qū)?Y 分量寫出,然后再根據(jù) U、V 分別將它們依次寫出即可。

NV12 的單幀結(jié)構(gòu)示意圖如下(Planar 方式):

YUV數(shù)據(jù)格式是什么

可以看出與 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è)資訊頻道,感謝各位的閱讀!

向AI問(wèn)一下細(xì)節(jié)

免責(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)容。

yuv
AI