Reputation: 21
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 True
s anytime there's a False
followed by a True
until the next instance of a False
?
Upvotes: 2
Views: 186
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
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