Python 读取 YUV(NV12) 视频文件

一、YUV 简介

  • YUV:是一种颜色编码方法,常使用在各个视频处理组件中
    • Y'UV, YCbCr, YPbPr等专有名词都可以称为 YUV,彼此有重叠
    • Y表示明亮度(单取此通道即可得灰度图),UV则是色度、浓度
  • 主流的采样方式有三种,YUV4:4:4,YUV4:2:2,YUV4:2:0
    • 可以根据其采样格式来从码流中还原每个像素点的 YUV 值,进而通过 YUVRGB 的转换公式提取出每个像素点的 RGB 值,然后显示出来
    • YUV4:2:0 数据在内存中的长度是 3 / 2 * heigth * width,是 RGB24(heigth * width * 3) 格式视频数据内存的一半

二、YUV420(NV12、NV21、I420、YV12)

# NV12、NV21 的存储格式为 Y 平面,UV 打包,即:Y 信息存储在一个数组中,UV 信息存储在一个矩阵中。
# 不同点在于 UV 的排列顺序
NV12: YYYYYYYY UVUV     =>  YUV420SP
NV21: YYYYYYYY VUVU     =>  YUV420SP

# I420、YV12 三个分量均为平面格式,即:分别存放在三个 Byte 型数组中
I420: YYYYYYYY UU VV    =>  YUV420P
YV12: YYYYYYYY VV UU    =>  YUV420P
  • 假设一个分辨率为8X4YUV 图像,它们的格式如下图:
    这里写图片描述

三、读取 YUV(NV12) 视频文件并保存

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import cv2
import numpy as np


def yuv2bgr(filename, height, width, startfrm):
    """
    :param filename: 待处理 YUV 视频的名字
    :param height: YUV 视频中图像的高
    :param width: YUV 视频中图像的宽
    :param startfrm: 起始帧
    :return: None
    """
    fp = open(filename, 'rb')

    framesize = height * width * 3 // 2  # 一帧图像所含的像素个数
    h_h = height // 2
    h_w = width // 2

    fp.seek(0, 2)  # 设置文件指针到文件流的尾部
    ps = fp.tell()  # 当前文件指针位置
    numfrm = ps // framesize  # 计算输出帧数
    fp.seek(framesize * startfrm, 0)

    for i in range(numfrm - startfrm):
        Yt = np.zeros(shape=(height, width), dtype='uint8', order='C')
        Ut = np.zeros(shape=(h_h, h_w), dtype='uint8', order='C')
        Vt = np.zeros(shape=(h_h, h_w), dtype='uint8', order='C')

        for m in range(height):
            for n in range(width):
                Yt[m, n] = ord(fp.read(1))
        for m in range(h_h):
            for n in range(h_w):
                Ut[m, n] = ord(fp.read(1))
        for m in range(h_h):
            for n in range(h_w):
                Vt[m, n] = ord(fp.read(1))

        img = np.concatenate((Yt.reshape(-1), Ut.reshape(-1), Vt.reshape(-1)))
        img = img.reshape((height * 3 // 2, width)).astype('uint8')  # YUV 的存储格式为:NV12(YYYY UV)

        # 由于 opencv 不能直接读取 YUV 格式的文件, 所以要转换一下格式
        bgr_img = cv2.cvtColor(img, cv2.COLOR_YUV2BGR_NV12)  # 注意 YUV 的存储格式
        cv2.imwrite('yuv2bgr/%d.jpg' % (i + 1), bgr_img)
        print("Extract frame %d " % (i + 1))

    fp.close()
    print("job done!")
    return None


if __name__ == '__main__':
    _ = yuv2bgr(filename='xxx.yuv', height=1080, width=1920, startfrm=0)

四、参考资料

1、详解YUV数据格式
2、关于YUV视频的读取、播放,保存帧图片、以及处理的图像保存为YUV视频
3、opencv 读取 普通视频、以YUV数据格式保存的视频 以及 图片序列的方法
4、Python读取YUV格式文件,并使用opencv显示

猜你喜欢

转载自blog.csdn.net/mzpmzk/article/details/81239532