python - 如何将实时视频帧从 ffmpeg 传输到 PIL?

标签 python linux ffmpeg raspberry-pi gstreamer

我需要使用 ffmpeg/avconv 将 jpg 帧通过管道传输到 python PIL (Pillow) Image 对象,使用 gst 作为中介*。我一直在到处寻找这个答案,但运气不佳。我想我很接近 - 但我被困住了。 使用 Python 2.7

我理想的管道,从 python 启动,看起来像这样:

  1. ffmpeg/avconv(作为 h264 视频)
  2. 管道 ->
  3. gst-streamer(帧拆分为 jpg)
  4. 管道 ->
  5. Pil 图像对象

我将前几个步骤控制为单个命令,以硬件允许的最快速度将 .jpg 写入磁盘。

该命令看起来像这样:

command = [
        "ffmpeg",
        "-f video4linux2",
        "-r 30",
        "-video_size 1280x720",
        "-pixel_format 'uyvy422'",
        "-i /dev/video0",
        "-vf fps=30",
        "-f H264",
        "-vcodec libx264",
        "-preset ultrafast",
        "pipe:1 -",
        "|", # Pipe to GST
        "gst-launch-1.0 fdsrc !",
        "video/x-h264,framerate=30/1,stream-format=byte-stream !",
        "decodebin ! videorate ! video/x-raw,framerate=30/1 !",
        "videoconvert !",
        "jpegenc quality=55 !",
        "multifilesink location=" + Utils.live_sync_path + "live_%04d.jpg"
      ]

如果使用 popen 或 os.system 运行,这将成功地将帧写入磁盘。

但是我不想将帧写入磁盘,而是想在我的子进程管道中捕获输出,并在写入时读取帧,然后将其写入类似文件的缓冲区,然后 PIL 可以读取该缓冲区。

像这样:

    import subprocess as sp
    import shlex
    import StringIO

    clean_cmd = shlex.split(" ".join(command))
    pipe = sp.Popen(clean_cmd, stdout = sp.PIPE, bufsize=10**8)

    while pipe:

        raw = pipe.stdout.read()
        buff = StringIO.StringIO()
        buff.write(raw)
        buff.seek(0)

        # Open or do something clever...
        im = Image.open(buff)
        im.show()

        pipe.flush()

此代码不起作用 - 我什至不确定我是否可以通过这种方式使用“while pipe”。我对以这种方式使用缓冲区和管道还很陌生。

我不确定如何知道图像已写入管道或何时读取“下一个”图像。

在理解如何从管道而不是磁盘读取图像方面的任何帮助将不胜感激。

  • 这最终是一个 Raspberry Pi 3 管道,为了提高我的帧速率,我不能 (A) 从磁盘读取/写入磁盘或 (B) 使用逐帧捕获方法 - 相反直接从摄像头芯片运行 H246 视频。

最佳答案

我假设最终目标是在 Linux 上以高帧率处理 USB 摄像头,下面解决了这个问题。

首先,虽然一些 USB 摄像头支持 H.264,但 USB 摄像头的 Linux 驱动程序(UVC 驱动程序)目前不支持基于流的有效负载,其中包括 H.264,请参阅 driver home page 上的“UVC 功能”表。 ffmpeg 等用户空间工具使用该驱动程序,因此对于用于 USB 传输的视频格式具有相同的限制。

好消息是,如果相机支持 H.264,它几乎肯定会支持 MJPEG,它由 UVC 驱动程序支持并且压缩得很好,可以通过 USB 2.0 以 30 fps 支持 1280x720。您可以使用 v4l2-ctl -d 0 --list-formats-ext 列出您的相机支持的视频格式。例如,对于 Microsoft Lifecam Cinema,1280x720 仅支持 10 fps 的 YUV 4:2:2,但支持 30 fps 的 MJPEG。

对于从相机读取,我对 OpenCV 有很好的经验。在我的一个项目中,我有 24(!) 个 Lifecams 连接到一台 Ubuntu 6 核 i7 机器,它使用 320x240 以每台相机 7.5 fps 的速度实时跟踪果蝇(以及为每个相机保存一个 MJPEG AVI 以记录实验)。由于 OpenCV 直接使用 V4L2 API,因此它应该比使用 ffmpeg、gst-streamer 和两个管道的解决方案更快。

使用 OpenCV 从相机读取并创建 PIL 图像的基本代码(无错误检查)如下所示:

import cv2
from PIL import Image

cap = cv2.VideoCapture(0)   # /dev/video0
while True:
  ret, frame = cap.read()
  if not ret:
    break
  pil_img = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
  ...   # do something with PIL image

最后说明:您可能需要构建 v4l 版本的 OpenCV 以获得压缩 (MJPEG),请参见 this answer

关于python - 如何将实时视频帧从 ffmpeg 传输到 PIL?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41580034/

相关文章:

python - Python 和 Django 中用于从包含 m 的文件中选择 n 个 JavaScript 函数的高效算法

python - 为什么我会收到以下错误?值错误: setting an array element with a sequence

linux - 无法使用 postgres 创建空数据库——更改目录时权限被拒绝

node.js - 使用 NodeJS 将 ffmpeg 文件输出上传到 AWS s3

ffmpeg - 如何理解给定的 ffplay C 代码片段?

python - 如何在 Python 中使用递归来处理这个练习?

python - Pyspark dataframe 列包含字典数组,想要将字典中的每个键都变成一列

linux - 如何使用命令提示符从远程服务器的错误日志文件中删除 n 行

PHP pam_auth() 最佳管理指南

android - (Android) 将一个音频叠加到另一个音频