【问题标题】:How to extract frames from a .yuv video file (YUV420) using python and openCV?如何使用 python 和 openCV 从 .yuv 视频文件 (YUV420) 中提取帧?
【发布时间】:2020-06-27 11:57:00
【问题描述】:

我需要读取一个 yuv 视频文件,从中提取单个帧,将其转换为灰度,然后计算相邻帧之间的 Lucas Kanade 光流。我最初使用的是 mp4 视频,这是我提取单个帧的代码:

import cv2 as cv
import numpy as np
cap = cv.VideoCapture('C:\\Users\\Ann Baiju\\Project\\video_tampering_dataset\\videos\\h264_lossless\\07_forged.mp4')

ret, frame1 = cap.read()
prvs = cv.cvtColor(frame1, cv.COLOR_BGR2GRAY)

height, width, _ = frame1.shape

while(1):
    ret, frame2 = cap.read()
    if ret==False: break
    next = cv.cvtColor(frame2, cv.COLOR_BGR2GRAY)
    #Code for calculating Lucas Kanade optical flow
    N=N+1
    prvs = next

cap.release()

现在有些事情发生了变化,我必须使用 yuv 视频文件的数据集。但是当我给 VideoCapture() 一个 yuv 文件时,我收到如下错误:

[IMGUTILS @ 00000078a4bee5c0] 图片尺寸0x0无效 [错误:0] 全局 C:\projects\opencv-python\opencv\modules\videoio\src\cap.cpp (116) cv::VideoCapture::open VIDEOIO(CV_IMAGES): 引发 OpenCV 异常:

OpenCV(4.1.2) C:\projects\opencv-python\opencv\modules\videoio\src\cap_images.cpp:253: error: (-5:Bad argument) CAP_IMAGES: 找不到起始编号 (以文件名):C:\Users\Ann Baiju\Project\Copy_Move_Datasets\new original\DERF\hall.yuv in function 'cv::icvExtractPattern'

Traceback(最近一次调用最后一次): 文件“test1.py”,第 6 行,在 prvs = cv.cvtColor(frame1, cv.COLOR_BGR2GRAY) cv2.error: OpenCV(4.1.2) C:\projects\opencv-python\opencv\modules\imgproc\src\color.cpp:182: error: (-215:Assertion failed) !_src.empty() in function 'cv::cvtColor'

我该如何解决这个问题? 此外,我了解 yuv 是一种原始视频文件格式,其中没有大小或 fps 信息。有什么方法可以从文件中推断出来,还是我必须手动输入该信息?

关于如何从 yuv 视频获取帧大小信息(高度和宽度)的问题,是否可以使用 FFmpeg 将 yuv 视频转换为其他格式(例如 mp4),从中获取信息,然后删除mp4 视频并继续使用 yuv 视频?如果有怎么办?

import cv2
import numpy as np
import os
import subprocess as sp

yuv_filename = 'can_0.yuv'
#flow=[]

width, height = 320, 240

file_size = os.path.getsize(yuv_filename)
n_frames = file_size // (width*height*3 // 2)
f = open(yuv_filename, 'rb')


