在包含多个图像的ndarray中转置和规范化数据的最快方法是什么?

时间:2018-05-07 02:41:28

标签: python numpy optimization

我有一批'图像',通常是128个,最初被读入一个尺寸为128x360x640x3的numpy数组。我需要将每个图像从NHWC转换到NCHW,因此操作ndarray.transpose(2,0,1)并将像素标准化为[0,1]范围,因此我需要将数组除以255.此批处理操作将是可能会定期重复一百次左右。最简单的实现如下:

for i in range(128):
    batchImageDataNew[i,:,:] = batchImageData[i,:,:].transpose(2,0,1)/255.

batchImageDataNew的类型为np.float32,而batchImageData的类型为np.uint8。我试图尽可能地加快这个过程。我以为ndarray.transpose只重新排列了步幅而没有实际接触到内存,但我看到每张图像只有大约1毫秒的转置(总共120毫秒)。另一方面,同时进行转置和分割会使总时间达到约350ms。尽可能加快速度的最佳方法是什么? Cython和多线程(线程?)处理的组合会有帮助吗?我正在Ubuntu上工作,我也可以访问OpenMP。

编辑:我尝试了一个简单的multiprocessing.Pool实现,它为整个循环提供了大约270毫秒,但我想进一步优化它。

def preprocess(i):
    batchImageDataNew[i,:,:] = batchImageData[i,:,:].transpose(2,0,1)/255.


pool = multiprocessing.Pool(8)
pool.map(preprocess, range(128))

2 个答案:

答案 0 :(得分:1)

虚假数据

a = np.array([[[1,1]],[[2,2]],[[3,3]]])
b = a + 10
c = b + 10
d = c + 10
e = np.stack((a,b,c,d))

如果可以并且对整个数组进行操作,通常最好避免for循环

f = np.transpose(e, (0,3,1,2))
g = f / 255

>>> e.shape
(4, 3, 1, 2)
>>> f.shape
(4, 2, 3, 1)

np.moveaxis代替transpose

f = np.moveaxis(e, 3, 1)
f = np.moveaxis(e, (1,2,3), (2,3,1))

通过预先创建数组以接受分割结果,可以实现轻微约25%的改进(在我的机器上):

a = np.array(np.random.rand(128,360,640,3)*255,dtype=np.uint8)
b = np.zeros((128,3,360,640), dtype=np.float32)
np.divide(np.moveaxis(a, (1,2,3), (2,3,1)), 255, out=b)

答案 1 :(得分:1)

您的问题是高度内存和缓存依赖。最佳解决方案取决于您的处理器和RAM速度。这是使用Numba的解决方案,但你可以使用cython做一个非常相似的方法。

示例

import numba as nb
import numpy as np
import time


def tran_scal(batchImageData):
  s=batchImageData.shape
  batchImageDataNew=np.empty((s[0],s[3],s[1],s[2]),dtype=np.float32)
  for i in range(batchImageData.shape[0]):
    batchImageDataNew[i,:,:] = batchImageData[i,:,:].transpose(2,0,1)/255.
  return batchImageDataNew


@nb.njit()
def tran_scal_nb(batchImageData):
  s=batchImageData.shape
  batchImageDataNew=np.empty((s[0],s[3],s[1],s[2]),dtype=np.float32)
  for i in range(batchImageData.shape[0]):
    for j in range(batchImageData.shape[1]):
      for k in range(batchImageData.shape[2]):
        for l in range(batchImageData.shape[3]):
          batchImageDataNew[i,l,j,k] = batchImageData[i,j,k,l]*(1/255.)
  return batchImageDataNew

@nb.njit(parallel=True)
def tran_scal_nb_p(batchImageData):
  s=batchImageData.shape
  batchImageDataNew=np.empty((s[0],s[3],s[1],s[2]),dtype=np.float32)
  for i in nb.prange(batchImageData.shape[0]):
    for j in range(batchImageData.shape[1]):
      for k in range(batchImageData.shape[2]):
        for l in range(batchImageData.shape[3]):
          batchImageDataNew[i,l,j,k] = batchImageData[i,j,k,l]*(1/255.)
  return batchImageDataNew

<强>计时

Core i7-4xxx
#Test data
data=np.array(np.random.rand(128,360,640,3)*255,dtype=np.uint8)
Your solution:    550ms
@wwii(transpose): 379ms
tran_scal_nb:     190ms 
tran_scal_nb_p:   100ms 

在第一次调用时,编译开销大约为0.5秒,但未包含在时序中。