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!
source to share
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!
source to share
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
source to share