Issue
I'm looking for the fastest way to compute the intersection over union (Jaccard Index) of two binary masks (2d arrays of 1s and 0s) in numpy of the exact same shape. My code for computing this is:
import numpy as np
def binaryMaskIOU(mask1, mask2):
mask1_area = np.count_nonzero(mask1 == 1)
mask2_area = np.count_nonzero(mask2 == 1)
intersection = np.count_nonzero(np.logical_and(mask1==1, mask2==1))
iou = intersection/(mask1_area+mask2_area-intersection)
return iou
So I have two arrays with only 1s and zeros (a binary image mask), and:
- the area of each mask is the number of 1s in the array
- the intersection of both masks is the spatial locations where both masks are equal to 1
- the IOU (intersection over union) is defined as the intersection divided by the union
Any optimization hints/insights appreciated as in my code I need to run this operation many times with large numbers of masks and I want to make sure it is as fast as possible.
Solution
I initialy posted an answer, realised I'd over complicated it and when I went to edit it found the timings were worse than the original function. That's been deleted.
The code in the question performs close to the others.
There's no need for the two maskN == 1
in the logical_and
.
The dtype can have a significant effect. bm1
is much faster If it's np.bool but it depends what else is being done with the data.
If it's possible to change the dtype there may be some significant time savings otherwise I can't see much, perhaps somebody else can.
import numpy as np
def make_arrays( size = ( 5,5 ), dtype = np.int ):
return ( np.random.randint( 2, size = size, dtype = dtype ),
np.random.randint( 2, size = size, dtype = dtype ) )
def bm0(mask1, mask2):
mask1_area = np.count_nonzero(mask1 == 1) # I assume this is faster as mask1 == 1 is a bool array
mask2_area = np.count_nonzero(mask2 == 1)
intersection = np.count_nonzero( np.logical_and( mask1, mask2) )
iou = intersection/(mask1_area+mask2_area-intersection)
return iou
def bm1(mask1, mask2):
mask1_area = np.count_nonzero( mask1 )
mask2_area = np.count_nonzero( mask2 )
intersection = np.count_nonzero( np.logical_and( mask1, mask2 ) )
iou = intersection/(mask1_area+mask2_area-intersection)
return iou
def binaryMaskIOU(mask1, mask2): # From the question.
mask1_area = np.count_nonzero(mask1 == 1)
mask2_area = np.count_nonzero(mask2 == 1)
intersection = np.count_nonzero(np.logical_and( mask1==1, mask2==1 ))
iou = intersection/(mask1_area+mask2_area-intersection)
return iou
n = 50
a, b = make_arrays( ( n, n ) )
bm0( a, b )
# 0.3416313559322034
bm1(a,b)
# 0.3416313559322034
binaryMaskIOU(a,b)
# 0.3416313559322034
%timeit bm0( a, b )
# 7.73 µs ± 22.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit bm1( a, b )
# 12.2 µs ± 20.3 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit binaryMaskIOU( a, b )
# 10.3 µs ± 36.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
n = 500
a, b = make_arrays( ( n, n ) )
%timeit bm0( a, b )
# 342 µs ± 1.29 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit bm1( a, b )
# 1.01 ms ± 514 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit binaryMaskIOU( a, b )
# 419 µs ± 1.11 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
################# make a and b np.bool arrays #################
n = 500
a, b = make_arrays( ( n, n), dtype = np.bool )
%timeit bm0( a, b )
# 439 µs ± 406 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit bm1( a, b )
# 63.2 µs ± 174 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit binaryMaskIOU( a, b )
# 814 µs ± 496 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Answered By - Tls Chris
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.