Python 将二维阵列切片为较小的二维阵列

Python 将二维阵列切片为较小的二维阵列,python,numpy,Python,Numpy,有没有办法将numpy中的2d数组切片为更小的2d数组 示例 [[1,2,3,4], -> [[1,2] [3,4] [5,6,7,8]] [5,6] [7,8]] 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 #bloc

有没有办法将numpy中的2d数组切片为更小的2d数组

示例

[[1,2,3,4],   ->    [[1,2] [3,4]   
 [5,6,7,8]]          [5,6] [7,8]]
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)

所以我基本上想把一个2x4数组减少为2个2x2数组。正在寻找一种用于图像的通用解决方案。

目前,当大的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
ang
q
确定块大小

代码

[[1,2,3,4],   ->    [[1,2] [3,4]   
 [5,6,7,8]]          [5,6] [7,8]]
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)

在我看来,这是一个任务或某种变体

e、 g

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


也可以使用。

您应该能够使用
重塑
交换盘的一些组合,将阵列分解为“块”:

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
    assert h % nrows == 0, "{} rows is not evenly divisble by {}".format(h, nrows)
    assert w % ncols == 0, "{} cols is not evenly divisble by {}".format(w, ncols)
    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]]
进入


我已经发布了一个,和一个N维的概括。这种概括使我们对该算法背后的推理有了更多的了解


请注意,还有一个问题。它安排了 不同格式的块(使用更多轴),但它具有(1)的优点 始终返回视图,并且(2)能够处理任何
维度。

还有一些其他答案似乎已经非常适合您的具体情况,但您的问题激发了我的兴趣,我想找到一种内存高效的解决方案,可以使用numpy支持的最大维度数,最后我花了一下午的大部分时间想出可能的方法。(该方法本身相对简单,只是我还没有使用numpy支持的大多数真正奇特的功能,所以大部分时间都花在研究numpy有哪些可用功能以及它可以做多少,这样我就不必去做了。)

def blockgen(阵列,bpa): “”“创建一个生成器,该生成器从给定的 数组(_-like);bpa是由每个轴的块数组成的数组 (最小值为1,必须是数组相应轴大小的除数) 块是使用普通numpy切片选择的,它们将是视图而不是视图 而不是拷贝;这对于正在创建的非常大的多维数组非常有用 阻塞,对于非常大的块,但这也意味着结果必须是 如果要修改,则复制(除非也需要修改原始数据 " bpa=np.asarray(bpa)#如果bpa还不是ndarray #参数检查 如果array.ndim!=bpa.size:#bpa与数组维度不匹配 raise VALUERROR(“bpa的大小必须等于数组维度。”) 如果(bpa.dtype!=np.int#bpa必须是所有整数 或(bpa<1)。any()#bpa中的所有值必须大于等于1 或(array.shape%bpa).any()):#%!=0表示不可等分 raise VALUERROR(“bpa({0})必须由非零正整数组成” “平均分割相应阵列轴的” “大小”。格式(bpa)) #生成块边缘索引 rgen=(np.r[:array.shape[i]+1:array.shape[i]//blk\u n] 对于i,枚举中的blk_n(bpa)) #为每个轴(轴)构建切片序列 #不能用于使物品易于操作 c=[[np.s_u[i:j]表示i,j在zip中(r[:-1],r[1:])]表示r在rgen中] #现在让我们来看看积木;这比可能的效率要低一点 #因为numpy不喜欢锯齿状数组,我也不想写 #这是一个很好的例子。 对于np.ndindex(*bpa)中的IDX: blockbounds=tuple(c[j][idxs[j]]表示范围内的j(bpa.size)) 产量数组[块边界]
您可以提问。您可以使用带有
np.ndindex()
重塑()
的一行程序:

要创建所需的结果,请执行以下操作:

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

如果您想要一个解决方案,该解决方案还可以处理矩阵为 不等分,您可以使用以下方法:

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)

这是一个基于unutbu的答案的解决方案,可以处理矩阵不能等分的情况。在这种情况下,它将在使用插值之前调整矩阵的大小。为此,您需要OpenCV。请注意,我必须交换
ncol
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))

对工程师的答案进行了一些小的改进,解决了大2d阵列无法完美分割成大小相等的子阵列的情况

