Python result changes during cv2.Rodrigues computation
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.]]
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...