cv2.Rodrigues计算期间的Python结果更改


19

如果我运行:

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)注释掉该行,它就会停止更改 ,这让我觉得这是系统中的错误。


“ e-310”是非常接近0的浮点数。这看起来像是python浮点数表示法的普遍问题,它在每次内存分配上都会有所不同。
Aryerez '19

这真是太不可思议了……对我来说也像个虫子。
朱利安(Julien)

1
IMO的主要内容是,将tvec定义为数组(而不是int或字符串)根本没有效果。一旦完成,就不会回头...我的猜测是tvec是内部状态不应被篡改的cv2.Rodrigues,但该界面似乎允许这种副作用的产生……
Julien

这很混乱。如果展开循环,则将结果存储np.zeros(3)两个不同的变量中时它将起作用。如果我不存储结果或两次使用相同的变量,则不会。也许有些麻木的人可以对此有所启发。
怠惰

1
仅供参考,我在Windows上的Python3中看到了同样的事情……
Julien

Answers:


8

这很可能是未初始化的数组,例如由返回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。确实,rmat没有3个元素的1D 被Python包装器正确拒绝了。我怀疑2D´rmat`s在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.]]

对于np.empty这种行为是众所周知的,因为它占用的内存字节既来之,而不更新现有值。但是cv2.Rodrigues,经过严格的计算后,该函数应该返回一些有意义的值。此外,OP中呈现的奇异值几乎都几乎为零,因此几乎不能视为垃圾。
sciroccorics

1
@sciroccorics您不同意我的第二个片段非常吸引人吗?
保罗·潘泽

我已提交PR以检查输入大小。
Catree

3

绝对是Rodrigues函数中的错误...

如果您阅读了相应的文档,则可能会看到cv2.Rodrigues有两个不同的界面:

模仿C ++接口的接口,其中旋转矢量(和可选的jacobian)通过引用传递,并由函数修改

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

还有一个(更多Pythonic版本),其中旋转向量和jacobian作为元组返回

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

经过进一步调查后编辑:

该功能甚至比预期的还要多:在使用第一个接口时,参数dstjacobian没有被修改,这与文档字符串完全矛盾:

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

换句话说,这显然需要一个错误报告...


其他答案是正确的。问题来自np.eye(4)。该方法需要(3x1或1x3)旋转矢量或(3x3)旋转矩阵。在这里,使用np.eye(4)函数创建具有一定大小的dst。但是由于输入形状错误,因此该方法不执行任何操作并使它统一化。此外,您还指向过时的OpenCV版本。最好使用主版本或指向特定版本:请参阅docs.opencv.org
Catree
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.