基于YUV 数据格式详解及python实现方式


Posted in Python onDecember 09, 2019

YUV 数据格式概览

YUV 的原理是把亮度与色度分离,使用 Y、U、V 分别表示亮度,以及蓝色通道与亮度的差值和红色通道与亮度的差值。其中 Y 信号分量除了表示亮度 (luma) 信号外,还含有较多的绿色通道量,单纯的 Y 分量可以显示出完整的黑白图像。U、V 分量分别表示蓝 (blue)、红 (red) 分量信号,它们只含有色彩 (chrominance/color) 信息,所以 YUV 也称为 YCbCr,C 意思可以理解为 (component 或者 color)。

维基百科上的 RGB 转 YUV 的公式能更好的反应 YUV 与 RGB 的关系,以及为什么称为 YCbCr:

基于YUV 数据格式详解及python实现方式

Y 中含有三元色色信息,且有较多的 G,所以他们一起可以显示出全彩的图像。

很显然我们可以想到是不是会有 YCgCb、YCgCr 等,针对不同的应用场景,也确实有相关应用研究。

如下图,一张从上到下分别为原图、Y、U 和 V:

基于YUV 数据格式详解及python实现方式

采用 YUV 而不是使用 RGB,既有历史原因:为了兼容老式黑白电视,因为 YUV 如果只输出 Y 就成了黑白图像了。也有 YUV 自己的其他优点,例如可以根据需要,采用特定的 YUV 存储格式,以降低?码流的空间占用。

YUV 存储格式

YUV 存储格式有两大类:planar 和 packed。

对于 planar 的 YUV 格式,先连续存储所有像素点的 Y,紧接着存储所有像素点的 U,随后是所有像素点的 V。相当于将 YUV 拆分成三个平面 (plane) 存储。

对于 packed 的 YUV 格式,每个像素点的 Y,U,V 是连续交替存储的。

YUV 码流又根据不同的采样方式分为 YUV4:4:4、YUV4:2:2、YUV4:2:0、YUV4:1:1 等存储格式,其中前 3 种较常见。所谓采样意思就是根据一定的间隔取值。其中的比例是指 Y、U、V 表示的像素,三者分别占的比值。可以按照如下方式理解,实现存储和扫描与 DVD 的扫描线有关。

例如:

YUV4:4:4 是指每个像素分别有一个 Y、一个 U 和一个 V 组成,即每 4 个 Y 采样,就对应 4 个 Cb 和 4 个 Cr 采样,也就是一个像素占用 8+8+8=24 位,这种存储方式图像质量最高,但空间占用也最大,空间占用与 RGB 存储时一样。对于一个 M*N分辨率的图像,该模式下存储空间占用字节数为 M*N*3。

YUV4:2:2 是指每 4 个 Y 采样,对应 2 个 Cb 和 2 个 Cr 采样,这样在解析时就会有一些像素点只有亮度信息而没有色度信息,缺失的色度信息就需要在解析时由相邻的其他色度信息根据一定的算法填充。这种方式下平均一个像素占用空间为 8+4+4=16 位。对于一个 M*N 分辨率的图像,空间占用 16/24,即 M*N*3*(16/24) = M*n*2 个字节。

YUV4:2:0 是指每 4 个 4 采样,对应 2 个 U 采样或者 2 个 V 采样,注意其中并不是表示 2 个 U 和 0 个 V,而是指无论水平下采样还是垂直下采样,色度采样都只有亮度的一半。该存储格式下,平均每个像素占用空间为 8+4+0=12 位。对于一个 M*N 分辨率的图像来说,空间占用为原来的 12/24,即 M*N*3*(12/24)=M*N*3/2。节省较多存储空间,该存储格式也最常用。

YUV4:1:1 是指每 4 个 Y 采样,对应 1 个 U 采样和一个 V 采样。平均每个像素占用空间为 8+2+2=12 位。图像空间占用情况同上。这种存储格式实际使用的非常少。

对于 packed 存储格式,略。

YV12/I420/YU12/NV12/NV21

YV12/I420/YU12/NV12/NV21 都属于 YUV 4:2:0。YU12 就是 I420,YV12/I420 也称为 YUV420P(即平面格式,planar),YV12 与标准模式 I420 的区别是 UV 顺序不同。

YV12 取名来源是 Y 后面紧跟 V(然后是 U),12 表示它位深为 12,也就是一个像素占用空间为 12 位。

在 I420(YU12) 格式中,U 平面紧跟在 Y 平面之后,然后才是 V 平面(即:YUV);但 YV12 则是相反(即:YVU)。大部分视频解码器的输出的原始图像都是 I420 格式(例如安卓下的图像通常都是 I420 或 NV21),而多数硬解码器中使用的都是 NV12 格式(例如 Intel MSDK、NVIDIA 的 cuvid、IOS 硬解码)。

另一类 YUV420SP, Y 分量平面格式,UV 打包格式,即 NV12。 NV12 与 NV21 类似,U 和 V 交错排列,不同在于 UV 顺序。

可理解如下:

