Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)
like image 226
egayer Avatar asked Jun 24 '26 05:06

egayer


1 Answers

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)
like image 170
ali_m Avatar answered Jun 25 '26 19:06

ali_m