old_yuv = np.frombuffer(f.read(width*height*3//2), dtype=np.uint8).reshape((height*3//2, width))
cv2.imshow('frame',old_yuv)
cv2.waitKey(3000)

# Convert YUV420 to Grayscale
old_gray = cv2.cvtColor(old_yuv, cv2.COLOR_YUV2GRAY_I420)
cv2.imshow('frame_gs',old_gray)
cv2.waitKey(3000)

当我运行上面的代码时,我得到的yuv图像是:

这对于 yuv 图像或某种分辨率问题是否正常?还有为什么没有颜色?但是,当我将其转换为灰度时,它会正常显示:

原始帧是(使用 yuvplayer 2.5 查看):

【问题讨论】:

    标签: python opencv video yuv


    【解决方案1】:

    没有单一的 YUV420 文件格式,但有许多可能的像素排序。

    我创建了一个“自包含”代码示例,演示如何读取 YUV420 帧。
    代码示例:

    • 使用 FFmpeg 生成 YUV420 格式的合成视频。
      要执行示例,请下载(或安装)最新的稳定版本。
      对于 Windows 操作系统,您可以将 ffmpeg.exe 放在与 Python 脚本相同的路径中。
    • 逐帧读取YUV420,转换为BGR,并显示每一帧。
      您不需要颜色,但它可能对测试很重要。
    • 将 YUV420 转换为灰度,并将每一帧显示为灰度。

    FFmpeg 创建 I420 平面格式的 YUV:

    YYYYYY
    YYYYYY
    YYYYYY
    YYYYYY
    UUU
    UUU
    VVV
    VVV

    YUV 像素的范围是“有限范围”:

    • Y 范围是 [16, 235]。
    • U 和 V 范围为 [16, 240]。

    代码如下:

    import cv2
    import numpy as np
    import os
    import subprocess as sp
    
    # Build synthetic video and read binary data into memory (for testing):
    #########################################################################
    mp4_filename = 'input.mp4'  # the mp4 is used just as reference
    yuv_filename = 'input.yuv'
    width, height = 640, 480
    fps = 1 # 1Hz (just for testing)
    
    # Build synthetic video, for testing (the mp4 is used just as reference):
    sp.run('ffmpeg -y -f lavfi -i testsrc=size={}x{}:rate=1 -vcodec libx264 -crf 18 -t 10 {}'.format(width, height, mp4_filename))
    sp.run('ffmpeg -y -f lavfi -i testsrc=size={}x{}:rate=1 -pix_fmt yuv420p -t 10 {}'.format(width, height, yuv_filename))
    #########################################################################
    
    
    file_size = os.path.getsize(yuv_filename)
    
    # Number of frames: in YUV420 frame size in bytes is width*height*1.5
    n_frames = file_size // (width*height*3 // 2)
    
    # Open 'input.yuv' a binary file.
    f = open(yuv_filename, 'rb')
    
    for i in range(n_frames):
        # Read Y, U and V color channels and reshape to height*1.5 x width numpy array
        yuv = np.frombuffer(f.read(width*height*3//2), dtype=np.uint8).reshape((height*3//2, width))
    
        # Convert YUV420 to BGR (for testing), applies BT.601 "Limited Range" conversion.
        bgr = cv2.cvtColor(yuv, cv2.COLOR_YUV2BGR_I420)
    
        # Convert YUV420 to Grayscale
        gray = cv2.cvtColor(yuv, cv2.COLOR_YUV2GRAY_I420)
    
        #Show RGB image and Grayscale image for testing
        cv2.imshow('rgb', bgr)
        cv2.waitKey(500)  # Wait a 0.5 second (for testing)
        cv2.imshow('gray', gray)
        cv2.waitKey(500)  # Wait a 0.5 second (for testing)
    
    f.close()
    
    cv2.destroyAllWindows()
    

    注意:

    • 很有可能您的 YUV420 文件不是 I420,“有限范围”格式。

    更新:

    使用FFmpegYUV420 转换为未压缩的AVI 的示例:

    • 输入文件的扩展名必须为.yuv

    在命令行控制台中:

    ffmpeg -y -s 640x480 -pixel_format yuv420p -i input.yuv -vcodec rawvideo -pix_fmt bgr24 input.avi
    

    在 Python 中使用 subprocess

    sp.run('ffmpeg -y -s {}x{} -pixel_format yuv420p -i input.yuv -vcodec rawvideo -pix_fmt bgr24 input.avi'.format(width, height))
    
    • 未压缩的 AVI 视频文件将非常大(是 YUV 文件大小的两倍)。
      如果视频质量不是最重要的,您可以使用 H.264 压缩(例如):

      ffmpeg -y -s 640x480 -pixel_format yuv420p -i input.yuv -vcodec libx264 -crf 17 -pix_fmt yuv420p input.mp4
      

      -crf 17 参数保持高质量(几乎无损)。
      注意:文件格式.mp4.avi在使用H.264编码时并不重要(但.mp4在使用H.264编码时更常见)。

    【讨论】:

    • 我找到了正确的转换:cv2.COLOR_YUV2BGR_I420cv2.COLOR_YUV2GRAY_I420
    • 需要乘以1.5得到帧大小吗?
    • 在 YUV420 格式中,平均每个像素有 1.5 个字节。你可以数一下我给出的例子:对于 6x4 的图像分辨率,有6*4 Y 元素、3*2 U 元素和3*2 V 元素。总计:6*4 + 3*2 + 3*2 = 36 = 6*4*1.5
    • 我更新了我的帖子以包含使用 FFmpeg 将 YUV 转换为 AVI 和 MP4 的示例。我还将imput 的拼写错误更正为input
    • 我编辑了我的问题以添加我得到的 yuv 框架。它应该看起来像这样吗?另外我还有一个问题,我想你可以回答:stackoverflow.com/questions/61035461/…你能看一下吗? @Rotem
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-06
    • 2018-03-09
    • 1970-01-01
    • 1970-01-01
    • 2011-01-14
    • 2016-07-15
    • 1970-01-01
    相关资源
    最近更新 更多