def blockfy(a, p, q):
    '''
    Divides array a into subarrays of size p-by-q
    p: block row size
    q: block column size
    '''
    m = a.shape[0]  #image row size
    n = a.shape[1]  #image column size

    # pad array with NaNs so it can be divided by p row-wise and by q column-wise
    bpr = ((m-1)//p + 1) #blocks per row
    bpc = ((n-1)//q + 1) #blocks per column
    M = p * bpr
    N = q * bpc

    A = np.nan* np.ones([M,N])
    A[:a.shape[0],:a.shape[1]] = a

    block_list = []
    previous_row = 0
    for row_block in range(bpc):
        previous_row = row_block * p   
        previous_column = 0
        for column_block in range(bpr):
            previous_column = column_block * q
            block = A[previous_row:previous_row+p, previous_column:previous_column+q]

            # remove nan columns and nan rows
            nan_cols = np.all(np.isnan(block), axis=0)
            block = block[:, ~nan_cols]
            nan_rows = np.all(np.isnan(block), axis=1)
            block = block[~nan_rows, :]

            ## append
            if block.size:
                block_list.append(block)

    return block_list
示例:

a = np.arange(25)
a = a.reshape((5,5))
out = blockfy(a, 2, 3)

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

out[0] ->
array([[0., 1., 2.],
       [5., 6., 7.]])

out[1]->
array([[3., 4.],
       [8., 9.]])

out[-1]->
array([[23., 24.]])
屈服

[[7 6 2 4 4 2 5 2 3]
 [2 3 7 6 8 8 2 6 2]
 [4 1 3 1 3 8 1 3 7]
 [6 1 1 5 7 2 1 5 8]
 [8 8 7 6 6 1 8 8 4]
 [6 1 8 2 1 4 5 1 8]
 [7 3 4 2 5 6 1 2 7]
 [4 6 7 5 8 2 8 2 8]
 [6 6 5 5 6 1 2 6 4]]
[[array([[7, 6, 2],
       [2, 3, 7],
       [4, 1, 3]]), array([[4, 4, 2],
       [6, 8, 8],
       [1, 3, 8]]), array([[5, 2, 3],
       [2, 6, 2],
       [1, 3, 7]])], [array([[6, 1, 1],
       [8, 8, 7],
       [6, 1, 8]]), array([[5, 7, 2],
       [6, 6, 1],
       [2, 1, 4]]), array([[1, 5, 8],
       [8, 8, 4],
       [5, 1, 8]])], [array([[7, 3, 4],
       [4, 6, 7],
       [6, 6, 5]]), array([[2, 5, 6],
       [5, 8, 2],
       [5, 6, 1]]), array([[1, 2, 7],
       [8, 2, 8],
       [2, 6, 4]])]]

我发布了我的解决方案。请注意,这段代码实际上并没有创建原始数组的副本,所以它可以很好地处理大数据。此外,如果数组不能被平均分割,它也不会崩溃(但您可以通过删除
ceil
并检查
v_切片
h_切片
是否在不休息的情况下被分割来轻松添加条件)

此代码更改(或者更准确地说,允许您直接访问数组的一部分)如下:

为此:

[[0 1]
 [3 4]]
[[2]
 [5]]
[[6 7]]
[[8]]
如果您需要实际的副本,这是您正在寻找的


如果你确信大数组可以被平均分割,你可以使用工具。

要添加到@Aenaon answer和他的blockfy函数中,如果你正在使用彩色图像/3D数组,这里是我为三通道输入创建224 x 224作物的管道

def blockfy(a, p, q):
'''
Divides array a into subarrays of size p-by-q
p: block row size
q: block column size
'''
m = a.shape[0]  #image row size
n = a.shape[1]  #image column size

# pad array with NaNs so it can be divided by p row-wise and by q column-wise
bpr = ((m-1)//p + 1) #blocks per row
bpc = ((n-1)//q + 1) #blocks per column
M = p * bpr
N = q * bpc

A = np.nan* np.ones([M,N])
A[:a.shape[0],:a.shape[1]] = a

block_list = []
previous_row = 0
for row_block in range(bpc):
    previous_row = row_block * p   
    previous_column = 0
    for column_block in range(bpr):
        previous_column = column_block * q
        block = A[previous_row:previous_row+p, previous_column:previous_column+q]

        # remove nan columns and nan rows
        nan_cols = np.all(np.isnan(block), axis=0)
        block = block[:, ~nan_cols]
        nan_rows = np.all(np.isnan(block), axis=1)
        block = block[~nan_rows, :]

        ## append
        if block.size:
            block_list.append(block)

return block_list
然后扩展到

for file in os.listdir(path_to_crop):   ### list files in your folder
   img = io.imread(path_to_crop + file, as_gray=False) ### open image 

   r = blockfy(img[:,:,0],224,224)  ### crop blocks of 224 x 224 for red channel
   g = blockfy(img[:,:,1],224,224)  ### crop blocks of 224 x 224 for green channel
   b = blockfy(img[:,:,2],224,224)  ### crop blocks of 224 x 224 for blue channel

   for x in range(0,len(r)):
       img = np.array((r[x],g[x],b[x])) ### combine each channel into one patch by patch

       img = img.astype(np.uint8) ### cast back to proper integers

       img_swap = img.swapaxes(0, 2) ### need to swap axes due to the way things were proceesed
       
       img_swap_2 = img_swap.swapaxes(0, 1) ### do it again

       Image.fromarray(img_swap_2).save(path_save_crop+str(x)+"bounding" + file,
                                        format = 'jpeg',
                                        subsampling=0,
                                        quality=100) ### save patch with new name etc 

你能不能让它更通用一些,使块大小成为变量?(条件是块完全适合原始数组)谢谢你的编辑。你能解释一下算法背后的原因吗?几个月前,我有了一个想法,使用
重塑
交换
h//nrows
很有意义,因为这样可以将第一个块的行保持在一起。你需要
nrows
一个d
ncols
成为形状的一部分。
-1
告诉整形填充所需的任何数字a
import numpy as np
from math import ceil

a = np.arange(9).reshape(3, 3)

p, q = 2, 2
width, height = a.shape

v_slices = ceil(width / p)
h_slices = ceil(height / q)

for h in range(h_slices):
    for v in range(v_slices):
        block = a[h * p : h * p + p, v * q : v * q + q]
        # do something with a block
[[0 1 2]
 [3 4 5]
 [6 7 8]]
[[0 1]
 [3 4]]
[[2]
 [5]]
[[6 7]]
[[8]]
def blockfy(a, p, q):
'''
Divides array a into subarrays of size p-by-q
p: block row size
q: block column size
'''
m = a.shape[0]  #image row size
n = a.shape[1]  #image column size

# pad array with NaNs so it can be divided by p row-wise and by q column-wise
bpr = ((m-1)//p + 1) #blocks per row
bpc = ((n-1)//q + 1) #blocks per column
M = p * bpr
N = q * bpc

A = np.nan* np.ones([M,N])
A[:a.shape[0],:a.shape[1]] = a

block_list = []
previous_row = 0
for row_block in range(bpc):
    previous_row = row_block * p   
    previous_column = 0
    for column_block in range(bpr):
        previous_column = column_block * q
        block = A[previous_row:previous_row+p, previous_column:previous_column+q]

        # remove nan columns and nan rows
        nan_cols = np.all(np.isnan(block), axis=0)
        block = block[:, ~nan_cols]
        nan_rows = np.all(np.isnan(block), axis=1)
        block = block[~nan_rows, :]

        ## append
        if block.size:
            block_list.append(block)

return block_list
for file in os.listdir(path_to_crop):   ### list files in your folder
   img = io.imread(path_to_crop + file, as_gray=False) ### open image 

   r = blockfy(img[:,:,0],224,224)  ### crop blocks of 224 x 224 for red channel
   g = blockfy(img[:,:,1],224,224)  ### crop blocks of 224 x 224 for green channel
   b = blockfy(img[:,:,2],224,224)  ### crop blocks of 224 x 224 for blue channel

   for x in range(0,len(r)):
       img = np.array((r[x],g[x],b[x])) ### combine each channel into one patch by patch

       img = img.astype(np.uint8) ### cast back to proper integers

       img_swap = img.swapaxes(0, 2) ### need to swap axes due to the way things were proceesed
       
       img_swap_2 = img_swap.swapaxes(0, 1) ### do it again

       Image.fromarray(img_swap_2).save(path_save_crop+str(x)+"bounding" + file,
                                        format = 'jpeg',
                                        subsampling=0,
                                        quality=100) ### save patch with new name etc