Home > Blockchain >  How to fill 2D binary Numpy array without using for loop?
How to fill 2D binary Numpy array without using for loop?

Time:01-10

Suppose I have a Numpy array a and I want to fill the inner with all 1 like array b

print(a)
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 1., 1., 1., 1., 1., 0., 0., 0.],
       [0., 1., 0., 1., 1., 1., 1., 1., 0., 0.],
       [0., 1., 0., 0., 1., 0., 0., 0., 1., 0.],
       [0., 1., 0., 1., 0., 0., 0., 0., 1., 0.],
       [0., 1., 0., 1., 0., 0., 0., 1., 0., 0.],
       [0., 1., 0., 1., 0., 0., 0., 1., 0., 0.],
       [0., 0., 1., 0., 0., 0., 0., 1., 0., 0.],
       [0., 0., 0., 1., 1., 1., 1., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])

print(b)
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 1., 1., 1., 1., 1., 0., 0., 0.],
       [0., 1., 1., 1., 1., 1., 1., 1., 0., 0.],
       [0., 1., 1., 1., 1., 1., 1., 1., 1., 0.],
       [0., 1., 1., 1., 1., 1., 1., 1., 1., 0.],
       [0., 1., 1., 1., 1., 1., 1., 1., 0., 0.],
       [0., 1., 1., 1., 1., 1., 1., 1., 0., 0.],
       [0., 0., 1., 1., 1., 1., 1., 1., 0., 0.],
       [0., 0., 0., 1., 1., 1., 1., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])

I'm currently using the for loop to do this, are there any ways to do this without using the for loop and only using Numpy? Thanks.

b = np.zeros(a.shape)
for i in range(a.shape[0]):
    occupied = np.where(a[i] == 1)[0]
    if len(occupied) > 0:
        for j in range(occupied[0], occupied[-1]   1):
            b[i][j] = 1

Edit:

  • Only using Numpy
  • The areas I want to fill always have contiguous boundaries.

CodePudding user response:

Can you use scipy? Do the areas you want to fill always have contiguous boundaries?

In [1]: from scipy import ndimage

In [2]: # x = ... copied from OP

In [3]: ndimage.binary_fill_holes(x).astype(int)
Out[3]:
array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 1, 1, 1, 1, 0, 0],
       [0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
       [0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
       [0, 1, 1, 1, 1, 1, 1, 1, 0, 0],
       [0, 1, 1, 1, 1, 1, 1, 1, 0, 0],
       [0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
       [0, 0, 0, 1, 1, 1, 1, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])

CodePudding user response:

This is one way to achieve everything in numpy.

def dilation(x):
    out = x.copy()
    # Get mask of non-zeros and then use it to forward-filled indices
    r, c = x.nonzero()
    bins = np.bincount(r)
    indices = np.cumsum(bins)
    col_slices = np.split(c, indices)
    row_slices = np.split(r, indices)    
    for rw, cl in zip(row_slices, col_slices):
        if cl.size !=0:
            out[rw[0], cl[0]:cl[-1]] = 1
    return out

We can acheive further speed up by changing the last for loop to list comprehesion. It doesn't delegate the work to underlying C but the assembly code generated is shorter than normal for loop and hence faster.

def assign_val(out, rw, cl):
    out[rw[0], cl[0]:cl[-1]] = 1

def dilation(x):
    out = x.copy()
    # Get mask of non-zeros and then use it to forward-filled indices
    r, c = x.nonzero()
    bins = np.bincount(r)
    indices = np.cumsum(bins)
    col_slices = np.split(c, indices)
    row_slices = np.split(r, indices)    
    [assign_val(out, rw, cl) for rw, cl in zip(row_slices, col_slices) if cl.size !=0]
    return out
  • Related