将图像堆叠为numpy数组比预分配更快吗?

4

我经常需要堆叠2D的numpy数组(tiff图像)。为此,我首先将它们附加在一个列表中,然后使用np.dstack。这似乎是获取3D阵列叠加图像最快的方法。但是,是否有更快/更节省内存的方法?

from time import time
import numpy as np

# Create 100 images of the same dimention 256x512 (8-bit). 
# In reality, each image comes from a different file
img = np.random.randint(0,255,(256, 512, 100))

t0 = time()
temp = []
for n in range(100):
    temp.append(img[:,:,n])
stacked = np.dstack(temp)
#stacked = np.array(temp)  # much slower 3.5 s for 100

print time()-t0  # 0.58 s for 100 frames
print stacked.shape

# dstack in each loop is slower
t0 = time()
temp = img[:,:,0]
for n in range(1, 100):
    temp = np.dstack((temp, img[:,:,n]))
print time()-t0  # 3.13 s for 100 frames
print temp.shape

# counter-intuitive but preallocation is slightly slower
stacked = np.empty((256, 512, 100))
t0 = time()
for n in range(100):
    stacked[:,:,n] = img[:,:,n]
print time()-t0  # 0.651 s for 100 frames
print stacked.shape

# (Edit) As in the accepted answer, re-arranging axis to mainly use 
# the first axis to access data improved the speed significantly.
img = np.random.randint(0,255,(100, 256, 512))

stacked = np.empty((100, 256, 512))
t0 = time()
for n in range(100):
    stacked[n,:,:] = img[n,:,:]
print time()-t0  # 0.08 s for 100 frames
print stacked.shape

如果您保证temp中的所有数组都符合条件,则可以避免调用dstack。此时,您只需调用stacked = np.concatenate(temp,axis=2)来节省Python开销的小部分时间。如果您展示更多的代码,可能会有更好的解决方法,但是就所展示的代码而言,它已经几乎是最优的了。 - Daniel
Temp中的数组都是2D的,我想要连接它们以获得一个3D数组。因此,np.concatenate(temp, axis=2)会产生一个错误:轴2超出了范围[0, 2)。np.concatenate(temp, axis=1)将创建一个2D数组(256x51200)。 - otterb
我在评论中漏掉了一个关键部分,它应该是“...如果满足这个条件,temp 中的所有数组都是3D的”。需要注意的是,这种节省对于非常大的 temp 大小来说是微不足道的,可能每个数组约为2微秒。 - Daniel
1个回答

6

经过与otterb的共同努力,我们得出了预分配数组是正确的方式。显然,性能瓶颈是图像编号(n)作为最快变化索引的数组布局。如果我们将n作为数组的第一个索引(默认为“C”顺序:第一个索引变化最慢,最后一个索引变化最快),我们可以获得最佳性能:

from time import time
import numpy as np

# Create 100 images of the same dimention 256x512 (8-bit). 
# In reality, each image comes from a different file
img = np.random.randint(0,255,(100, 256, 512))

# counter-intuitive but preallocation is slightly slower
stacked = np.empty((100, 256, 512))
t0 = time()
for n in range(100):
    stacked[n] = img[n]
print time()-t0  
print stacked.shape

谢谢!我也认为预分配应该是最快的,但不知何故它稍微慢了一些。我更新了我的问题以包括预分配。有什么想法吗? - otterb
嗨,这确实非常有趣,我本来以为它会更快。你能试试把时间索引放在第一位是否有所不同吗(想法是你写的块可以更容易地被访问)?我猜它可能会更慢的原因是数组的索引有一些开销,允许负数等。使用Cython可以摆脱这些问题... - Magellan88
你是说应该用 stacked[n,:,:] 而不是 stacked[:,:,n] 吗?好主意。我会在能够访问到进行性能分析的同一台电脑时尝试的。 - otterb
1
是的!我按照你建议的重新排列了轴。现在速度显著提高了。我已经接受了你的答案,但是,你能否修改一下你的答案,以澄清第一个轴的问题? - otterb
嗨,otterb,感谢你的努力。真的很高兴它能够正常工作。我必须说,我也从中学到了一些东西。我将答案以一种形式呈现出来,这应该对其他人有用,并反映了你的贡献。你介意提供一下你得到的性能数据吗?这样我们也可以把它放进去。 - Magellan88
这就是我的问题。100帧只需要约0.08秒,速度相当快。我对结果非常满意。再次感谢! - otterb

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