2013-05-31 4 views
32

Есть ли способ разрезать 2d-массив в numpy на меньшие 2d массивы?Slice 2d массив в меньшие 2d массивы

Пример

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

Так что я в основном хотят, чтобы сократить массив 2х4 в 2 2х2 массивов. Ищете универсальное решение для использования на изображениях.

ответ

42

Вы должны быть в состоянии разбить массив на "блоки", используя некоторую комбинацию reshape и 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)) 

превращает 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' сообщает reshape, чтобы заполнить любое число, необходимое для того, чтобы сделать reshape действительной. Вооружившись формой решения, я просто пробовал вещи, пока не нашел формулу, которая работает. Извините, у меня нет более глубокого объяснения для вас. – 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]]]) 

p анг q определяют размер блока

Код

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) 

Если у вас есть NxN изображение, которое вы можете создать, например, список 2 NxN/2 подизображений, а затем разделить их вдоль другой оси.

numpy.hsplit и numpy.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. Заметьте, что мне пришлось поменять ncols и nrows, чтобы они работали, не поняли почему.

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)) 
Смежные вопросы