Cortar matriz 2d en matrices 2d más pequeñas

¿Hay alguna manera de dividir una matriz 2D en números en matrices 2D más pequeñas?

Ejemplo

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

Así que básicamente quiero cortar una matriz de 2×4 en 2 matrices de 2×2. Buscando una solución genérica para ser usada en imágenes.

Debería poder dividir su matriz en “bloques” utilizando alguna combinación de reshape y 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)) 

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

dentro

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

He publicado una función inversa, sin forma de unblockshaped , aquí , y una generalización N-dimensional aquí . La generalización da un poco más de información sobre el razonamiento detrás de este algoritmo.


Tenga en cuenta que también hay una vista en blockwise_view de blockwise_view . Organiza los bloques en un formato diferente (con más ejes), pero tiene la ventaja de que (1) siempre devuelve una vista y (2) es capaz de administrar matrices de cualquier dimensión.

Ya hay algunas otras respuestas que parecen adecuadas para su caso específico, pero su pregunta despertó mi interés en la posibilidad de una solución eficiente en memoria que se pueda utilizar hasta el número máximo de dimensiones que Numpy admite, y terminé gastando la mayor parte del tiempo. La tarde viene con un posible método. (El método en sí es relativamente simple, es solo que todavía no he usado la mayoría de las funciones realmente elegantes que soporta Numpy, por lo que la mayor parte del tiempo lo pasé investigando para ver qué número tenía disponible y cuánto podía hacer para no hacerlo. No hay que hacerlo.)

 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] 

Me parece que esta es una tarea para numpy.split o alguna variante.

p.ej

 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) 

Si tiene una imagen NxN, puede crear, por ejemplo, una lista de 2 subimágenes NxN / 2, y luego dividirlas a lo largo del otro eje.

numpy.hsplit y numpy.vsplit también están disponibles.

Si desea una solución que también maneje los casos en los que la matriz no está dividida por igual, puede usar esto:

 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) 

Por ahora, solo funciona cuando el gran arreglo 2D se puede dividir perfectamente en subarreglos de igual tamaño.

El código de abajo rebanadas

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

dentro de esto

 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 determinar el tamaño del bloque

Código

 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) 

Cuestionas prácticamente lo mismo que este . Puede usar el one-liner con np.ndindex() y 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) 

Para crear el resultado que desea:

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

Aquí hay una solución basada en la respuesta de unutbu que trata el caso donde la matriz no se puede dividir por igual. En este caso, cambiará el tamaño de la matriz antes de usar alguna interpolación. Necesitas OpenCV para esto. Tenga en cuenta que tuve que intercambiar ncols y nrows para que funcionara, no se me ocurrió por qué.

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