1

I have an array (n,m) :

 [255 100 255]
 [100 255 100]
 [255 100 255]

I need to create a new array like that where neigboring values are tested an if North, East, South, West are ALL equal to 100 my value is set to 100 :

 [255 100 255]
 [100 100 100]
 [255 100 255]

I have a simple solution that loops on 1:n and 1:m but it is obviously very slow and I am wondering if there is way to do it faster. I found several links that are talking about sliding window to compute average but i don't see how i can keep track of my indexes to create the new array. Using strides for an efficient moving average filter

Thanks in advance for your inputs.

Community
  • 1
  • 1
Tristan Kpka
  • 60
  • 1
  • 8

1 Answers1

1

Assuming A as the input array, here's one approach using slicing and boolean indexing -

# Get west, north, east & south elements for [1:-1,1:-1] region of input array
W = A[1:-1,:-2]
N  = A[:-2,1:-1]
E = A[1:-1,2:]
S  = A[2:,1:-1]

# Check if all four arrays have 100 for that same element in that region
mask = (W == 100) & (N == 100) & (E == 100) & (S == 100)

# Use the mask to set corresponding elements in a copy version as 100s
out = A.copy()
out[1:-1,1:-1][mask] = 100

Sample run -

In [90]: A
Out[90]: 
array([[220,  93, 205,  82,  23, 210,  22],
       [133, 228, 100,  27, 210, 186, 246],
       [196, 100,  73, 100,  86, 100,  53],
       [195, 131, 100, 142, 100, 214, 100],
       [247,  73, 117, 116,  24, 100,  50]])

In [91]: W = A[1:-1,:-2]
    ...: N  = A[:-2,1:-1]
    ...: E = A[1:-1,2:]
    ...: S  = A[2:,1:-1]
    ...: mask = (W == 100) & (N == 100) & (E == 100) & (S == 100)
    ...: 
    ...: out = A.copy()
    ...: out[1:-1,1:-1][mask] = 100
    ...: 

In [92]: out
Out[92]: 
array([[220,  93, 205,  82,  23, 210,  22],
       [133, 228, 100,  27, 210, 186, 246],
       [196, 100, 100, 100,  86, 100,  53],
       [195, 131, 100, 142, 100, 100, 100],
       [247,  73, 117, 116,  24, 100,  50]])

Such problems are seen largely in signal-processing/image-processing domain. So, you can use 2D convolution too for an alternative solution, like so -

from scipy import signal
from scipy import ndimage

# Use a structuring elements with north, west, east and south elements as 1s
strel = ndimage.generate_binary_structure(2, 1)

# 2D Convolve to get 4s at places that are surrounded by 1s
mask = signal.convolve2d((A==100).astype(int),strel,'same')==4

# Use the mask to set corresponding elements in a copy version as 100
out = A.copy()
out[mask] = 100

Sample run -

In [119]: A
Out[119]: 
array([[108, 184,   0, 176, 131,  86, 201],
       [ 22,  47, 100,  78, 151, 196, 221],
       [185, 100, 142, 100, 121, 100,  24],
       [201, 101, 100, 138, 100,  20, 100],
       [127, 227, 217,  19, 206, 100,  43]])

In [120]: strel = ndimage.generate_binary_structure(2, 1)
     ...: mask = signal.convolve2d((A==100).astype(int),strel,'same')==4
     ...: 
     ...: out = A.copy()
     ...: out[mask] = 100
     ...: 

In [121]: out
Out[121]: 
array([[108, 184,   0, 176, 131,  86, 201],
       [ 22,  47, 100,  78, 151, 196, 221],
       [185, 100, 100, 100, 121, 100,  24],
       [201, 101, 100, 138, 100, 100, 100],
       [127, 227, 217,  19, 206, 100,  43]])

A more straight-forward approach would be with ndimage.binary_closing, which is exactly the intended operation of closing here. So, another alternative way to get the mask would be -

strel = ndimage.generate_binary_structure(2, 1)
mask = ndimage.binary_closing(A==100, structure=strel)
Divakar
  • 218,885
  • 19
  • 262
  • 358
  • Great answer ! Thank you. I link another related post for reference. http://stackoverflow.com/questions/32357087/replace-values-in-specific-columns-of-a-numpy-array?rq=1 – Tristan Kpka Mar 30 '16 at 09:31