2012-03-01 55 views
2

我试图创建一个列表,其中每个子数组需要被展平的大小相同的所有重叠子阵列的列表(基于分片的分割)作为一维矢量),所以我可以利用sklearn.neighbours.BallTree中的球树。例如,如果给定一个100x100x100的图像,如果我将它分解为5x5x5重叠的补丁(子阵列),那么我将拥有96x96x96 = 884,736个图像。Numpy - 创建重叠的3D子阵列作为具有高效内存的向量

但是我还没有找到任何方式这样做没有numpy分配更多的内存为每个flattened/vectorized子阵列。这似乎是因为每个子阵列在内存中不是连续的。

例如对于100x100x100的图像,如果我想将每个5x5x5补丁作为一维向量(长度为125),numpy决定在内存中为所有884,736个分配一个全新的阵列,然后它变得相当大,特别是如果我想要处理超过一个100x100x100的图像!

我会欢迎在Python/numpy中克服这种内存挑战的任何解决方案。我正在考虑创建一个numpy.ndarray对象的子类,该对象在更大的图像中存储指向修补程序位置的指针,但仅在调用时才将数据作为一维numpy数组返回(并在不使用时再次删除它)但是我还没有得到足够的关于子类化ndarray对象的细节。如果唯一的解决方案是用C/C++来实现所有的东西,我会非常失望。我很感激可以提供的任何帮助,谢谢!

+0

也需要矢量化图像数据才能在scipy中使用kdtree – Zehan 2012-03-01 14:46:28

回答

1

根据你的问题,你可能已经意识到所有这些。然而,我发布这个“答案”作为更多的讨论是什么问题,因为很多人可能不知道他们......

如果你不是,但你可以做一个96x96x96x5x5x5数组,来自100x100x100图像,充当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/[email protected]/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]] 

然而,正如你已经注意到,我们不能这样来重塑96x96x96x125而不创建副本。 y.shape = (96,96,96,-1)将引发错误,并且z = y.reshape((96,96,96,-1))将起作用,但会返回副本。

(相关文件是在numpy.reshape,如果这样还是很迷惑,基本上reshape将避免可能的复制时,如果它不是返回副本,而设置shape属性时,复制是不可能会引发错误。)

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

你提到你正在做图像分割。为什么不研究一个更有效的算法,而不是你似乎正在尝试的相当“蛮力”? (或者如果这不可行,请给我们更多的细节,为什么......也许有人会有更好的主意?)

+0

这种基于补丁的分割方法基于[这里]描述的方法(http://www.sciencedirect.com/science/article/pii/S1053811910011997),这已被证明比其他“更高效”的分割算法有更好的结果。但是我希望在更大范围内尝试这个想法,但是如果使用python/numpy,它会被内存要求所禁止。如果没有必要重新实现这些东西的话,这会很好。 – Zehan 2012-03-02 11:14:37