When testing if a numpy array c
is member of a list of numpy arrays CNTS
:
import numpy as np
c = np.array([[[ 75, 763]],
[[ 57, 763]],
[[ 57, 749]],
[[ 75, 749]]])
CNTS = [np.array([[[ 78, 1202]],
[[ 63, 1202]],
[[ 63, 1187]],
[[ 78, 1187]]]),
np.array([[[ 75, 763]],
[[ 57, 763]],
[[ 57, 749]],
[[ 75, 749]]]),
np.array([[[ 72, 742]],
[[ 58, 742]],
[[ 57, 741]],
[[ 57, 727]],
[[ 58, 726]],
[[ 72, 726]]]),
np.array([[[ 66, 194]],
[[ 51, 194]],
[[ 51, 179]],
[[ 66, 179]]])]
print(c in CNTS)
I get:
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
However, the answer is rather clear: c
is exactly CNTS[1]
, so c in CNTS
should return True!
How to correctly test if a numpy array is member of a list of numpy arrays?
The same problem happens when removing:
CNTS.remove(c)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Application: test if an opencv
contour (numpy array) is member of a list of contours, see for example Remove an opencv contour from a list of contours.
You are getting the error because in
essentially invokes bool(c == x)
on every element x
of CNTS
. It's the __bool__
conversion that is raising the error:
>>> c == CNTS[1]
array([[[ True, True]],
[[ True, True]],
[[ True, True]],
[[ True, True]]])
>>> bool(_)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
The same applies for removal, since it tests for equality with each element.
Containment
The solution is to use np.array_equal
or apply the all
method to each comparison:
any(np.array_equal(c, x) for x in CNTS)
OR
any((c == x).all() for x in CNTS)
Removal
To perform the removal, you are more interested in the index of the element than its existence. The fastest way I can think of is to iterate over the indices, using the elements of CNTS
as comparison keys:
index = next((i for i, x in enumerate(CNTS) if (c == x).all()), -1)
This option short circuits quite nicely, and returns -1
as the default index rather than raising a StopIteration
. You can remove the argument -1
to next
if you prefer the error. If you prefer, you can replace (c == x).all()
with np.array_equal(c, x)
.
Now you can remove as usual:
del CNTS[index]
This solution could work for this case:
def arrayisin(array, list_of_arrays):
for a in list_of_arrays:
if np.array_equal(array, a):
return True
return False
This function iterates over a list of arrays and tests the equality against some other array. So the usage would be:
>>> arrayisin(c, CNTS)
True
To remove the array from the list, you can get the index of the array and then use list.pop
. In the function get_index
, we enumerate the list of arrays, meaning we zip the indices of the list and the contents of the list. If there is a match, we return the index of the match.
def get_index(array, list_of_arrays):
for j, a in enumerate(list_of_arrays):
if np.array_equal(array, a):
return j
return None
idx = get_index(c, CNTS) # 1
CNTS.pop(idx)
Please see the python data structures tutorial for the documentation of list.pop
https://docs.python.org/3/tutorial/datastructures.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With