21

If I run:

import numpy as np
import cv2

def changes():
    rmat=np.eye(4)
    tvec=np.zeros(3)
    (rvec, jacobian)=cv2.Rodrigues(rmat)
    print rvec

for i in range(2):
    changes()

I get:

[[6.92798859e-310]
 [2.19380404e-316]
 [1.58101007e-322]]
[[0.]
 [0.]
 [0.]]

So the result from changes() changes.

I don't understand why that is, and the fact that it stops changing if the tvec=np.zeros(3) line is commented out, makes me feel that this is a bug in the system.

11
  • "e-310" are floating numbers very close to 0. It looks like the general issue with python floating numbers representation, which can vary on every allocation of memory. Commented Nov 21, 2019 at 7:44
  • This is seriously weird... looks like a bug to me too. Commented Nov 21, 2019 at 7:45
  • 1
    The main thing IMO is that defining tvec as an array (but not as an int or string) has an effect at all... And once you've done it, no turning back... My guess is tvec is an internal state of cv2.Rodrigues that shouldn't be tampered with, yet the interface seems to allow such tampering by side effect... Commented Nov 21, 2019 at 7:51
  • This is confusing. If I unroll the loop, it will work when I store the result of np.zeros(3) in two different variables. If I don't store the result or use the same variable twice, it will not. Maybe someone with more numpy knowledge can shed some light on this. Commented Nov 21, 2019 at 7:55
  • 1
    FYI, I see the same thing in Python3 on Windows... Commented Nov 21, 2019 at 22:19

2 Answers 2

8
+200

This is very likely an uninitialized array such as returned by np.empty. This together with memory recycling can lead to the kind of effect you are seeing. A minimal example would be:

for a in range(5):
    y = np.empty(3,int)
    x = (np.arange(3)+a)**3
    print(x,y)
    del x

# [0 1 8] [94838139529536              0              0]
# [ 1  8 27] [0 1 8]
# [ 8 27 64] [ 1  8 27]
# [ 27  64 125] [ 8 27 64]
# [ 64 125 216] [ 27  64 125]

Observe how at the first iteration y contains garbage and at each subsequent iteration it contains the value of the previous x because it is assigned its memory which has been freed just before.

We can easily check that in the original example it is also the previous tvec that pops up:

def changes():                              
    rmat=np.eye(4)                      
    tvec=np.array([4,0.0,2.5])
    (rvec, jacobian)=cv2.Rodrigues(rmat)
    print(rvec)

for i in range(3):                    
    changes()                               

# [[4.6609787e-310]
#  [0.0000000e+000]
#  [0.0000000e+000]]
# [[4. ]
#  [0. ]
#  [2.5]]
# [[4. ]
#  [0. ]
#  [2.5]]

We may further speculate that it is the peculiar choice of rmat that triggers the error.

It is probably a bug that eye(4) is accepted at all because, officially, rmat should be 3x1 1x3 or 3x3. Indeed, a 1D rmat that doesn't have 3 Elements is correctly rejected by the Python wrapper. My suspicion is that 2D ´rmat`s are not properly checked at the Python level. The C code then detects the wrong shape does nothing except for returning an error code which the Python code doesn't check for.

Indeed using a rmat=eye(3) the effect goes away:

def changes():
    rmat=np.eye(3)
    tvec=np.array([4,0.0,2.5])
    (rvec, jacobian)=cv2.Rodrigues(rmat)
    print(rvec)

for a in range(3):
    changes()

# [[0.]
#  [0.]
#  [0.]]
# [[0.]
#  [0.]
#  [0.]]
# [[0.]
#  [0.]
#  [0.]]
Sign up to request clarification or add additional context in comments.

3 Comments

For np.empty this behavior is well known, because it takes memory bytes as they come, without updating existing values. But the cv2.Rodrigues function is supposed to return some meaningful values, after rigorous computation. Moreover, the strange values presented in the OP can hardly be considered as garbage, as they are all very close to zero.
@sciroccorics wouldn't you agree that my second snippet is pretty compelling?
I have submitted a PR to check for the input size.
4
+100

Definitely, it's a bug in the Rodrigues function...

If you read the corresponding doc, you may see that cv2.Rodrigues has 2 different interfaces:

one that mimics the C++ interface, where the rotation vector (and optionaly the jacobian) are passed by reference and modified by the function

cv2.Rodrigues(src, dst[, jacobian]) --> None

and one (more Pythonic) where the rotation vector and the jacobian are returned as a tuple

cv2.Rodrigues(src) --> dst, jacobian

If you use the first interface, the pb vanishes...

import numpy as np
import cv2

def changes():                              
    rmat=np.eye(4)                      
    tvec=np.zeros(3)
    #(rvec, jacobian)=cv2.Rodrigues(rmat)
    cv2.Rodrigues(rmat, tvec)
    print(tvec)

for i in range(2):                    
    changes()

Result:

[0. 0. 0.]
[0. 0. 0.]

EDIT after further investigation:

The function is even more buggy as expected: when using the first interface, parameters dst and jacobian are not modified, which is in total contracdiction with the docstring:

>>> help(cv2.Rodrigues)
Help on built-in function Rodrigues:

Rodrigues(...)
    Rodrigues(src[, dst[, jacobian]]) -> dst, jacobian
    .   @brief Converts a rotation matrix to a rotation vector or vice versa.
    .   
    .   @param src Input rotation vector (3x1 or 1x3) or rotation matrix (3x3).
    .   @param dst Output rotation matrix (3x3) or rotation vector (3x1 or 1x3), respectively.
    .   @param jacobian Optional output Jacobian matrix, 3x9 or 9x3, which is a matrix of partial
    .   derivatives of the output array components with respect to the input array components.

In other words, this clearly requires a bug report...

1 Comment

Other answer is correct. The problem comes from np.eye(4). The method requires (3x1 or 1x3) rotation vector or (3x3) rotation matrix. Here with np.eye(4) the function creates dst with some size. But since the input shape is wrong, the method does nothing and leaves it unitialized. Also, you are pointing to an obsolete version of OpenCV. It is better to use the master version or to point to a specific version: see docs.opencv.org.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.