使用NumPy's powerful broadcasting feature根据迭代器限制创建范围数组,然后直接使用公式,像这样-
mask_mat = np.zeros((1280,720),np.bool)
I = np.arange(640,1220)[:,None]
J = np.arange(0,360)
mask_mat[640:1220,0:360] = ((J > (-9/16*I + 690))&(J < (-9/16*I + 750)))
特别注意步骤:I = np.arange(640,1220)[:,None]。我们在那里添加一个新轴,它代表输出中的第一个轴。这引入了广播功能,以向量化的方式执行元素加法。另一个范围数组J 沿着输出的第二个轴。
为了进一步提高性能,我们可能需要计算 -9/16*I 并在公式的两个位置重复使用它。
运行时测试
方法-
# Original loopy soln
def loopy_app():
mask_mat = np.zeros((1280,720),np.bool)
for i in range(640,1220):
for j in range(0,360):
if ((j > (-9/16*i + 690))&(j < (-9/16*i + 750))):
mask_mat[i][j] = True;
return mask_mat
# @donkopotamus's soln
def fromfunc_app():
return np.fromfunction(
lambda i, j: ((640 <= i) & (i < 1220) &
(j < 360) &
(j > (-9/16 * i + 690)) &
(j < (-9/16*i + 750))), (1280, 720))
# Proposed in this post
def vectorized_app():
mask_mat = np.zeros((1280,720),np.bool)
I = np.arange(640,1220)[:,None]
J = np.arange(0,360)
mask_mat[640:1220,0:360] = ((J > (-9/16*I + 690))&(J < (-9/16*I + 750)))
return mask_mat
时间安排 -
In [86]: %timeit loopy_app()
10 loops, best of 3: 29.7 ms per loop
In [87]: %timeit fromfunc_app()
100 loops, best of 3: 11.9 ms per loop
In [88]: %timeit vectorized_app()
1000 loops, best of 3: 370 µs per loop
In [90]: 29700/370.0
Out[90]: 80.27027027027027
80x+ 使用基于广播的矢量化方法加速!
numexpr 进一步提升
我们可以通过引入numexpr module 将这些算术运算作为一个表达式来进一步提升它:
import numexpr as ne
def vectorized_expr_app():
mask_mat = np.zeros((1280,720),np.bool)
I = np.arange(640,1220)[:,None]
J = np.arange(0,360)
vals = ne.evaluate('((J > (-9/16*I + 690))&(J < (-9/16*I + 750)))')
mask_mat[640:1220,0:360] = vals
return mask_mat
时间:
In [101]: %timeit vectorized_expr_app()
1000 loops, best of 3: 321 µs per loop
In [102]: 29700/321.0
Out[102]: 92.5233644859813
90x+现在加速!