How can I create a boolean matrix with an appropriate index condition in Python?

I am completely new to Python and I am trying to create a BOOLEAN 1280x720 matrix that is TRUE when the x-index and y-index are met in the conditions. In particular, the black area is where I want all values ​​to be TRUE, others to be FALSE. This is the geometry of the matrix image So far I have done this code:

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;

      

But the cycle takes so long. So please help!

+3


source to share


2 answers


Create arrays of ranges based on iterator constraints using NumPy powerful broadcasting feature

and then directly use a formula like:

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)))

      

Especially pay attention to the step: I = np.arange(640,1220)[:,None]

. We add a new axis there, which represents the first axis in the output. This causes the broadcast function to perform elementary vector additions. Another array of arrays J

runs along the second axis of the output.

To improve performance, we can calculate -9/16*I

and reuse it in two places in the formula.

Runtime test

Approaches -

# 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 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

      

Timing -



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+

speed up using a vectorized broadcast based approach!

Further enhancement with numexpr

We could augment it by adding a numexpr

module
to perform these arithmetic operations as a single expression:

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

      

Timing:

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+

speed up now!

+1


source


One option is to use 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))

      

It may not be the fastest as we are evaluating a bunch of lines, which is always False

, but it might be fast enough for you (16ms on this particular machine)



Note:

Since it uses matrix operations for likelihood estimation rather than function calls, @Divakar is much faster at 511ΞΌs

0


source







All Articles