Hal T Hal T - 3 months ago 28
Python Question

Numpy: array > 5 yields "The truth value of an array with more than one element is ambiguous"

I know that in numpy you can't simply do conditionals on arrays as it doesn't know how to treat them and that this error is a result of that, however in my case my code is a lot simpler. I have:

# H and _H are 3x3 arrays with hand-assigned values
# uv1 is 3x57600 array of coordinates, hand assigned in a loop
HH = np.dot(_H,np.linalg.inv(H))
new_uv = np.dot(HH,uv1)
du = uv1[0,:] * new_uv[2,:]
u = new_uv[0,:] - du
u_greater_5 = u > 5


And the last line gives me the "ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()" error. u is of shape (57600,) and I can open up the interactive prompt and the following works fine:

>>> a = np.array([1,2,3,4,5])
>>> a > 3
array([False, False, False, True, True], dtype=bool)


But the code in the previous block doesn't. I've also tried

np.greater(u,5)
u[u>5] = 1


But they give the same error as well. Any ideas?

Also, I don't know if this is related, but bizarrely, trying to access u[0] gives me a 3-vector of the same 3 values (the first value in u) whereas it should be a scalar? Considering its shape is (57600,) ?

Edit: the traceback, as per request:

Traceback (most recent call last):
File "ros2vid.py", line 333, in <module>
process_frames(bag)
File "ros2vid.py", line 239, in process_frames
u_greater_5 = u > 5
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()


Here's how I generate uv1:

# frame is an image loaded from a ros bag
uv1 = []
im_r = frame.shape[0]
im_c = frame.shape[1]
for i in range(1, im_r):
for j in range(im_c):
uv1.append([j, i, 1])
uv1 = np.transpose(np.array(uv1))


and the values of H and _H are just numbers I hard code in by hand. Something like:

h11 = u0 * cosphi
h12 = -alph_u
h13 = alph_u + u0 * (-cosphi + cz * sinphi)
h21 = -alph_u * sinphi + v0 * cosphi
h22 = 0
h23 = alph_v * (sinphi + cz * cosphi) + v0 * (-cosphi + cz * sinphi)
h31 = cosphi
h32 = 0
h33 = -cosphi + cz * sinphi
H = np.array([[h11, h12, h13], [h21, h22, h23], [h31, h32, h33]])


All the values up there like u0, cosphi, cz, etc. are simply scalars. I've individually checked them, and _H is assigned similarly as well. I've checked the shapes of H and _H and verified them to be 3x3.

Weirdly, I just tried u == 5 and that doesn't give me an error, u > 5 does.

Answer

I suspect that u is a one-dimensional array of objects, and the objects are themselves one-dimensional numpy arrays with length 3. I can reproduce the behavior you report as follows. First, create a one-dimensional array of objects (length 3), and fill it with numpy arrays:

In [26]: u = np.empty(3, dtype=object)

In [27]: u[:] = [np.array([1, 2, 3]), np.array([4, 5, 6]), np.array([7, 8, 9])]

Check the type and attributes of u. First, it is a numpy array with shape (3,).

In [28]: type(u)
Out[28]: numpy.ndarray

In [29]: u.shape
Out[29]: (3,)

The first element of u is an array with length 3:

In [30]: u[0]
Out[30]: array([1, 2, 3])

The dtype of u is dtype('O'), which is numpy's representation for the object data type. (If you print u.dtype, it will show object.)

In [31]: u.dtype
Out[31]: dtype('O')

Now try u > 5:

In [32]: u > 5
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-32-7f680a2a9455> in <module>()
----> 1 u > 5

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

u == 5 doesn't generate an error, but it does generate a warning, and returns the scalar False:

In [33]: u == 5
/Users/warren/miniconda3/bin/ipython:1: DeprecationWarning: elementwise == comparison failed; this will raise an error in the future.
  #!/bin/bash /Users/warren/miniconda3/bin/python.app
Out[33]: False

To figure out why u is an object array, you can work backwards, checking the .shape and .dtype attributes of the variables that you use to create u.