将2d阵列切成较小的2d阵列

时间:2023-01-21 21:30:48

Is there a way to slice a 2d array in numpy into smaller 2d arrays?

有没有办法将ndy中的2d数组切成较小的2d数组?

Example

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

So I basically want to cut down a 2x4 array into 2 2x2 arrays. Looking for a generic solution to be used on images.

所以我基本上想要将2x4阵列减少为2个2x2阵列。寻找用于图像的通用解决方案。

7 个解决方案

#1


52  

You should be able to break your array into "blocks" using some combination of reshape and swapaxes:

你应该能够使用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))

turns c

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

into

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

I've posted an inverse function, unblockshaped, here, and an N-dimensional generalization here. The generalization gives a little more insight into the reasoning behind this algorithm.

我在这里发布了一个反函数,unblockshaped,这里和一个N维泛化。这种推广可以更深入地了解该算法背后的推理。


Note that there is also superbatfish's blockwise_view. It arranges the blocks in a different format (using more axes) but it has the advantage of (1) always returning a view and (2) being capable of handing arrays of any dimension.

请注意,还有superbatfish的blockwise_view。它以不同的格式排列块(使用更多的轴),但它的优点是(1)总是返回一个视图,(2)能够处理任何维度的数组。

#2


5  

It seems to me that this is a task for numpy.split or some variant.

在我看来,这是numpy.split或某些变体的任务。

e.g.

例如

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)

If you have a NxN image you can create, e.g., a list of 2 NxN/2 subimages, and then divide them along the other axis.

如果您有NxN图像,则可以创建例如2个NxN / 2个子图像的列表,然后沿另一个轴划分它们。

numpy.hsplit and numpy.vsplit are also available.

numpy.hsplit和numpy.vsplit也可用。

#3


5  

There are some other answers that seem well-suited for your specific case already, but your question piqued my interest in the possibility of a memory-efficient solution usable up to the maximum number of dimensions that numpy supports, and I ended up spending most of the afternoon coming up with possible method. (The method itself is relatively simple, it's just that I still haven't used most of the really fancy features that numpy supports so most of the time was spent researching to see what numpy had available and how much it could do so that I didn't have to do it.)

还有一些其他答案似乎已经非常适合您的具体案例,但是您的问题引起了我对有效内存效率解决方案可能达到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]

#4


2  

If you want a solution that also handles the cases when the matrix is not equally divided, you can use this:

如果你想要一个解决方案,当矩阵没有被平分时也可以处理这些情况,你可以使用:

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)

#5


1  

For now it just works when the big 2d array can be perfectly sliced into equally sized subarrays.

现在它只是在大的2d阵列可以完美地切成相同大小的子阵列时才起作用。

The code bellow slices

代码低于切片

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

into this

进入这个

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 determine the block size

p ang q确定块大小

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)

#6


1  

You question practically the same as this one. You can use the one-liner with np.ndindex() and reshape():

你问的几乎和这个一样。您可以使用带有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)

To create the result you want:

要创建所需的结果:

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

#7


0  

Here is a solution based on unutbu's answer that handle case where matrix cannot be equally divided. In this case, it will resize the matrix before using some interpolation. You need OpenCV for this. Note that I had to swap ncols and nrows to make it works, didn't figured why.

这是一个基于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))

#1


52  

You should be able to break your array into "blocks" using some combination of reshape and swapaxes:

你应该能够使用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))

turns c

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

into

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

I've posted an inverse function, unblockshaped, here, and an N-dimensional generalization here. The generalization gives a little more insight into the reasoning behind this algorithm.

我在这里发布了一个反函数,unblockshaped,这里和一个N维泛化。这种推广可以更深入地了解该算法背后的推理。


Note that there is also superbatfish's blockwise_view. It arranges the blocks in a different format (using more axes) but it has the advantage of (1) always returning a view and (2) being capable of handing arrays of any dimension.

请注意,还有superbatfish的blockwise_view。它以不同的格式排列块(使用更多的轴),但它的优点是(1)总是返回一个视图,(2)能够处理任何维度的数组。

#2


5  

It seems to me that this is a task for numpy.split or some variant.

在我看来,这是numpy.split或某些变体的任务。

e.g.

例如

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)

If you have a NxN image you can create, e.g., a list of 2 NxN/2 subimages, and then divide them along the other axis.

如果您有NxN图像,则可以创建例如2个NxN / 2个子图像的列表,然后沿另一个轴划分它们。

numpy.hsplit and numpy.vsplit are also available.

numpy.hsplit和numpy.vsplit也可用。

#3


5  

There are some other answers that seem well-suited for your specific case already, but your question piqued my interest in the possibility of a memory-efficient solution usable up to the maximum number of dimensions that numpy supports, and I ended up spending most of the afternoon coming up with possible method. (The method itself is relatively simple, it's just that I still haven't used most of the really fancy features that numpy supports so most of the time was spent researching to see what numpy had available and how much it could do so that I didn't have to do it.)

还有一些其他答案似乎已经非常适合您的具体案例,但是您的问题引起了我对有效内存效率解决方案可能达到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]

#4


2  

If you want a solution that also handles the cases when the matrix is not equally divided, you can use this:

如果你想要一个解决方案,当矩阵没有被平分时也可以处理这些情况,你可以使用:

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)

#5


1  

For now it just works when the big 2d array can be perfectly sliced into equally sized subarrays.

现在它只是在大的2d阵列可以完美地切成相同大小的子阵列时才起作用。

The code bellow slices

代码低于切片

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

into this

进入这个

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 determine the block size

p ang q确定块大小

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)

#6


1  

You question practically the same as this one. You can use the one-liner with np.ndindex() and reshape():

你问的几乎和这个一样。您可以使用带有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)

To create the result you want:

要创建所需的结果:

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

#7


0  

Here is a solution based on unutbu's answer that handle case where matrix cannot be equally divided. In this case, it will resize the matrix before using some interpolation. You need OpenCV for this. Note that I had to swap ncols and nrows to make it works, didn't figured why.

这是一个基于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))