JustANoob
JustANoob

Reputation: 620

Identify the interior of a boolean array / blob - NumPy / Python

Suppose i have a boolean array with shape (nrows,ncols). True represents that i have a defined value (real number) and False represents an undefined / (not of interest) values.

Im trying to figure out an efficient way to extract the rows and cols of both the boundary and the interior, for example if i had the floowing boolean array, where im marking the boundaries by red and the interior by green:

enter image description here

then a desired output would be (the position of the green Trues):

interior = [(2,3), (2,4)]

We can assume that the interior is always connected.

Using np.where(array == False)[0], i get the indices of the Falses, but how to go from here to the boundaries indices and then to the interior ? I can ofcourse loop through each boolean and check if any of the neighbours is False, if no, then its an interior.

Any tips on how to do this efficiently without looping? Another example to be clear:

enter image description here

desired output: interior = [(2,3) , (2,4) , (3,3) , (3,4) , (3,5) , (4,3), (4,4), (4,5)]

The output can be a boolean array as well, containing Trues in interior positions, False otherwise. It does not matter. Thanks in advance.

Upvotes: 2

Views: 895

Answers (2)

Divakar
Divakar

Reputation: 221574

Approach #1

We can use 2D convolution -

from scipy.signal import convolve2d

def interior_indices(a):
    kernel = np.ones((3,3),dtype=int)
    return np.argwhere(convolve2d(a,kernel,'same')==9)

Sample runs -

In [44]: a1
Out[44]: 
array([[False, False, False, False, False, False, False, False],
       [ True,  True,  True,  True,  True,  True, False, False],
       [False,  True,  True,  True,  True,  True,  True, False],
       [False, False,  True,  True,  True,  True, False, False]])

In [45]: interior_indices(a1)
Out[45]: 
array([[2, 3],
       [2, 4]])

In [46]: a2
Out[46]: 
array([[False, False, False, False, False, False, False, False],
       [False,  True,  True,  True,  True,  True, False, False],
       [False,  True,  True,  True,  True,  True,  True, False],
       [False, False,  True,  True,  True,  True,  True, False],
       [False, False,  True,  True,  True,  True,  True, False],
       [False,  True,  True,  True,  True,  True,  True, False],
       [False, False, False,  True,  True, False, False, False]])

In [47]: interior_indices(a2)
Out[47]: 
array([[2, 3],
       [2, 4],
       [3, 3],
       [3, 4],
       [3, 5],
       [4, 3],
       [4, 4],
       [4, 5]])

Approach #2

Alternatively, with uniform-filter -

In [61]: from scipy.ndimage import uniform_filter

In [62]: np.argwhere(uniform_filter(a1,mode='constant'))
Out[62]: 
array([[2, 3],
       [2, 4]])

In [63]: np.argwhere(uniform_filter(a2,mode='constant'))
Out[63]: 
array([[2, 3],
       [2, 4],
       [3, 3],
       [3, 4],
       [3, 5],
       [4, 3],
       [4, 4],
       [4, 5]])

Approach #3

And with binary-erosion -

In [72]: from scipy.ndimage.morphology import binary_erosion

In [73]: kernel = np.ones((3,3),dtype=bool)

In [74]: np.argwhere(binary_erosion(a1,kernel))
Out[74]: 
array([[2, 3],
       [2, 4]])

In [75]: np.argwhere(binary_erosion(a2,kernel))
Out[75]: 
array([[2, 3],
       [2, 4],
       [3, 3],
       [3, 4],
       [3, 5],
       [4, 3],
       [4, 4],
       [4, 5]])

Upvotes: 2

JustANoob
JustANoob

Reputation: 620

Found a way ! If its too trivial, vote delete :)

    #data: the boolean array

    d0 = data[1:-1, 2:]
    d1 = data[:-2, 2:]
    d2 = data[:-2, 1:-1]
    d3 = data[:-2, :-2]
    d4 = data[1:-1, :-2]
    d5 = data[2:, :-2]
    d6 = data[2:, 1:-1]
    d7 = data[2:, 2:]


    interior = np.where(d0 & d1 & d2 & d3 & d4 & d5 & d6 & d7, True, False)

Upvotes: 1

Related Questions