将大量图像数据(10 ^ 9像素)加载到内存中

时间:2011-03-08 11:47:51

标签: c++ image video video-processing

我正在将一系列视频提取到一个Surface阵列,重新排列成一个新视频,随时间交换x维。以下是一些不同类型的效果示例:http://www.youtube.com/view_play_list?p=B2540182DE868E85

当我尝试将1280帧1280x720视频(1,179,648,000像素)存储到std::bad_alloc时,应用始终与Surface[]崩溃。它不会因1280帧1080x720视频(995,328,000像素)而崩溃。

我做了一个简单的测试,它可以在我的电脑上运行(4GB RAM),但不能在朋友的懦弱笔记本电脑上运行:

maxWidth = 1920;
while ((inW * inH * maxWidth) >= 1000000000)
  maxWidth -= 20;

两个问题:

  1. 有没有比Surface阵列快速访问10 ^ 9像素的更好方法?
  2. 这个内存限制是什么,如何在为输出设置maxWidth时对其进行测试并避免使用?
  3. 非常感谢C ++ noob。我将来源放在Github: Redimensionator上。它使用Cinder。

4 个答案:

答案 0 :(得分:2)

嗯,这取决于您的硬件/操作系统/软件/编译器。

  • 您是在32位还是64位操作系统上运行?
  • 您是否将应用编译为32位或64位?
  • 你是一次性分配还是分块(分别说每一帧)?
  • 你真的需要同时在内存中的所有图像,还是可以分割你的作品?

是的,每个问题的后半部分都使您更容易使用大型数组。

答案 1 :(得分:1)

你试图存储10亿像素,每个像素有一个24位的颜色?假设你有1GB的可用内存,你就可以使用256色的调色板并且每个字节存储一个像素。

尝试将其存储在连续的内存中更有可能失败。如果你使用std :: deque,你就有更多的机会将大量的内容装入内存。

顺便说一句,即使你的系统有RAM,你的地址空间也有限。

假设256种颜色是不够的,你可以选择65536种颜色,这将使用2个字节的像素,但这需要2GB。 64位地址空间可以帮到你。完整的24位颜色需要至少3GB,更有可能使用4GB进行正确对齐。

从长远来看,如果你想要高分辨率,你可能不希望将它全部存储在内存中。

答案 2 :(得分:1)

首先,在32位平台上,您对地址空间使用的硬限制将大约为2GB(但可能更低) - 假设您将其全部映射到一次。最好假设在连续内存中不能超过512MB,在非连续内存中不能超过1-1.5GB(即通过制作多个小映射)。这很可能是你遇到的问题;你用完了连续的地址空间。对于32位系统,硬件又限制在(对于intel CPU)大约16GB的内存。你真的,真的不想交换。所以这意味着您有以下几种选择之一:

  • 使用64位系统和一个非常大的阵列(简单快速,需要大量内存)。
  • 使用32位系统和黑客来解决地址空间限制。这往往意味着您需要创建共享内存对象,并且一次只映射部分空间。在Windows上,您可以使用anonymous file mapping object - 基本上具有NULL名称的共享内存。在Linux上,您需要先增加/ dev / shm的最大大小,然后使用shm_openmmap。 (复杂,几乎和64位一样快,如果做得好的话。最小化你做的重映射次数。还需要大量内存)
  • 使用磁盘文件。 只有SSD的真实选项;在真正的磁盘上,寻求将花费太长时间才能实用。基本上你只是寻找文件并一次写出数据列。 (相对较慢,但不太复杂。需要SSD。内存要求最低)
  • 多次通过。您可以选择一组输出帧一次保存在内存中;解码整个视频,跳过与你未在内存中保存的帧相对应的部分。完成当前的一组帧后,将它们写入磁盘并重新开始,使用一组新的输出帧从头开始解码视频。这非常适合大规模并行性 - 您可以将每个输出设置分离到另一台单独的计算机来完成工作,然后将它们全部拼接在一起。 (中等复杂;慢;交换内存的CPU时间。如果并行化可以非常快)。

如果您有足够的内存来容纳整个输出视频,前两个选项都很好。理想情况下,你想要64位路线;重新映射共享内存窗口是一项昂贵的操作,你将会做很多事情。

使用第四个选项,可能很难知道内存限制是什么。我建议使用测试分配进行二进制搜索,以确定您可以使用的地址空间中有多少空间(您应该使用低级分配调用来避免堆开销,请注意)。请注意,如果您不小心这可能不会为视频解码器留下任何地址空间 - 最好从结果中减去100mb左右,并重新分配它以为正常堆提供一些空间。你也应该小心保持低于总物理内存,以避免触及交换。

在不了解您的操作系统以及您从Surface课程中获得的库的情况下,很难更具体地了解如何进行探测 - 但您确实应该避免将其保留在正常堆中,以避免其他代码中的分配错误,可能没有检测到处理OOM。

作为旁注,您可能希望在准备输出帧时将其旋转90度(即将它们放入column major order)。然后,您可以在构建所有原始图像后(或者甚至在从原始图像数据编码为压缩格式时)将它们作为最终传递回旋转。如果您决定使用带有SSD的磁盘路径,这一点尤其重要 - 它将有助于避免不必要的读取和写入,就像行主要顺序(视频的常规顺序)一样,您必须跳过其他列的像素你写一个。但是,对于内存工作,它仍然有用,因为它可以改善缓存局部性。

答案 3 :(得分:1)

为什么不一次为一帧分配存储,直到操作系统无法分配内存为止?这样,您不需要打扰32位与64位或共享内存。唯一的价格是额外的间接。

关键是,确实没有一个好的,独立于平台的方法来确定操作系统可以给你的最大块的大小(这取决于很多因素......虚拟地址空间碎片,数量物理内存可用(包括交换空间),配额)。