Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python result changes during cv2.Rodrigues computation

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.

like image 766
Ian Carr-de Avelon Avatar asked Nov 21 '19 07:11

Ian Carr-de Avelon


2 Answers

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.]]
like image 69
Paul Panzer Avatar answered Nov 04 '22 02:11

Paul Panzer


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...

like image 26
sciroccorics Avatar answered Nov 04 '22 03:11

sciroccorics