2013-05-31 226 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))