矢量化的数组计算版本

时间:2015-10-12 04:05:12

标签: python-2.7 multidimensional-array vectorization

是否有一种矢量化以下数组计算的方法(即不使用for循环):

for i in range(numCells):
    z[i] = ((i_mask == i)*s_image).sum()/pixel_counts[i]

s_image是存储为二维ndarray的图像(为简单起见,我在此处删除了颜色维度)。 i_mask也是一个与s_image大小相同的二维数组,但它包含整数,这些整数是长度为numCells的“单元”列表的索引。结果z是长度为numCell的一维数组。计算的目的是对掩码包含相同索引的所有像素值求和,并将结果放在z向量中。 (pixel_counts也是长度为numCell的一维数组)。

3 个答案:

答案 0 :(得分:1)

作为一种矢量化方法,您可以利用broadcastingmatrix-multiplication,就像这样 -

# Generate a binary array of matches for all elements in i_mask against 
# an array of indices going from 0 to numCells 
matches = i_mask.ravel() == np.arange(numCells)[:,None]

# Do elementwise multiplication against s_image and sum those up for 
# each such index going from 0 to numCells. This is essentially doing 
# matix multiplicatio. Finally elementwise divide by pixel_counts 
out = matches.dot(s_image.ravel())/pixel_counts

或者,作为另一种向量化方法,您也可以使用np.einsum进行乘法和求和,这可能会提升性能,就像这样 -

out = np.einsum('ij,j->i',matches,s_image.ravel())/pixel_counts

运行时测试 -

功能定义:

def vectorized_app1(s_image,i_mask,pixel_counts):
    matches = i_mask.ravel() == np.arange(numCells)[:,None]
    return matches.dot(s_image.ravel())/pixel_counts

def vectorized_app2(s_image,i_mask,pixel_counts):
    matches = i_mask.ravel() == np.arange(numCells)[:,None]
    return np.einsum('ij,j->i',matches,s_image.ravel())/pixel_counts

def org_app(s_image,i_mask,pixel_counts):
    z = np.zeros(numCells)
    for i in range(numCells):
        z[i] = ((i_mask == i)*s_image).sum()/pixel_counts[i]
    return z

时序:

In [7]: # Inputs
   ...: numCells = 100
   ...: m,n = 100,100
   ...: pixel_counts = np.random.rand(numCells)
   ...: s_image = np.random.rand(m,n)
   ...: i_mask = np.random.randint(0,numCells,(m,n))
   ...: 

In [8]: %timeit org_app(s_image,i_mask,pixel_counts)
100 loops, best of 3: 8.13 ms per loop

In [9]: %timeit vectorized_app1(s_image,i_mask,pixel_counts)
100 loops, best of 3: 7.76 ms per loop

In [10]: %timeit vectorized_app2(s_image,i_mask,pixel_counts)
100 loops, best of 3: 4.08 ms per loop

答案 1 :(得分:0)

这是我的解决方案(处理了所有三种颜色)。不确定这是多么有效。有人有更好的解决方案吗?

import numpy as np
import pandas as pd

# Unravel the mask matrix into a 1-d array
i = np.ravel(i_mask)

# Unravel the image into 1-d arrays for
# each colour (RGB)
r = np.ravel(s_image[:,:,0])
g = np.ravel(s_image[:,:,1])
b = np.ravel(s_image[:,:,2])

# prepare a dictionary to create the dataframe
data = {'i' : i, 'r' : r, 'g' : g, 'b' : b}

# create a dataframe
df = pd.DataFrame(data)

# Use pandas pivot table to average the colour
# intensities for each cell index value
pixAvgs = pd.pivot_table(df, values=['r', 'g', 'b'], index='i')
pixAvgs.head()

输出:

            b           g           r
i                                    
-1  26.719482   68.041868  101.603297
 0  75.432432  170.135135  202.486486
 1  92.162162  184.189189  208.270270
 2  71.179487  171.897436  201.846154
 3  76.026316  178.078947  211.605263

答案 2 :(得分:0)

最后,我以不同的方式解决了这个问题,并大大提高了速度。不是使用如上所述的i_mask,而是在输出强度的1-d阵列中的二维索引数组z,我创建了一个不同的数组mask1593,其尺寸为(numCells×45)。每行是平展的256x256像素图像(0到65536)中大约35到45个索引的列表。

In [10]: mask1593[0]
Out[10]: 
array([14853, 14854, 15107, 15108, 15109, 15110, 15111, 15112, 15363,
       15364, 15365, 15366, 15367, 15368, 15619, 15620, 15621, 15622,
       15623, 15624, 15875, 15876, 15877, 15878, 15879, 15880, 16131,
       16132, 16133, 16134, 16135, 16136, 16388, 16389, 16390, 16391,
       16392,     0,     0,     0,     0,     0,     0,     0,     0], dtype=int32)

然后,我可以使用numpy的高级索引实现与以下相同的转换:

def convert_image(self, image_array):
    """Convert 256 x 256 RGB image array to 1593 RGB led intensities."""
    global mask1593
    shape = image_array.shape
    img_data = image_array.reshape(shape[0]*shape[1], shape[2])
    return np.mean(img_data[mask1593], axis=1)

这是结果!一个256x256像素的彩色图像转换成1593种颜色的阵列,用于在这个不规则的LED显示屏上显示:

enter image description here