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个元素的1Drmat
会被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版本。最好使用主版本或指向特定版本:请参阅。