1

I have a question which is similar to the one asked here: How to apply a disc shaped mask to a numpy array?. However, instead of masking a single circular sector, I would like to create a mask with multiple circular sectors (number is variable). These masks all have the same radius, but different centers. The coordinates of these centers are stored in a numpy 2d array. For example:

maskcenters = array([[1, 1],
   [7, 2],
   [2, 8]])

Now to create a round mask (radius 2) around a single center I could simply use the solution given by Bi Rico:

import numpy as np

a, b = maskcenters[0]
n = 10
r = 2

y,x = np.ogrid[-a:n-a, -b:n-b]
mask = x*x + y*y <= r*r

Where a and b represent the center of the mask; n the size of the array to mask; and r the radius.

This would result in a mask like:

#output
array([[ True,  True,  True, False, False, False, False, False, False],
   [ True,  True,  True,  True, False, False, False, False, False],
   [ True,  True,  True, False, False, False, False, False, False],
   [False,  True, False, False, False, False, False, False, False],
   [False, False, False, False, False, False, False, False, False],
   [False, False, False, False, False, False, False, False, False],
   [False, False, False, False, False, False, False, False, False],
   [False, False, False, False, False, False, False, False, False],
   [False, False, False, False, False, False, False, False, False]], dtype=bool)

However, I have a variable number of centers around which I want to create masks. I know how to create multiple circular masks with a for loop, but this slows down the code. My optimal outcome, using the example mask centers in a 10x10 array, would be:

#output
array([[ True,  True,  True, False, False, False, False, False,  True],
   [ True,  True,  True,  True, False, False, False,  True,  True],
   [ True,  True,  True, False, False, False,  True,  True,  True],
   [False,  True, False, False, False, False, False,  True,  True],
   [False, False, False, False, False, False, False, False,  True],
   [False, False,  True, False, False, False, False, False, False],
   [False,  True,  True,  True, False, False, False, False, False],
   [ True,  True,  True,  True,  True, False, False, False, False],
   [False,  True,  True,  True, False, False, False, False, False]], dtype=bool)

Any idea how to create this mask without using loops?

Community
  • 1
  • 1
Wilmar van Ommeren
  • 7,469
  • 6
  • 34
  • 65

1 Answers1

2

You can use scipy's binary_dilation with a disk shaped kernel, like so -

# Define inputs
maskcenters = np.array([
   [1, 1],
   [7, 2],
   [2, 8]])

out_shp = (10,10) # Output array shape
r = 2 # Radius of circles

# Get a disk kernel
X,Y = [np.arange(-r,r+1)]*2
disk_mask = X[:,None]**2 + Y**2 <= r*r

# Initialize output array and set the maskcenters as 1s 
out = np.zeros(out_shp,dtype=bool)
out[maskcenters[:,0],maskcenters[:,1]] = 1

# Use binary dilation to get the desired output
out = binary_dilation(out,disk_mask)

Output -

In [64]: print out
[[ True  True  True False False False False False  True False]
 [ True  True  True  True False False False  True  True  True]
 [ True  True  True False False False  True  True  True  True]
 [False  True False False False False False  True  True  True]
 [False False False False False False False False  True False]
 [False False  True False False False False False False False]
 [False  True  True  True False False False False False False]
 [ True  True  True  True  True False False False False False]
 [False  True  True  True False False False False False False]
 [False False  True False False False False False False False]]

Here's another approach using simple indexing -

X,Y = [np.arange(-r,r+1)]*2
disk_mask = X[:,None]**2 + Y**2 <= r*r
Ridx,Cidx = np.where(disk_mask)

out = np.zeros(out_shp,dtype=bool)

absidxR = maskcenters[:,None,0] + Ridx-r
absidxC = maskcenters[:,None,1] + Cidx-r

valid_mask = (absidxR >=0) & (absidxR <out_shp[0]) & \
             (absidxC >=0) & (absidxC <out_shp[1])

out[absidxR[valid_mask],absidxC[valid_mask]] = 1
Divakar
  • 218,885
  • 19
  • 262
  • 358
  • Thanks a lot, this is a perfect! – Wilmar van Ommeren May 03 '16 at 14:19
  • Except this method is more time consuming than looping through all my center points for the creation of the mask. Probably, because my mask has a shape of 1400x1400. Originally it took about 0.35 secs and with this method about 10 secs (0.35 might seem low, but I have to create many masks). – Wilmar van Ommeren May 03 '16 at 15:00
  • @WilmarvanOmmeren Typically how many `maskcenters` points are you dealing with? If it's a small number, loopy solution might be a better fit then. – Divakar May 03 '16 at 18:01
  • Well I am still working with a subset to test my algorithm. Right know I have about 15000 points, while with the complete dataset I probably will have millions of points (fun fact: the points represent tree centers in my study area). – Wilmar van Ommeren May 03 '16 at 18:14
  • 1
    @WilmarvanOmmeren Added another approach. Check it out if it's any better? – Divakar May 03 '16 at 19:38
  • Thanks! I will check it first thing tommorow morning! – Wilmar van Ommeren May 03 '16 at 21:24
  • The second approach decreased my calculation time with an average of 2.6 seconds (using the mask shape of 1400x1400). Thanks a lot! This will save a significant amount of processing time on my complete dataset! – Wilmar van Ommeren May 04 '16 at 06:49