I420: YYYYYYYY UU VV => YUV420P
YV12: YYYYYYYY VV UU => YUV420P
NV12: YYYYYYYY UVUV => YUV420SP
NV21: YYYYYYYY VUVU => YUV420SP

维基百科上有两张 I420 和 NV12 的两张图非常好:

I420 的单帧结构示意图如下(Planar 方式):

基于YUV 数据格式详解及python实现方式

这幅图的上面一幅可以看出 Y1、Y2、Y7、Y8 共用 U1 和 V1。后面的线性数组为其存储顺序,可以看出 Y、U 和 V 都是顺序存储的,往外写的时候,先按顺序将 Y 分量写出,然后再根据 U、V 分别将它们依次写出即可。

NV12 的单帧结构示意图如下(Planar 方式):

基于YUV 数据格式详解及python实现方式

可以看出与 YV12 不同的时,它的 Y 虽然也是顺序存储,但 U、V 却是交错存储的,这种方式存储在往外写出时则先直接顺序写出 Y,然后对 UV 分别依次写出。

Python的实现:将420P转为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的实现:将NV21转为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")

以上这篇基于YUV 数据格式详解及python实现方式就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python文档生成工具pydoc使用介绍
Jun 02 Python
python抽象基类用法实例分析
Jun 04 Python
python 全局变量的import机制介绍
Sep 07 Python
利用Python2下载单张图片与爬取网页图片实例代码
Dec 25 Python
pandas.dataframe中根据条件获取元素所在的位置方法(索引)
Jun 07 Python
将Dataframe数据转化为ndarry数据的方法
Jun 28 Python
解决django同步数据库的时候app models表没有成功创建的问题
Aug 09 Python
解决python 文本过滤和清理问题
Aug 28 Python
python如何求数组连续最大和的示例代码
Feb 04 Python
解决Python pip 自动更新升级失败的问题
Feb 21 Python
Python文件名匹配与文件复制的实现
Dec 11 Python
用python制作个音乐下载器
Jan 30 Python
Python编写一个验证码图片数据标注GUI程序附源码
Dec 09 #Python
Python内置方法实现字符串的秘钥加解密(推荐)
Dec 09 #Python
opencv-python 读取图像并转换颜色空间实例
Dec 09 #Python
opencv-python 提取sift特征并匹配的实例
Dec 09 #Python
python 多维高斯分布数据生成方式
Dec 09 #Python
使用python模拟高斯分布例子
Dec 09 #Python
使用python+whoosh实现全文检索
Dec 09 #Python
You might like
实现dedecms全站URL静态化改造的代码
2007/03/29 PHP
php中使用DOM类读取XML文件的实现代码
2011/12/14 PHP
php安全之直接用$获取值而不$_GET 字符转义
2012/06/03 PHP
php短址转换实现方法
2015/02/25 PHP
typecho插件编写教程(三):保存配置
2015/05/28 PHP
简单谈谈php延迟静态绑定
2016/01/26 PHP
php 人员权限管理(RBAC)实例(推荐)
2017/05/24 PHP
laravel按天、按小时,查询数据的实例
2019/10/09 PHP
JS实现拖动示例代码
2013/11/01 Javascript
js实现Select头像选择实时预览代码
2015/08/17 Javascript
jQuery实现圣诞节礼物传送(花式轮播)
2016/12/25 Javascript
JS简单实现获取元素的封装操作示例
2017/04/07 Javascript
react-native中ListView组件点击跳转的方法示例
2017/09/30 Javascript
JavaScript实现英语单词题库
2019/12/24 Javascript
JavaScript修改注册表实例代码
2020/01/05 Javascript
微信小程序录音实现功能并上传(使用node解析接收)
2020/02/26 Javascript
[04:22]DOTA2上海特级锦标赛主赛事第四日TOP10
2016/03/06 DOTA
Python psutil模块简单使用实例
2015/04/28 Python
用python标准库difflib比较两份文件的异同详解
2018/11/16 Python
朴素贝叶斯Python实例及解析
2018/11/19 Python
Python中PyQt5/PySide2的按钮控件使用实例
2019/08/17 Python
Python logging设置和logger解析
2019/08/28 Python
python 基于dlib库的人脸检测的实现
2019/11/08 Python
pandas factorize实现将字符串特征转化为数字特征
2019/12/19 Python
Python argparse模块使用方法解析
2020/02/20 Python
CSS3条纹背景制作的实战攻略
2016/05/31 HTML / CSS
美国受欢迎的女性牛仔裤品牌:DL1961
2016/11/12 全球购物
美国运动鞋和运动服零售商:Footaction
2017/04/07 全球购物
美德少年事迹材料500字
2014/08/19 职场文书
超市仓管员岗位职责范本
2014/09/18 职场文书
小学师德师风整改措施
2014/10/27 职场文书
自荐信格式模板
2015/03/27 职场文书
2015年护士工作总结范文
2015/03/31 职场文书
医疗纠纷调解协议书
2015/08/06 职场文书
Python3.8官网文档之类的基础语法阅读
2021/09/04 Python
「天才王子的赤字国家重生术」妮妮姆·拉雷粘土人开订
2022/03/21 日漫