我正在尝试从一个更大的 3D 数组(用于基于补丁的分割)创建一个包含所有相同大小的重叠子数组的列表,其中每个子数组都需要展平(作为一维向量),因此我可以利用sklearn.neighbours.BallTree 中的球树。
因此,例如,给定一张 100x100x100 的图像,如果我将其分解为 5x5x5 的重叠 block (子阵列),我将有 96x96x96 = 884,736 个。
但是,如果没有 numpy 为每个展平/矢量化子数组分配更多内存,我还没有找到任何这样做的方法。这似乎是因为每个子数组在内存中不连续。
例如对于 100x100x100 图像,如果我希望每个 5x5x5 补丁作为一维向量(长度为 125),numpy 决定在内存中为所有 884,736 个分配一个全新的数组,然后它变得相当大,特别是如果我想处理超过一张 100x100x100 的图片!
我欢迎在 python/numpy 中克服这一内存挑战的任何解决方案。我正在考虑创建 numpy.ndarray 对象的子类,该对象存储指向大图像中补丁位置的指针,但仅在调用时将数据作为一维 numpy 数组返回(然后在不使用时再次删除)但是我还没有找到足够的关于子类化 ndarray 对象的细节来做到这一点。如果唯一的解决方案是用 C/C++ 实现所有内容,我会非常失望。感谢您提供的任何帮助,谢谢!
请您参考如下方法:
根据您的问题,您可能已经知道所有这些。然而,我发布这个“答案”更多是为了讨论问题是什么,因为很多人可能没有意识到它们......
如果您不是,您可以从 100x100x100 图像制作一个 96x96x96x5x5x5 数组,充当 5x5x5 移动窗口,无需分配任何额外的内存。
但是,因为每个维度只能有一个步幅,所以如果不制作副本,就无法将其 reshape 为 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]]
但是,正如您已经注意到的,我们无法在不创建副本的情况下将其 reshape 为 96x96x96x125。 y.shape = (96,96,96,-1) 会报错,z = y.reshape((96,96,96,-1)) 会工作,但会返回一个副本。
(相关文档在 numpy.reshape 中,如果这看起来令人困惑。基本上 reshape 将尽可能避免复制,如果不是则返回一个副本,而设置shape 属性将在无法复制时引发错误。)
但是,即使您构建了一个更高效的数组容器,sklearn.neighbors.BallTree 几乎肯定会制作临时中间副本。
您提到您正在进行图像分割。为什么不研究一种比您似乎正在尝试的相当“蛮力”更有效的算法呢? (或者,如果这不可行,请向我们提供更多详细信息,说明原因……也许有人会有更好的主意?)
