2013-05-31 45 views
32

Có cách nào để cắt mảng 2d thành các mảng 2d nhỏ hơn không?Mảng 2d cắt thành các mảng 2d nhỏ hơn

Ví dụ

[[1,2,3,4], -> [[1,2] [3,4] 
[5,6,7,8]]   [5,6] [7,8]] 

Vì vậy, tôi về cơ bản muốn cắt xuống một mảng 2x4 thành 2 mảng 2x2. Tìm kiếm một giải pháp chung để được sử dụng trên hình ảnh.

Trả lời

42

Bạn sẽ có thể phá vỡ mảng của bạn thành "khối" sử dụng một số sự kết hợp của 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)) 

biến 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]] 

vào

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]]] 

Tôi đã đăng một số inverse function, unblockshaped, here và tổng quát hóa N-here. Việc khái quát hóa cung cấp một cái nhìn sâu sắc hơn vào lý do đằng sau thuật toán này.


Lưu ý rằng cũng có superbatfish's blockwise_view. Nó sắp xếp các khối ở định dạng khác (sử dụng nhiều trục) nhưng nó có lợi thế là (1) luôn trả về một khung nhìn và (2) có khả năng bàn giao mảng bất kỳ kích thước nào.

+0

Bạn có thể làm cho nó chung chung hơn, do đó kích thước khối là các biến? (với điều kiện chặn hoàn toàn phù hợp trong mảng ban đầu) – TheMeaningfulEngineer

+0

Cảm ơn bạn đã chỉnh sửa. Bạn có thể giải thích lý do đằng sau thuật toán không? – TheMeaningfulEngineer

+2

Đã có [một câu hỏi khác] (http://stackoverflow.com/a/13990648/190597) một vài tháng trước đây đã đưa tôi vào ý tưởng sử dụng 'reshape' và' swapaxes'. 'H // nrows' có ý nghĩa vì nó giữ các hàng của khối đầu tiên với nhau. Nó cũng có nghĩa là bạn sẽ cần 'nrows' và' ncols' để trở thành một phần của hình dạng. '-1' cho biết định dạng lại để điền vào bất kỳ số nào là cần thiết để làm cho định dạng lại hợp lệ. Được trang bị các hình thức của giải pháp, tôi chỉ thử những thứ cho đến khi tôi tìm thấy công thức hoạt động. Tôi xin lỗi tôi không có một lời giải thích sâu sắc hơn cho bạn. – unutbu

1

Hiện tại, nó chỉ hoạt động khi mảng 2d lớn có thể được cắt hoàn toàn thành các mảng con có kích thước bằng nhau.

Mã dưới lát

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]]) 

vào này

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]]]) 

p ang q xác định kích thước khối

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

Dường như với tôi rằng đây là một nhiệm vụ cho numpy.split hoặc một số biến thể.

ví dụ:

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) 

Nếu bạn có hình ảnh NxN bạn có thể tạo, ví dụ: danh sách 2 ảnh phụ NxN/2, sau đó chia chúng dọc theo trục kia.

numpy.hsplitnumpy.vsplit cũng khả dụng.

5

Có một số câu trả lời khác có vẻ phù hợp với trường hợp cụ thể của bạn, nhưng câu hỏi của bạn đã khơi gợi sự quan tâm của tôi về khả năng giải pháp hiệu quả bộ nhớ có thể sử dụng tối đa số thứ nguyên hỗ trợ gumpy và tôi đã kết thúc chi tiêu hầu hết các buổi chiều đến với phương pháp có thể.(Phương pháp này tương đối đơn giản, chỉ là tôi vẫn chưa sử dụng hầu hết các tính năng thực sự ưa thích mà hỗ trợ gumpy nên phần lớn thời gian đã được nghiên cứu để xem những gì có sẵn và bao nhiêu nó có thể làm như vậy mà tôi đã không 't phải làm điều đó.)

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

Bạn có câu hỏi practically the same as this one. Bạn có thể sử dụng một lớp lót với 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) 

Để tạo kết quả bạn muốn:

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

Nếu bạn muốn có một giải pháp mà cũng xử lý các trường hợp khi ma trận là không bằng nhau chia, bạn có thể sử dụng điều này:

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

Đây là giải pháp dựa trên câu trả lời của unutbu xử lý trường hợp ma trận không thể bằng nhau chia. Trong trường hợp này, nó sẽ thay đổi kích thước ma trận trước khi sử dụng một số nội suy. Bạn cần OpenCV cho việc này. Lưu ý rằng tôi đã phải trao đổi ncolsnrows để làm cho nó hoạt động, không có lý do tại sao.

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)) 
Các vấn đề liên quan