【问题标题】:Named memory-mapped files in Python?Python中命名的内存映射文件?
【发布时间】:2021-11-18 12:14:27
【问题描述】:

我正在使用OpenCV 处理网络服务中的一些视频数据。在调用 OpenCV 之前,视频已经加载到 bytearray 缓冲区,我想将其传递给 VideoCapture 对象:

# The following raises cv2.error because it can't convert '_io.BytesIO' to 'str' for 'filename'
cap = cv2.VideoCapture(buffer) 

不幸的是,VideoCapture() 需要一个字符串文件名,而不是缓冲区。现在,我将bytearray 保存到一个临时文件中,并将其名称传递给VideoCapture()

问题:

  • 有没有办法在 Python 中创建 named 内存文件,这样我就可以安抚 OpenCV?
  • 或者,是否有另一个支持缓冲区的 OpenCV API?

【问题讨论】:

    标签: python opencv


    【解决方案1】:

    注意:POSIX 特定的!由于您没有提供 OS 标签,我认为没关系。

    根据this answer(和this shm_overview manpage),系统上始终存在/dev/shm。正如here 建议的那样,这是一个映射在共享(不是Python 进程内存)内存池中的tmpfs,但优点是您不需要创建它,所以没有有趣的发明:

    • os.system("mount ...")
    • Popen(["mount", ...]) 包装器。

    像这样简单地使用tempfile.NamedTemporaryFile()

    from tempfile import NamedTemporaryFile
    with NamedTemporaryFile(dir="/dev/shm") as file:
        print(file.name)
        # /dev/shm/tmp2m86e0e0
    

    然后您可以将其输入 OpenCV 的 API 包装器。或者,使用pyfilesystem 作为该设备/FS 的更广泛的包装器。

    另外,multiprocessing.heap.Arena 也使用它,所以如果它不起作用,就会出现更多麻烦。对于使用 winapi 的 Windows check this implementation

    对于/dev/shm的大小:

    根据sudo ipcs 判断,如果您不使用套接字、管道或磁盘,这很可能是您希望在进程之间共享内容时使用的方式。

    由于它是 POSIX,它应该可以在兼容 POSIX 的系统上运行,因此也可以在 MacOS(no) 或 Solaris 上运行,但我无法尝试。

    【讨论】:

    • 使用/dev/shm 是个好主意。我没有提出它只是因为您(afaik)无法说出它有多大,这可能很重要。我宁愿自己制作一个 tmpfs 并且知道我不会杀死系统。但是YMMV
    • @2e0byo 公平点。如果我找到它,我会尝试从内核源代码中挖掘一些常量或设置并编辑答案。现在我只添加了 linuxquestions.org 参考。
    • @2e0byo 已更新
    • 有趣的是,我的系统上cat /proc/mounts | grep tmpfs 的输出表明/dev/shm 不受挂载选项的限制(但可能很容易受到其他东西的限制),而 /tmp 和 /run/user/ 1000 甚至 更多 有限:tmpfs /dev/shm tmpfs rw,nosuid,nodev,inode64 0 0 tmpfs /tmp tmpfs rw,nosuid,nodev,nr_inodes=409600,inode64 0 0 tmpfs /run/user/1000 tmpfs rw,nosuid,nodev,relatime,size=376112k,nr_inodes=94028,mode=700,uid=1000,gid=1000,inode64 0 0
    • shm.h 似乎包含最大大小常量,所以我认为它有限
    【解决方案2】:

    部分回答这个问题:我不知道在 python 中创建指向内存的命名文件类对象:这是操作系统要做的事情。有一种非常简单的方法可以在大多数现代 *nixs 中创建非常类似的命名内存映射文件:将文件保存到/tmp。如今,/tmp 几乎总是一个 ramdisk。但当然它可能是 zram(基本上是一个压缩的 ramdisk),你可能想先检查一下。无论如何,这比破坏磁盘或依赖于操作系统缓存要好。

    顺便说一句,制作专用 ramdisk 就像 mount -t tmpfs -o size=1G tmpfs /path/to/tmpfs 一样简单,或者与 ramfs 类似。

    调查一下,我认为您也不会对替代 api 有太多的运气:文件名的使用一直到 cap.cpp,我们有类似的东西:

    VideoCapture::VideoCapture(const String& filename, int apiPreference) : throwOnFail(false)
    {
        CV_TRACE_FUNCTION();
        open(filename, apiPreference);
    }
    

    似乎 python 绑定只是在此之上的一个薄层。但我愿意被证明是错的!

    参考文献

    https://github.com/opencv/opencv/blob/master/modules/videoio/src/cap.cpp#L72

    【讨论】:

      【解决方案3】:

      如果 VideoCapture 一个普通的 Python 对象,并且除了路径之外它还接受“类文件对象”,你可以喂它一个“类文件对象”,它可以从中读取。

      Python 的 StringIO 和 BytesIO 是内存中的类文件对象。一些有用的东西要记住;)

      OpenCV 特别希望那里有一个文件系统路径,所以这不可能

      OpenCV 是一个计算机视觉库。它不是处理视频文件的库。

      您应该查看 PyAV。它是 ffmpeg 库的(正确的!)包装器。您可以直接在其中输入数据,它会解码。 Here are some exampleshere are its tests that demonstrate further functionality。它的文档很薄,因为大多数用法都是(或应该已经...)由 ffmpeg 本身记录的。

      【讨论】:

      • 感谢您推荐 PyAV。我正在使用 OpenCV,因为我们可能需要未来的愿景。
      【解决方案4】:

      可能能够摆脱命名管道。您可以使用os.mkfifo 创建一个,然后使用multiprocess 模块生成一个将视频文件输入其中的后台进程。请注意,mkfifo 在 Windows 上不受支持。

      最重要的限制是管道不支持搜索,因此您的视频也无法搜索或倒带。它是否真的有效可能取决于 OpenCV 使用的视频格式和后端(gstreamer、v4l2、...)。

      【讨论】:

      • 从文件对象/文件系统的角度来看,它就像一辆破自行车。再说一次,如果只需要一个简单的只读缓冲区,那几乎就是管道的目的(至少是其中之一)。 +1,即使这样,我也肯定会在将来使用它。类似于套接字的行为。
      • 确实如此。至于“破自行车”的比喻:在 Unix 中,并非文件系统上的所有内容都必须是“常规文件”。例如,/dev/stdin/dev/stdout 也是流,与硬件通信的大多数设备节点也是如此。但果然,一些库假定您传递给它的文件是可查找的。​​span>
      • 是的,这就是比喻的原因。对于软件的任意两端之间的快速缓冲来说,这是一个非常好的方法,但除此之外它并不是很有用(与内存文件或共享内存相比)。它可能是最便宜的特定于操作系统的形式,因此在某些情况下它可能比/dev/shm 更受欢迎。因此,就像一辆没有刹车或没有链条的自行车,您仍然可以骑着它滑下山坡,它会很好地工作。只是不是其余的东西。 :D
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-03
      • 2016-02-23
      相关资源
      最近更新 更多