Python结果在cv2.Rodrigues计算期间发生变化

Python结果在cv2.Rodrigues计算期间发生变化,python,numpy,opencv,debugging,opencv3.0,Python,Numpy,Opencv,Debugging,Opencv3.0,如果我跑步: 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() 我得到: [[6.92798859e-310] [2.19380404e-316] [1.58101007e-322]] [[0

如果我跑步:

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()
我得到:

[[6.92798859e-310]
 [2.19380404e-316]
 [1.58101007e-322]]
[[0.]
 [0.]
 [0.]]
因此,
changes()
的结果会发生变化


我不明白这是为什么,如果注释掉
tvec=np.zeros(3)
行,它就会停止更改,这让我觉得这是系统中的一个错误。

这很可能是未初始化的数组,如
np.empty
返回的数组。再加上记忆的循环,你会看到这样的效果。一个最简单的例子是:

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]
观察在第一次迭代中
y
如何包含垃圾,在每次后续迭代中它如何包含上一次
x
的值,因为它被分配了之前刚刚释放的内存

我们可以很容易地检查,在原始示例中,弹出的也是前面的
tvec

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]]
我们可以进一步推测,触发错误的是
rmat
的特殊选择

这可能是一个缺陷,
eye(4)
完全被接受,因为官方规定,
rmat
应该是3x1 1x3或3x3。实际上,没有3个元素的1D
rmat
会被Python包装器正确地拒绝。我怀疑2D'rmat'没有在Python级别正确检查。然后,C代码检测到错误的形状,除了返回Python代码没有检查的错误代码之外,什么也不做

实际上,使用
rmat=eye(3)
效果会消失:

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

当然,这是罗德里格斯函数中的一个错误

如果您阅读,您可能会看到
cv2.Rodrigues
有两个不同的接口:

模仿C++接口的一个,其中旋转向量(和选择雅可比)通过引用传递,并被函数

修改。
cv2.Rodrigues(src, dst[, jacobian]) --> None
还有一个(更多Pythonic),其中旋转向量和雅可比矩阵作为元组返回

cv2.Rodrigues(src) --> dst, jacobian
如果使用第一个界面,pb将消失

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()
结果:

[0. 0. 0.]
[0. 0. 0.]
进一步调查后编辑:

该函数甚至比预期的更容易出错:当使用第一个接口时,参数
dst
jacobian
不会被修改,这与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.

换句话说,这显然需要一份错误报告。

“e-310”是非常接近于0的浮点数。这看起来像是python浮点数表示法的一般问题,它在每次内存分配时都会有所不同。。。在我看来,这也是一个bug。在我看来,主要的一点是,将tvec定义为数组(而不是int或字符串)会产生任何影响。。。一旦你做到了,就别回头。。。我的猜测是tvec是cv2的一种内部状态。罗德里格斯不应该被篡改,但界面似乎允许通过副作用进行此类篡改……这令人困惑。如果我展开循环,当我将
np.zero(3)
的结果存储在两个不同的变量中时,它将起作用。如果我不存储结果或两次使用同一个变量,它将不会。也许有更多numpy知识的人可以对此有所了解。仅供参考,我在Windows上的Python3中也看到了同样的情况……对于
np.empty
这种行为是众所周知的,因为它会占用内存字节,而不会更新现有值。但是经过严格的计算,
cv2.Rodrigues
函数应该返回一些有意义的值。此外,OP中出现的奇怪值很难被视为垃圾,因为它们都非常接近于零。@SciroCorics您不同意我的第二个代码片段非常有说服力吗?我已经提交了一份报告来检查输入大小。这是正确的。这个问题来自于np.eye(4)。该方法需要(3x1或1x3)旋转矢量或(3x3)旋转矩阵。这里使用np.eye(4)函数创建具有一定大小的dst。但由于输入的形状是错误的,因此该方法不做任何操作,而是将其统一化。另外,您指向的是一个过时的OpenCV版本。最好使用主版本或指向特定版本:请参阅。