2013-05-31 123 views

回答

42

你应该能够打破你的数组转换成 “块” 使用的reshapeswapaxes一些组合:

import numpy as np 
def blockshaped(arr, nrows, ncols): 
    """ 
    Return an array of shape (n, nrows, ncols) where 
    n * nrows * ncols = arr.size 

    If arr is a 2D array, the returned array should look like n subblocks with 
    each subblock preserving the "physical" layout of arr. 
    """ 
    h, w = arr.shape 
    return (arr.reshape(h//nrows, nrows, -1, ncols) 
       .swapaxes(1,2) 
       .reshape(-1, nrows, ncols)) 

c

c = np.arange(24).reshape((4,6)) 
print(c) 
# [[ 0 1 2 3 4 5] 
# [ 6 7 8 9 10 11] 
# [12 13 14 15 16 17] 
# [18 19 20 21 22 23]] 

print(blockshaped(c, 2, 3)) 
# [[[ 0 1 2] 
# [ 6 7 8]] 

# [[ 3 4 5] 
# [ 9 10 11]] 

# [[12 13 14] 
# [18 19 20]] 

# [[15 16 17] 
# [21 22 23]]] 

我已发布inverse function, unblockshaped, here,以及N维概括here。泛化提供了对该算法背后原因的更多了解。


请注意,也有superbatfish's blockwise_view。它以不同的格式(使用更多的轴)排列 块,但它的优点是(1) 总是返回视图和(2)能够处理任何维度的数组。

+0

你能否使它更通用,以便块大小是变量? (条件是块完全适合原始数组) – TheMeaningfulEngineer

+0

感谢您的编辑。请您解释算法背后的推理吗? – TheMeaningfulEngineer

+2

几个月前有[另一个问题](http://stackoverflow.com/a/13990648/190597)提到了使用'reshape'和'swapaxes'的想法。 'h // nrows'是有意义的,因为这会将第一个块的行保留在一起。这也是有道理的,你需要'nrows'和'ncols'成为形状的一部分。 '-1'告诉重塑以填写任何必要的数字以使重塑有效。以解决方案的形式武装起来,我只是尝试了一些东西,直到找到可行的公式。对不起,我对你没有更深入的解释。 – unutbu

1

现在,它只是当大2d阵列可以完美切成相同大小的子阵时工作。

代码波纹管切片

a ->array([[ 0, 1, 2, 3, 4, 5], 
      [ 6, 7, 8, 9, 10, 11], 
      [12, 13, 14, 15, 16, 17], 
      [18, 19, 20, 21, 22, 23]]) 

到此

block_array-> 
    array([[[ 0, 1, 2], 
      [ 6, 7, 8]], 

      [[ 3, 4, 5], 
      [ 9, 10, 11]], 

      [[12, 13, 14], 
      [18, 19, 20]], 

      [[15, 16, 17], 
      [21, 22, 23]]]) 

pq确定块大小

代码

a = arange(24) 
a = a.reshape((4,6)) 
m = a.shape[0] #image row size 
n = a.shape[1] #image column size 

p = 2  #block row size 
q = 3  #block column size 

block_array = [] 
previous_row = 0 
for row_block in range(blocks_per_row): 
    previous_row = row_block * p 
    previous_column = 0 
    for column_block in range(blocks_per_column): 
     previous_column = column_block * q 
     block = a[previous_row:previous_row+p,previous_column:previous_column+q] 
     block_array.append(block) 

block_array = array(block_array) 
5

在我看来,这是numpy.split或某个变体的任务。

例如

a = np.arange(30).reshape([5,6]) #a.shape = (5,6) 
a1 = np.split(a,3,axis=1) 
#'a1' is a list of 3 arrays of shape (5,2) 
a2 = np.split(a, [2,4]) 
#'a2' is a list of three arrays of shape (2,5), (2,5), (1,5) 

如果有可以创建,例如,为2的N×N/2子图像的列表,然后沿另一个轴将它们划分NxN的图像。

numpy.hsplitnumpy.vsplit也可用。

5

还有一些其他答案似乎已经非常适合您的特定情况,但您的问题激起了我对可以使用高达numpy支持的最大维数的内存高效解决方案的兴趣,并且我结束了大部分时间都花在了可能的方法上。(该方法本身相对简单,只是我还没有使用numpy支持的大多数真正奇特的功能,因此大部分时间都花在研究上以查看numpy可用的数量以及它可以做多少,以使我没有不必这样做。)

def blockgen(array, bpa): 
    """Creates a generator that yields multidimensional blocks from the given 
array(_like); bpa is an array_like consisting of the number of blocks per axis 
(minimum of 1, must be a divisor of the corresponding axis size of array). As 
the blocks are selected using normal numpy slicing, they will be views rather 
than copies; this is good for very large multidimensional arrays that are being 
blocked, and for very large blocks, but it also means that the result must be 
copied if it is to be modified (unless modifying the original data as well is 
intended).""" 
    bpa = np.asarray(bpa) # in case bpa wasn't already an ndarray 

    # parameter checking 
    if array.ndim != bpa.size:   # bpa doesn't match array dimensionality 
     raise ValueError("Size of bpa must be equal to the array dimensionality.") 
    if (bpa.dtype != np.int   # bpa must be all integers 
     or (bpa < 1).any()    # all values in bpa must be >= 1 
     or (array.shape % bpa).any()): # % != 0 means not evenly divisible 
     raise ValueError("bpa ({0}) must consist of nonzero positive integers " 
         "that evenly divide the corresponding array axis " 
         "size".format(bpa)) 


    # generate block edge indices 
    rgen = (np.r_[:array.shape[i]+1:array.shape[i]//blk_n] 
      for i, blk_n in enumerate(bpa)) 

    # build slice sequences for each axis (unfortunately broadcasting 
    # can't be used to make the items easy to operate over 
    c = [[np.s_[i:j] for i, j in zip(r[:-1], r[1:])] for r in rgen] 

    # Now to get the blocks; this is slightly less efficient than it could be 
    # because numpy doesn't like jagged arrays and I didn't feel like writing 
    # a ufunc for it. 
    for idxs in np.ndindex(*bpa): 
     blockbounds = tuple(c[j][idxs[j]] for j in range(bpa.size)) 

     yield array[blockbounds] 
1

您提问practically the same as this one。您可以使用一个衬垫与np.ndindex()reshape()

def cutter(a, r, c): 
    lenr = a.shape[0]/r 
    lenc = a.shape[1]/c 
    np.array([a[i*r:(i+1)*r,j*c:(j+1)*c] for (i,j) in np.ndindex(lenr,lenc)]).reshape(lenr,lenc,r,c) 

创建结果你想要的:

a = np.arange(1,9).reshape(2,1) 
#array([[1, 2, 3, 4], 
#  [5, 6, 7, 8]]) 

cutter(a, 1, 2) 
#array([[[[1, 2]], 
#  [[3, 4]]], 
#  [[[5, 6]], 
#  [[7, 8]]]]) 
2

如果你想有一个解决方案,同时处理情况下,当基质是 不是同样划分,你可以用这个:

from operator import add 
half_split = np.array_split(input, 2) 

res = map(lambda x: np.array_split(x, 2, axis=1), half_split) 
res = reduce(add, res) 
0

这里是一个解决方案基于unutbu的答案处理情况矩阵不能平等 分为。在这种情况下,它将在使用一些插值之前调整矩阵的大小。你需要OpenCV。请注意,我必须交换ncolsnrows才能使其工作,但没有想到为什么。

import numpy as np 
import cv2 
import math 

def blockshaped(arr, r_nbrs, c_nbrs, interp=cv2.INTER_LINEAR): 
    """ 
    arr  a 2D array, typically an image 
    r_nbrs numbers of rows 
    r_cols numbers of cols 
    """ 

    arr_h, arr_w = arr.shape 

    size_w = int(math.floor(arr_w // c_nbrs) * c_nbrs) 
    size_h = int(math.floor(arr_h // r_nbrs) * r_nbrs) 

    if size_w != arr_w or size_h != arr_h: 
     arr = cv2.resize(arr, (size_w, size_h), interpolation=interp) 

    nrows = int(size_w // r_nbrs) 
    ncols = int(size_h // c_nbrs) 

    return (arr.reshape(r_nbrs, ncols, -1, nrows) 
       .swapaxes(1,2) 
       .reshape(-1, ncols, nrows))