egayer
egayer

Reputation: 41

Find elements based on neighbor values

I would like to know if there is an efficient way to find indexes of elements next to a specific value in a Numpy array.

How can I find indexes of all the elements that are equal to 1 and that are next to a 0 in this array A ? Without a loop checking for the value of the 8 surrounded elements for each element ?

       A = [[ 0.,  0.,  0.,  0.,  0.,  0.],
           [ 0.,  1.,  1.,  1.,  1.,  0.],
           [ 0.,  1.,  1.,  1.,  1.,  0.],
           [ 0.,  1.,  1.,  1.,  1.,  0.],
           [ 0.,  1.,  1.,  1.,  1.,  0.],
           [ 0.,  0.,  0.,  0.,  0.,  0.]]

I would expect getting the indexes from a result like this :

              [[ False,  False,  False,  False,  False, False],
               [ False,  True,   True,   True,   True,  False],
               [ False,  True,   False,  False,  True,  False],
               [ False,  True,   False,  False,  True,  False],
               [ False,  True,   True,   True,   True,  False],
               [ False,  False,  False,  False,  False, False]]

I used canny edge detection but it does not always work for elements that only have one 0 on the North/South-West or North/South-East neighbor. For example :

           B = [[ 1.,  0.,  0.],
               [ 1.,  0.,  0.],
               [ 1.,  1.,  1.]]

can lead to

                   [[ True,  False,  False],
                   [ True,   False,  False],
                   [ False,  True,   True]]

instead of

                       [[ True,  False,  False],
                       [ True,   False,  False],
                       [ True,   True,   True]]

Thanks

update 1 : I did try first canny edge detection form scikit.image but it misses elements. I then tried with np.gradient with same results.

update 2 : Example :

B=np.array([[1,1,1,0,0,0,0],
            [1,1,1,0,0,0,0],
            [1,1,1,1,0,0,0],
            [1,1,1,1,0,0,0],
            [1,1,1,1,1,0,0],
            [1,1,1,1,1,0,0],
            [1,1,1,0,0,0,0],
            [1,1,1,1,0,0,0]])

On this example, both the canny edge detection, the gradient method, and the ndimage.laplace (method mentioned in the answer below) lead to the same results, with missing elements (in yellow on the figure) results

update 2:

Here is the looping method

def check_8neigh_Value(arr,eltvalue, neighvalue):
   "checking if the element of value=eltvalue is surrounded 
    by the value=neighvalue and returning the corresponding grid"

   l, c = np.shape(arr)
   contour = np.zeros(np.shape(arr))
   for i in np.arange(1,l-1):
      for j in np.arange(1,c-1):
         window = arr[i-1:i+2, j-1:j+2]
         if np.logical_and(arr[i,j]==eltvalue,neighvalue in window):
            contour[i,j]=1

return contour

image=check_8neigh_Value(B,1,0)

It gives me what I am looking for, however it is not efficient on large array.

I am stuck with the as_strided method, since I don't low how to use the result:

For a 3 by 3 window using the array B, I am able to get the as_stried B but can't get further.

window_h=3
window_w=3
l, c = image.shape
l_new, c_new = l - window_h + 1, c - window_w + 1
shape=[c_new, l_new, window_w, window_h]
strides=B.strides + B.strides
strided_image = np.lib.stride_tricks.as_strided(B,shape=shape,strides=strides)

Upvotes: 3

Views: 1431

Answers (1)

ali_m
ali_m

Reputation: 74162

Here's one approach using binary erosion:

import numpy as np
from scipy import ndimage

eroded = ndimage.binary_erosion(A, np.eye(3))
diff = (A - eroded).astype(np.bool)
print(repr(diff))
# array([[False, False, False, False, False, False],
#        [False,  True,  True,  True,  True, False],
#        [False,  True, False, False,  True, False],
#        [False,  True, False, False,  True, False],
#        [False,  True,  True,  True,  True, False],
#        [False, False, False, False, False, False]], dtype=bool)

You could also take the Laplacian of your input array and find where it is negative:

lap = ndimage.laplace(A)
print(repr(lap < 0))
# array([[False, False, False, False, False, False],
#        [False,  True,  True,  True,  True, False],
#        [False,  True, False, False,  True, False],
#        [False,  True, False, False,  True, False],
#        [False,  True,  True,  True,  True, False],
#        [False, False, False, False, False, False]], dtype=bool)

Upvotes: 4

Related Questions