【问题标题】:How do I create a Boolean matrix with a corresponding index condition in Python?如何在 Python 中创建具有相应索引条件的布尔矩阵?
【发布时间】:2017-10-29 08:17:50
【问题描述】:

我是 Python 的新手,我正在尝试创建一个 1280x720 布尔矩阵,当 x-index 和 y-index 在条件下满足时,该矩阵为 TRUE。具体来说,黑色区域是我希望所有值为 TRUE 的地方,其他值为 FALSE。 This is the image geometry of the matrix 到目前为止,我做了这样的代码:

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;

但是循环占用了很多时间。所以请帮忙!

【问题讨论】:

    标签: python performance numpy matrix vectorization


    【解决方案1】:

    使用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+现在加速!

    【讨论】:

      【解决方案2】:

      一种选择是使用np.fromfunction

      np.fromfunction(
          lambda i, j: ((640 <= i) & (i < 1220) &
                       (j < 360) & 
                       (j > (-9/16 * i + 690)) & 
                       (j < (-9/16 * i + 750))), (1280, 720))
      

      它可能不是最快的,因为我们正在评估一堆总是False 的行,但它可能对您来说足够快(在这台特定的机器上 16 毫秒)

      注意:

      由于它使用矩阵运算来评估真实性,而不是函数调用,@Divakar 的速度要快得多,为 511µs

      【讨论】:

        猜你喜欢
        • 2022-01-22
        • 1970-01-01
        • 2020-02-04
        • 2019-04-06
        • 2023-01-21
        • 1970-01-01
        • 1970-01-01
        • 2020-08-07
        • 1970-01-01
        相关资源
        最近更新 更多