将一个3D numpy数组分割成3D块

12
我希望以一种“Pythonic”的方式将3D numpy数组拆分为3D块。 我正在处理相当大的图像序列数组(1000X1200X1600),因此需要将它们分成几个部分进行处理。
我已编写了函数来执行此操作,但我想知道是否有一种本地numpy方法可以实现此操作 - numpy.split似乎不能满足我的要求(但也许我不理解其功能)。
明确一下:下面的代码完成了我的任务,但我正在寻求更快的方法来完成它。
def make_blocks(x,t):
#x should be a yXmXn matrix, and t should even divides m,n
#returns a list of 3D blocks of size yXtXt 
    down =  range(0,x.shape[1],t)
    across = range(0,x.shape[2],t)
    reshaped = []
    for d in down:
        for a in across:
            reshaped.append(x[:,d:d+t,a:a+t])
    return reshaped

def unmake_blocks(x,d,m,n):
#this takes a list of matrix blocks of size dXd that is m*n/d^2 long 
#returns a 2D array of size mXn
    rows = []
    for i in range(0,int(m/d)):
        rows.append(np.hstack(x[i*int(n/d):(i+1)*int(n/d)]))
    return np.vstack(rows)

你的缩进有误。给定一个 (1000X1200X1600) 的数组,你想要什么大小的块?np.split 只能在一个维度上工作。我猜测,没有研究你的函数,你想要在 2 或 3 个维度上进行分割。 - hpaulj
你的猜测是正确的,我想要由我的(1000X1200X1600)数组制成的更小的3D数组,例如大小为(1000X50X50)的块。看起来应该有一个在numpy中完成此操作的函数... - mdriscoll
1个回答

12

这里是使用排列维度与np.transposereshape的向量化版本实现 -

def make_blocks_vectorized(x,d):
    p,m,n = x.shape
    return x.reshape(-1,m//d,d,n//d,d).transpose(1,3,0,2,4).reshape(-1,p,d,d)

def unmake_blocks_vectorized(x,d,m,n):    
    return np.concatenate(x).reshape(m//d,n//d,d,d).transpose(0,2,1,3).reshape(m,n)
< p > 对于make_blocks的示例运行 -


In [120]: x = np.random.randint(0,9,(2,4,4))

In [121]: make_blocks(x,2)
Out[121]: 
[array([[[4, 7],
         [8, 3]],

        [[0, 5],
         [3, 2]]]), array([[[5, 7],
         [4, 0]],

        [[7, 3],
         [5, 7]]]), ... and so on.

In [122]: make_blocks_vectorized(x,2)
Out[122]: 
array([[[[4, 7],
         [8, 3]],

        [[0, 5],
         [3, 2]]],


       [[[5, 7],
         [4, 0]],

        [[7, 3],
         [5, 7]]],  ... and so on.

unmake_blocks的示例运行 -

In [135]: A = [np.random.randint(0,9,(3,3)) for i in range(6)]

In [136]: d = 3

In [137]: m,n = 6,9

In [138]: unmake_blocks(A,d,m,n)
Out[138]: 
array([[6, 6, 7, 8, 6, 4, 5, 4, 8],
       [8, 8, 3, 2, 7, 6, 8, 5, 1],
       [5, 2, 2, 7, 1, 2, 3, 1, 5],
       [6, 7, 8, 2, 2, 1, 6, 8, 4],
       [8, 3, 0, 4, 4, 8, 8, 6, 3],
       [5, 5, 4, 8, 5, 2, 2, 2, 3]])

In [139]: unmake_blocks_vectorized(A,d,m,n)
Out[139]: 
array([[6, 6, 7, 8, 6, 4, 5, 4, 8],
       [8, 8, 3, 2, 7, 6, 8, 5, 1],
       [5, 2, 2, 7, 1, 2, 3, 1, 5],
       [6, 7, 8, 2, 2, 1, 6, 8, 4],
       [8, 3, 0, 4, 4, 8, 8, 6, 3],
       [5, 5, 4, 8, 5, 2, 2, 2, 3]])

使用view_as_blocks作为make_blocks的替代方案 -

from skimage.util.shape import view_as_blocks

def make_blocks_vectorized_v2(x,d):
    return view_as_blocks(x,(x.shape[0],d,d))

运行时测试

1) 使用原始方法和基于 view_as_blocks 的方法进行 make_blocks 测试 -

In [213]: x = np.random.randint(0,9,(100,160,120)) # scaled down by 10

In [214]: %timeit make_blocks(x,10)
1000 loops, best of 3: 198 µs per loop

In [215]: %timeit view_as_blocks(x,(x.shape[0],10,10))
10000 loops, best of 3: 85.4 µs per loop

2) 使用原始和转置+重塑方法的unmake_blocks -

In [237]: A = [np.random.randint(0,9,(10,10)) for i in range(600)]

In [238]: d = 10

In [239]: m,n = 10*20,10*30

In [240]: %timeit unmake_blocks(A,d,m,n)
100 loops, best of 3: 2.03 ms per loop

In [241]: %timeit unmake_blocks_vectorized(A,d,m,n)
1000 loops, best of 3: 511 µs per loop

这个向量化的解决方案比我的循环解决方案慢(960毫秒对9.52毫秒)。这让我感到惊讶,因为我认为数组操作总是比循环更好... - mdriscoll
@mdriscoll 输入涉及的大小是多少?其他参数是什么?我们正在讨论哪种情况,两种情况都包括吗? - Divakar
输入是一个1000x1600x1200的矩阵,块大小为50。我只测试了make_block函数,引用的数字是%time返回的“墙时间”。 - mdriscoll
1
@mdriscoll 看起来对于 make_blocks 函数,我们可以使用一个更好的函数 view_as_blocks,它已经在帖子中添加了。去看看吧。 - Divakar
view_as_blocks 看起来正是我想要的 - 谢谢 - mdriscoll
首先,这是一个非常有用的解决方案。但是,如果发生了一些不好看的事情,比如 x = np.random.randint(0,9,(2,3,7)),该如何最优地更改/配置这些(或其中之一)解决方案呢? - nate

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接