Numpy View重塑没有复制(2d移动/滑动窗口,跨步,蒙面内存结构)

loc*_*mal 7 python numpy image scikit-learn scikit-image

我有一个图像存储为2d numpy数组(可能是multi-d).

我可以在反射2d滑动窗口的数组上进行视图,但是当我重塑它以使每行都是一个平坦的窗口(行是窗口,列是该窗口中的一个像素)时,python会生成一个完整的副本.它这样做是因为我使用了典型的步幅技巧,并且新形状在内存中不连续.

我需要这个,因为我将整个大图像传递给sklearn分类器,该分类器接受2d矩阵,其中没有批处理/部分拟合程序,并且完整的扩展副本对于存储器来说太大了.

我的问题:如果没有完整地复制视图,有没有办法做到这一点?

我相信一个答案将是(1)关于我忽略的步幅或numpy内存管理,或者(2)python的某种屏蔽内存结构,它可以模拟一个numpy数组,甚至包括像sklearn这样的外部包用Cython.

这种在内存中移动二维图像窗口的训练任务很常见,但我所知道的唯一一个直接考虑补丁的尝试是Vigra项目(http://ukoethe.github.io/vigra/).

谢谢您的帮助.

>>> A=np.arange(9).reshape(3,3)
>>> print A
[[0 1 2]
 [3 4 5]
 [6 7 8]]
>>> xstep=1;ystep=1; xsize=2; ysize=2
>>> window_view = np.lib.stride_tricks.as_strided(A, ((A.shape[0] - xsize + 1) / xstep, (A.shape[1] - ysize + 1) / ystep, xsize, ysize),
...       (A.strides[0] * xstep, A.strides[1] * ystep, A.strides[0], A.strides[1]))
>>> print window_view 
[[[[0 1]
   [3 4]]

  [[1 2]
   [4 5]]]


 [[[3 4]
   [6 7]]

  [[4 5]
   [7 8]]]]
>>> 
>>> np.may_share_memory(A,window_view)
True
>>> B=window_view.reshape(-1,xsize*ysize)
>>> np.may_share_memory(A,B)
False
Run Code Online (Sandbox Code Playgroud)

jas*_*rim 4

仅使用步幅无法完成您的任务,但 NumPy 确实支持一种可以完成这项工作的数组。通过跨步,masked_array 您可以创建所需的数据视图。然而,并非所有 NumPy 函数都支持 操作masked_array,因此 scikit-learn 也可能无法很好地处理这些操作。

让我们首先重新审视一下我们在这里尝试做的事情。考虑示例的输入数据。从根本上来说,数据只是内存中的一维数组,如果我们考虑一下它的跨度,那就更简单了。该数组看起来只是二维的,因为我们已经定义了它的形状。使用步幅,形状可以定义如下:

from numpy.lib.stride_tricks import as_strided

base = np.arange(9)
isize = base.itemsize
A = as_strided(base, shape=(3, 3), strides=(3 * isize, isize))
Run Code Online (Sandbox Code Playgroud)

现在的目标是设置这样的步幅,base使其对数字进行排序,如最终数组 中那样B。换句话说,我们要求的是整数ab这样

>>> as_strided(base, shape=(4, 4), strides=(a, b))
array([[0, 1, 3, 4],
       [1, 2, 4, 5],
       [3, 4, 6, 7],
       [4, 5, 7, 8]])
Run Code Online (Sandbox Code Playgroud)

但这显然是不可能的。我们可以实现的最接近的视图是使用滚动窗口base

>>> C = as_strided(base, shape=(5, 5), strides=(isize, isize))
>>> C
array([[0, 1, 2, 3, 4],
       [1, 2, 3, 4, 5],
       [2, 3, 4, 5, 6],
       [3, 4, 5, 6, 7],
       [4, 5, 6, 7, 8]])
Run Code Online (Sandbox Code Playgroud)

但这里的区别在于我们有多余的列和行,我们希望将其删除。因此,实际上我们要求的是一个不连续且定期跳跃的滚动窗口。在此示例中,我们希望将每三个项目从窗口中排除,并跳过两行后的一项。

我们可以将其描述为masked_array

>>> mask = np.zeros((5, 5), dtype=bool)
>>> mask[2, :] = True
>>> mask[:, 2] = True
>>> D = np.ma.masked_array(C, mask=mask)
Run Code Online (Sandbox Code Playgroud)

这个数组正是包含我们想要的数据,它只是原始数据的一个视图。我们可以确认数据是相等的

>>> D.data[~D.mask].reshape(4, 4)
array([[0, 1, 3, 4],
       [1, 2, 4, 5],
       [3, 4, 6, 7],
       [4, 5, 7, 8]])
Run Code Online (Sandbox Code Playgroud)

但正如我在一开始所说的,scikit-learn 很可能不理解屏蔽数组。如果只是将其转换为数组,则数据将是错误的:

>>> np.array(D)
array([[0, 1, 2, 3, 4],
       [1, 2, 3, 4, 5],
       [2, 3, 4, 5, 6],
       [3, 4, 5, 6, 7],
       [4, 5, 6, 7, 8]])
Run Code Online (Sandbox Code Playgroud)