2013-05-31 19 views
32

Czy istnieje sposób na podzielenie tablicy 2d na numpy na mniejsze tablice 2d?Cięcie tablicy 2d na mniejsze tablice 2d

Przykład

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

Więc w zasadzie chcą wyciąć macierz 2x4 na 2 macierzy 2x2. Poszukuje ogólnego rozwiązania do wykorzystania na obrazach.

Odpowiedz

42

Powinieneś być w stanie przebić swoją tablicę w "blokach" Korzystanie z jakąś kombinację reshape i swapaxes:

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

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

do

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

Opublikowałem inverse function, unblockshaped, here i N-wymiarową generalizację here. Uogólnienie daje trochę więcej wglądu w rozumowanie tego algorytmu.


Należy pamiętać, że istnieje również superbatfish's blockwise_view. Układa bloki w innym formacie (za pomocą większej liczby osi), ale ma tę zaletę: (1) zawsze zwraca widok i (2) jest w stanie przekazać tablice o dowolnym wymiarze .

+0

Czy można uczynić go bardziej ogólnym, aby rozmiary bloków były zmienne? (pod warunkiem, że bloki idealnie pasują do oryginalnej tablicy) – TheMeaningfulEngineer

+0

Dzięki za edycję. Czy mógłbyś wyjaśnić przyczyny algorytmu? – TheMeaningfulEngineer

+2

Kilka miesięcy temu [jeszcze jedno pytanie] (http://stackoverflow.com/a/13990648/190597) wpadło mi na pomysł użycia 'reshape' i' swapaxes'. 'H // nrows' ma sens, ponieważ utrzymuje on razem wiersze pierwszego bloku. Ma również sens, że będziesz potrzebować 'nrows' i' ncols' jako części kształtu. '-1' mówi, że zmiana kształtu wymaga podania liczby, która jest konieczna do zmiany kształtu. Uzbrojony w formę rozwiązania, po prostu próbowałem rzeczy, dopóki nie znalazłem formuły, która działa. Przepraszam, że nie mam dla ciebie bardziej wnikliwego wyjaśnienia. – unutbu

1

Na razie działa to tylko wtedy, gdy duża tablica 2d może być idealnie pokrojona na podobszary o podobnej wielkości.

Kodeksu mieszkowe plastry

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

do tego

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 określenia rozmiaru bloku

kod

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

Wydaje mi się, że jest to zadanie dla numpy.split lub jakiegoś wariantu.

np.

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) 

Jeśli masz zdjęcie NxN można utworzyć, na przykład listę 2 NxN/2 wyświetlać osobno, a następnie podzielić je wzdłuż drugiej osi. Dostępne są również:

numpy.hsplit i numpy.vsplit.

5

Jest kilka innych odpowiedzi, które wydają się już dobrze pasować do konkretnego przypadku, ale twoje pytanie wzbudziło moje zainteresowanie w możliwości wykorzystania wydajnego pod względem pamięci rozwiązania do maksymalnej liczby wymiarów obsługiwanych przez numpy, a ja zakończyłem spędzanie większości popołudnia nadejściem z możliwą metodą.(Sama metoda jest stosunkowo prosta, po prostu nadal nie używałem większości naprawdę ciekawych funkcji, które były obsługiwane przez większość czasu, więc większość czasu spędziłem na badaniu, aby sprawdzić, co numpy ma do dyspozycji i ile może zrobić, Trzeba to zrobić.)

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

Pytanie practically the same as this one. Można użyć jednego-liner z np.ndindex() i 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) 

Aby utworzyć wynik chcesz:

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

Jeśli chcesz rozwiązanie, które również obsługuje przypadki, gdy matryca jest nie równo podzielony, możesz użyć tego:

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

Oto rozwiązanie oparte na odpowiedzi unutbu, które obsługuje przypadek, w którym macierz nie może być jednakowa podzielony. W takim przypadku zmieni rozmiar macierzy przed użyciem jakiejś interpolacji. Do tego potrzebny jest OpenCV. Zauważ, że musiałem zamienić ncols i nrows, aby działało, nie domyśliłem się dlaczego.

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