使用OpenCV VideoWriter和Python BytesIO在内存中流式传输视频

9

我在想是否可能使用Python中的OpenCV VideoWriter类来“流式”传输数据?

通常用于处理本应写入磁盘的数据,我使用BytesIO(或StringIO)将其存储在内存中。

然而,我的尝试使用BytesIO失败了:

import cv2
from io import BytesIO

stream = cv2.VideoCapture(0)
fourcc = cv2.VideoWriter_fourcc('x264')

data = BytesIO()

# added these to try to make data appear more like a string
data.name = 'stream.{}'.format('av1')
data.__str__ = lambda x: x.name

try:
    video = cv2.VideoWriter(data, fourcc=fourcc, fps=30., frameSize=(640, 480))
    start = data.tell()

        # Check if camera opened successfully
        if (stream.isOpened() == False): 
            print("Unable to read camera feed", file=sys.stderr)
            exit(1)

        # record loop
        while True:
            _, frame = stream.read()
            video.write(frame)
            data.seek(start)
            # do stuff with frame bytes
            # ...

            data.seek(start)

    finally:
        try:
            video.release()
        except:
            pass
finally:
    stream.release()

然而,我没有写入BytesIO对象,而是出现了以下消息:

Traceback (most recent call last):
  File "video_server.py", line 54, in talk_to_client
    video = cv2.VideoWriter(data, fourcc=fourcc, fps=fps, frameSize=(width, height))
TypeError: Required argument 'apiPreference' (pos 2) not found

...所以当我修改VideoWriter调用为cv2.VideoWriter(data, apiPreference=0, fourcc=fourcc, fps=30., frameSize=(640, 480))时(我读到0表示自动,但我也尝试了cv2.CAP_FFMPEG),我得到以下错误:

Traceback (most recent call last):
  File "video_server.py", line 54, in talk_to_client
    video = cv2.VideoWriter(data, apiPreference=0, fourcc=fourcc, fps=fps, frameSize=(width, height))
TypeError: bad argument type for built-in operation

我的问题是,是否有可能使用cv2.VideoWriter类在内存中编写编码视频,如果可以的话,如何实现?

目前我已经没有更多的想法了,所以任何帮助都将不胜感激 :-)


你是否正在尝试在不写入磁盘的情况下对视频进行编码?这在OpenCV中是不可能的。 - zindarod
3个回答

5

不幸的是,OpenCV不支持将视频编码到内存中(或从内存中解码)。您必须将其写入磁盘(或从磁盘读取)才能让VideoWriter(或VideoCapture)正常工作。


3
我担心情况确实如此。我想在通过网络传输之前对视频进行编码,以减少发送的数据量,但是我试图避免将数据写入磁盘... 我猜我只能退而使用逐帧图像发送到网络。 - Liam Deacon
1
是的,很不幸OpenCV并不支持这个功能,我相信它会被广泛使用。 - Farhan

3
如果你想要方便地加载一个已经存在于内存中的视频,可以使用临时文件:https://docs.python.org/3/library/tempfile.html#tempfile.NamedTemporaryFile
import tempfile
import cv2

my_video_bytes = download_video_in_memory()

with tempfile.NamedTemporaryFile() as temp:
   temp.write(my_video_bytes)

   video_stream = cv2.VideoCapture(temp.name)

   # do your stuff.

这将不幸地在磁盘上创建一个文件。但是,至少你不必自己管理它。有一个`SpooledTemporaryFile()`的实现可以保留在内存中,但是不幸的是,它不会创建一个文件系统名称,供OpenCV引用。
编辑更新:
进一步探索Linux界面,看起来你可以完全利用临时文件,并通过使用`tmpfs`工具使其仅存在于内存中。
在Ubuntu机器上进行了测试,未指定`dir`的NamedTemporaryFile()对象将遵循TMPDIR(在我的机器上指向`/tmp`)。现在,对于其他操作系统,我不确定,但是我的机器没有将`/tmp`挂载为`tmpfs`。它被挂载在主分区上。
但是,只需像这样轻松地创建自己的“内存文件系统”:`sudo mount -t tmpfs -o size=1G tmpfs my_folder`
如果我们检查`df my_folder -h`的输出,我看到这个:
Filesystem      Size  Used Avail Use% Mounted on
tmpfs           1.0G     0  1.0G   0% my_folder

Fricken wicked!!
So now if I do this in Python:
with tempfile.NamedTemporaryFile(dir="my_folder") as temp:

我刚刚在Linux中使用mkstemp创建了一个临时文件,它位于完全内存的文件系统中。当然,我要承担将数据从Python内存复制到另一个内存位置的成本。但是,在处理大文件时,这可能比写入磁盘更便宜。
请确保您的内存文件系统有足够的RAM来容纳视频文件,否则,按设计,tmpfs将利用交换空间将文件放在磁盘上。
编辑更新:
Kubernetes还允许您在Pod或Deployment上直接执行此操作。因此,您可以直接在容器的文件系统上创建一个TMPFS目录,然后就可以使用了!
volumes:
- name: data
  emptyDir:
    medium: Memory
    sizeLimit: 64Mi
volumeMounts:
- mountPath: /tmp/data
  name: data

这非常整洁,将在机器学习环境中非常有用。 - pdoherty926

1
如果您使用Linux,您可以创建一个虚拟内存盘并向其写入内容。
mount -t tmpfs -o size=512m tmpfs /mnt/ramdisk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接