I have a MxN numpy array of boolean data representing a cloud mask for GIS data. I want to expand the mask by shifting it in all directions by n cells. It is a simple way of removing cloud shadows (which would be next to cloud pixels) in my mask. These numpy arrays are typically between 1000x1000 and 10000x10000 pixels.
My question is similar to this one but in 2 dimensions.
My very simple but inefficient solution looks like this:
def expand_for(arr, shiftx=1, shifty=1):
arr_b = arr.copy().astype(bool)
for i in range(arr.shape[0]):
for j in range(arr.shape[1]):
if(arr[i,j]):
i_min, i_max = max(i-shifty, 0), min(i+shifty+1, arr.shape[0])
j_min, j_max = max(j-shiftx, 0), min(j+shiftx+1, arr.shape[1])
arr_b[i_min:i_max, j_min:j_max] = True
return arr_b
Another solution is:
def shift_array(arr, x, y):
d, u, r, l = max(y, 0), max(-y, 0), max(x, 0), max(-x, 0)
ret = np.pad(arr, ((d, u), (r, l)), mode='constant')
return ret[u or None: -d or None, l or None: -r or None]
def expand_array(arr, shiftx=1, shifty=1):
return np.dstack([shift_array(arr, x, y)
for x in range(-shiftx, shiftx + 1) for y in range(-shifty, shifty + 1)]).any(axis=2)
But the complexity increases with the size of shiftx and shifty.
Here is a very simple example of what I'm trying to do:
a = np.array([[0,0,0,0,0],
[0,0,0,0,0],
[0,0,1,0,0],
[0,0,0,0,0],
[0,0,0,0,0]])
expand_for(a, 1, 1)
>>> array([[False, False, False, False, False],
[False, True, True, True, False],
[False, True, True, True, False],
[False, True, True, True, False],
[False, False, False, False, False]])
I have been using this code to generate more complex test data:
N = 100 # or 1000, 10000...
a = np.random.random((N,N))
a = (a < 0.30)*1