2013-05-31 12 views
32

Gibt es eine Möglichkeit, ein 2d Array in numpy in kleinere 2d Arrays zu schneiden?Slice 2d Array in kleinere 2d Arrays

Beispiel

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

So möchte ich ein 2x4-Array in zwei 2x2-Arrays abgeholzt grundsätzlich zu. Suche nach einer generischen Lösung für Bilder.

Antwort

42

Sie sollten in der Lage sein, Ihr Array zu brechen in "Blöcke" mit einer Kombination von reshape und 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)) 

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

in

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

Ich habe eine inverse function, unblockshaped, here, und eine N-dimensionale Verallgemeinerung here geschrieben. Die Verallgemeinerung gibt ein wenig mehr Einblick in die Überlegungen hinter diesem Algorithmus.


Beachten Sie, dass es auch superbatfish's blockwise_view gibt. Es ordnet die Blöcke in einem anderen Format an (unter Verwendung von mehr Achsen), aber es hat den Vorteil, dass (1) immer eine Ansicht zurückgibt und (2) in der Lage ist, Arrays einer beliebigen Dimension zu übergeben.

+0

Könnten Sie es generischer machen, so dass Blockgrößen Variablen sind? (mit der Bedingung, dass Blöcke perfekt in das ursprüngliche Array passen) – TheMeaningfulEngineer

+0

Danke für die Bearbeitung. Könnten Sie bitte die Gründe für den Algorithmus erklären? – TheMeaningfulEngineer

+2

Vor ein paar Monaten gab es [eine andere Frage] (http://stackoverflow.com/a/13990648/190597), die mich auf die Idee der Verwendung von "Umformen" und "Swapaxes" hindeutete. Die 'h // nrows' sind sinnvoll, da dadurch die Zeilen des ersten Blocks zusammengehalten werden. Es macht auch Sinn, dass du 'nrows' und' ncols' brauchst, um Teil der Form zu sein. "-1" sagt Umformen, um die Zahl einzufügen, die notwendig ist, um die Umformung gültig zu machen. Bewaffnet mit der Form der Lösung habe ich einfach Dinge ausprobiert, bis ich die Formel gefunden habe, die funktioniert. Es tut mir leid, dass ich keine aufschlussreichere Erklärung für dich habe. – unutbu

1

Jetzt funktioniert es nur, wenn das große 2d-Array perfekt in gleich große Sub-Arrays geschnitten werden kann.

Der Code unten Scheiben

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

in diesem

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 bestimmen die Blockgröße

-Code

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

Es scheint mir, dass dies eine Aufgabe für numpy.split oder eine Variante ist.

z.B.

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) 

Wenn Sie ein NxN Bild haben, können Sie, zum Beispiel erstellen, um eine Liste von 2 NxN/2 Unter-Bildern, und dann entlang der anderen Achse teilen.

numpy.hsplit und numpy.vsplit sind ebenfalls verfügbar.

5

Es gibt einige andere Antworten, die bereits für Ihren speziellen Fall gut geeignet scheinen, aber Ihre Frage hat mein Interesse an der Möglichkeit einer speichereffizienten Lösung bis zu der maximalen Anzahl an Dimensionen, die numpy unterstützen, geweckt und ich endete den größten Teil des Nachmittags mit der möglichen Methode verbringen.(Die Methode selbst ist relativ einfach, es ist nur so, dass ich immer noch nicht die meisten der fantastischen Funktionen verwendet habe, die numpy unterstützt, also wurde die meiste Zeit damit verbracht zu recherchieren, um zu sehen, was numpy zur Verfügung hatte und wie viel es tun konnte ‚t haben, es zu tun.)

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

Sie fragen practically the same as this one. Sie können die Einzeiler mit np.ndindex() verwenden und 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) 

Um das Ergebnis zu erstellen Sie wollen:

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

Wenn Sie eine Lösung wollen, die auch die Fälle behandelt, wenn die Matrix nicht in gleichem Maße aufgeteilt, können Sie diese verwenden:

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

Hier ist eine Lösung auf unutbu Antwort basierte, den Fall behandeln, wo Matrix nicht gleichmäßig sein kann geteilt. In diesem Fall wird die Größe der Matrix geändert, bevor eine Interpolation verwendet wird. Sie benötigen dazu OpenCV. Beachten Sie, dass ich ncols und nrows austauschen musste, damit es funktioniert, warum nicht.

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