user15300490
user15300490

Reputation:

Using np.isclose() to compare np.array()

I want to use np.isclose() to compare pixel arrays of an image. Below is what I'm trying to do:

print(image_array.shape) # (320, 240, 4)

for i in range(0, len(image_array)):
    for j in range(0, len(image_array[i])):
        if np.isclose(image_array[i][j], [1, 0, 0, 1]):
            image_array[i][j] = [0, 1, 0, 1]

But I am getting the following error ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all().

I cannot use np.all() because I want to replace all pixels that are even close enough.

Upvotes: 1

Views: 1234

Answers (3)

hpaulj
hpaulj

Reputation: 231540

image_array[i,j] is a shape (4,) array (you can check that). isclose with that and [1,0,0,1] is a like size boolean array. It can't be used in an if. But you can apply all to that, or use allclose to test if a 'pixel' matches.

But without the double loop you can test as follows:

In [381]: arr = np.random.randint(0,2,(3,6,4))
In [382]: arr.shape
Out[382]: (3, 6, 4)
In [383]: np.isclose(arr,np.array([1,0,0,1]))
Out[383]: 
array([[[ True,  True, False, False],
        [False, False,  True,  True],
        [ True,  True,  True, False],
        [False, False,  True, False],
        [ True, False, False,  True],
        [ True, False, False, False]],

       [[False,  True, False, False],
        [ True,  True,  True, False],
        [ True, False, False,  True],
        [False, False,  True,  True],
        [False,  True,  True, False],
        [False, False,  True,  True]],

       [[False, False, False,  True],
        [ True,  True,  True,  True],
        [ True,  True, False, False],
        [ True, False,  True,  True],
        [False, False, False, False],
        [False, False, False,  True]]])

(Since I used randint arr is int dtype, and == would be just as good as isclose. But the following is the same.

Applying all on the last dimension:

In [384]: np.isclose(arr,np.array([1,0,0,1])).all(axis=-1)
Out[384]: 
array([[False, False, False, False, False, False],
       [False, False, False, False, False, False],
       [False,  True, False, False, False, False]])

here one 'pixel' matches, which we can identify with:

In [385]: I,J = np.nonzero(_)
In [386]: I,J
Out[386]: (array([2]), array([1]))
In [387]: arr[2,1]
Out[387]: array([1, 0, 0, 1])

Or indexing the 2 arrays:

In [389]: arr[I,J]
Out[389]: array([[1, 0, 0, 1]])

This is a (n,4) array, where n is the number of matches (which could be 0). And we can modify arr with that:

In [390]: arr[I,J]=[1,0,1,0]

now there's not match:

In [391]: np.isclose(arr,np.array([1,0,0,1])).all(axis=-1)
Out[391]: 
array([[False, False, False, False, False, False],
       [False, False, False, False, False, False],
       [False, False, False, False, False, False]])

but there are now 3 matches to the new value:

In [392]: np.isclose(arr,np.array([1,0,1,0])).all(axis=-1)
Out[392]: 
array([[ True, False, False, False, False, False],
       [False, False, False, False, False, False],
       [False,  True,  True, False, False, False]])
In [393]: arr[_]          # index with the boolean mask
Out[393]: 
array([[1, 0, 1, 0],
       [1, 0, 1, 0],
       [1, 0, 1, 0]])

Upvotes: 0

NGilbert
NGilbert

Reputation: 501

Rather than using np.isclose, use np.allclose. This will return a single boolean value for that comparison.

Upvotes: 2

Matt
Matt

Reputation: 1166

From the documentation - isclose "Returns a boolean array where two arrays are element-wise equal within a tolerance."

Thus, there is an array of boolean values returned which represents the is-close-ness of each value of the 4-element array. It's up to you to convert that array to a single boolean value.

You might, for instance, only care if any of the 4 match, in which case you can use any(); alternatively, if you care if all match, you can use all().

Upvotes: 0

Related Questions