Gmo
Gmo

Reputation: 21

Numpy array manipulation within range of columns and rows

I have a numpy boolean 2D array that represents a grayscale image which is essentially an unfilled shape (triangle, square, circle) consisting of True for white pixels, and False for black pixels. I would like to add a black fill by modifying the white pixels to black pixels.

array([[True, True, True, False, False, False, False, False, True, True, True],
       [True, True, True, False,  True,  True,  True, False, True, True, True],
       [True, True, True, False,  True,  True,  True, False, True, True, True],
       [True, True, True, False,  True,  True,  True, False, True, True, True],
       [True, True, True, False, False, False, False, False, True, True, True]])

(The 9 True values in a square in the middle of this array should become False.)

Is there a numpy slice method that will make this easy/fast? Something that I can modify all Trues anytime there's a False followed by a True until the next instance of a False?

Upvotes: 2

Views: 186

Answers (2)

Alex Riley
Alex Riley

Reputation: 176750

Here one idea that's easy to implement and should perform reasonably quickly.

I'll use 0s and 1s so it's a little clearer to look at.

Here's the starting array:

>>> a
array([[1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1],
       [1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1],
       [1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1],
       [1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1],
       [1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1]])

Accumulate left-to-right using np.logical_and.accumulate, flip left-to-right, do the same again, flip back, and the "or" the two arrays together:

>>> andacc = np.logical_and.accumulate
>>> (andacc(a, axis=1) | andacc(a[:, ::-1], axis=1)[:, ::-1]).astype(int)
array([[1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1],
       [1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1],
       [1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1],
       [1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1],
       [1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1]])

(Leave out .astype(int) to keep a boolean array instead of 0s and 1s.)

Here's a triangle:

>>> b
array([[1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1],
       [1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1],
       [1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1],
       [0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0]])

>>> (andacc(b, axis=1) | andacc(b[:, ::-1], axis=1)[:, ::-1]).astype(int)
array([[1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1],
       [1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1],
       [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])

Upvotes: 1

akuiper
akuiper

Reputation: 214927

Based on your logic, you can replace all values between the first False and the last False with False:

def mutate(A):
    ind = np.where(~A)[0]
    if len(ind) != 0:
        A[ind.min():ind.max()] = False
    return A


np.apply_along_axis(mutate, 1, arr)

# array([[ True,  True,  True, False, False, False, False, False,  True,
#          True,  True],
#        [ True,  True,  True, False, False, False, False, False,  True,
#          True,  True],
#        [ True,  True,  True, False, False, False, False, False,  True,
#          True,  True],
#        [ True,  True,  True, False, False, False, False, False,  True,
#          True,  True],
#        [ True,  True,  True, False, False, False, False, False,  True,
#          True,  True]], dtype=bool)

Upvotes: 0

Related Questions