【问题标题】:Numpy - creating overlapping 3D subarrays as vectors that's memory efficientNumpy - 创建重叠的 3D 子数组作为内存高效的向量
【发布时间】:2012-03-01 14:43:36
【问题描述】:

我正在尝试从一个较大的 3D 数组(用于基于补丁的分割)中创建一个相同大小的所有重叠子数组的列表,其中每个子数组都需要展平(作为 1D 向量),以便我可以利用sklearn.neighbours.BallTree 中的球树。

例如,给定一个 100x100x100 的图像,如果我将其分解为 5x5x5 重叠的补丁(子阵列),我将拥有 96x96x96 = 884,736 个。

但是,如果没有 numpy 为每个展平/矢量化子数组分配更多内存,我还没有找到任何方法。这似乎是因为每个子数组在内存中并不连续。

例如对于 100x100x100 图像,如果我希望每个 5x5x5 补丁作为一维向量(长度为 125),numpy 决定在内存中为所有 884,736 分配一个全新的数组,然后变得相当大,特别是如果我想使用超过一张 100x100x100 的图片!

我欢迎任何在 python/numpy 中克服这种内存挑战的解决方案。我正在考虑创建 numpy.ndarray 对象的子类,该对象存储指向较大图像中补丁位置的指针,但仅在调用时将数据作为一维 numpy 数组返回(然后在不使用时再次删除)但我没有遇到足够的关于子类化 ndarray 对象的详细信息。如果唯一的解决方案是用 C/C++ 实现所有东西,我会非常失望。感谢您提供任何帮助,谢谢!

【问题讨论】:

  • 拥有矢量化图像数据也需要在 scipy 中使用 kdtree

标签: python memory-management image-processing tree numpy


【解决方案1】:

根据您的问题,您可能已经知道这一切。但是,我发布这个“答案”更多是为了讨论问题是什么,因为很多人可能不知道它们......

如果不是,您可以从 100x100x100 图像创建一个 96x96x96x5x5x5 数组,充当5x5x5 移动窗口,而无需分配任何额外的内存。

但是,由于每个维度只能有一个步幅,因此无法在不复制的情况下将其重塑为 96x96x96x125 数组。

无论如何,这是一个例子(基本上是straight from one of my previous answers):

import numpy as np

def rolling_window_lastaxis(a, window):
    """Directly taken from Erik Rigtorp's post to numpy-discussion.
    <http://www.mail-archive.com/numpy-discussion@scipy.org/msg29450.html>"""
    if window < 1:
       raise ValueError, "`window` must be at least 1."
    if window > a.shape[-1]:
       raise ValueError, "`window` is too long."
    shape = a.shape[:-1] + (a.shape[-1] - window + 1, window)
    strides = a.strides + (a.strides[-1],)
    return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)

def rolling_window(a, window):
    """Takes a numpy array *a* and a sequence of (or single) *window* lengths
    and returns a view of *a* that represents a moving window."""
    if not hasattr(window, '__iter__'):
        return rolling_window_lastaxis(a, window)
    for i, win in enumerate(window):
        if win > 1:
            a = a.swapaxes(i, -1)
            a = rolling_window_lastaxis(a, win)
            a = a.swapaxes(-2, i)
    return a

x = np.zeros((100,100,100), dtype=np.uint8)
y = rolling_window(x, (5,5,5))
print 'Now *y* will be a 96x96x96x5x5x5 array...'
print y.shape
print 'Representing a "rolling window" into *x*...'
y[0,0,0,...] = 1
y[1,1,0,...] = 2
print x[:10,:10,0] # Note that *x* and *y* share the same memory!

这会产生:

Now *y* will be a 96x96x96x5x5x5 array...
(96, 96, 96, 5, 5, 5)
Representing a "rolling window" into *x*...
[[1 1 1 1 1 0 0 0 0 0]
 [1 2 2 2 2 2 0 0 0 0]
 [1 2 2 2 2 2 0 0 0 0]
 [1 2 2 2 2 2 0 0 0 0]
 [1 2 2 2 2 2 0 0 0 0]
 [0 2 2 2 2 2 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]]

但是,正如您已经指出的,我们无法在不创建副本的情况下将其改造成 96x96x96x125y.shape = (96,96,96,-1) 会引发错误,z = y.reshape((96,96,96,-1)) 会起作用,但会返回一个副本。

(如果这看起来令人困惑,相关文档在numpy.reshape 中。基本上reshape 将尽可能避免复制,如果不是则返回副本,而设置shape 属性将在复制时引发错误'不可能。)

但是,即使您构建了更高效的数组容器,sklearn.neighbors.BallTree 也几乎肯定会制作临时中间副本。

你提到你正在做图像分割。为什么不研究一种比您似乎正在尝试的“蛮力”更有效的算法呢? (或者如果这不可行,请告诉我们更多细节,说明原因……也许有人会有更好的主意?)

【讨论】:

  • 这种基于补丁的分割方法基于here 所描述的方法,该方法已被证明比其他“更有效”的分割算法提供更好的结果。但是我希望在更大的范围内尝试这个想法,但是如果使用 python/numpy,我会被内存要求所禁止。如果有办法解决这个问题而不必重新实现东西,那就太好了。
猜你喜欢
  • 1970-01-01
  • 2015-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-11
  • 2015-11-08
  • 1970-01-01
  • 2016-07-14
相关资源
最近更新 更多