Alterações no resultado do Python durante o cálculo do cv2.


19

Se eu correr:

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()

Eu recebo:

[[6.92798859e-310]
 [2.19380404e-316]
 [1.58101007e-322]]
[[0.]
 [0.]
 [0.]]

Então, o resultado das changes()mudanças.

Eu não entendo por que isso é, e o fato de ele parar de mudar se a tvec=np.zeros(3)linha for comentada, me faz sentir que isso é um bug no sistema.


"e-310" são números flutuantes muito próximos de 0. Parece que o problema geral da representação de números flutuantes python, que pode variar em cada alocação de memória.
— Aryerez

Isso é muito estranho ... parece um bug para mim também.
— Julien

11
A principal coisa da IMO é que a definição de tvec como uma matriz (mas não como um int ou string) tem efeito algum ... E uma vez feito isso, não há como voltar atrás ... Meu palpite é que tvec é um estado interno de cv2.Rodrigues que não deve ser adulterados, ainda que a interface parece permitir tal violação por efeito colateral ...
— Julien

Isso é confuso. Se eu desenrolar o loop, ele funcionará quando eu armazenar o resultado np.zeros(3)em duas variáveis diferentes . Se eu não armazenar o resultado ou usar a mesma variável duas vezes, não será. Talvez alguém com um conhecimento mais entorpecido possa esclarecer isso.
— preguiça

11
FYI, eu vejo a mesma coisa em Python3 no Windows ... #
— Julien

Respostas:


8

É muito provável que uma matriz não inicializada, como retornada por np.empty. Isso, juntamente com a reciclagem da memória, pode levar ao tipo de efeito que você está vendo. Um exemplo mínimo seria:

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 como na primeira iteração ycontém lixo e a cada iteração subseqüente, ele contém o valor do anterior, xporque é atribuída sua memória que foi liberada pouco antes.

Podemos verificar facilmente que, no exemplo original, também é o anterior tvecque aparece:

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

Podemos especular ainda que é a escolha peculiar de rmatquem desencadeia o erro.

Provavelmente, é um erro eye(4)aceito, porque, oficialmente, rmatdeve ser 3x1 1x3 ou 3x3. De fato, um 1D rmatque não possui 3 elementos é corretamente rejeitado pelo wrapper Python. Minha suspeita é que os 2D´rmat`s não são devidamente verificados no nível do Python. O código C, em seguida, detecta que a forma incorreta não faz nada, exceto retornar um código de erro que o código Python não verifica.

De fato, usar um rmat=eye(3)efeito desaparece:

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

Pois np.emptyesse comportamento é bem conhecido, porque leva os bytes de memória à medida que eles chegam, sem atualizar os valores existentes. Mas a cv2.Rodriguesfunção deve retornar alguns valores significativos, após um cálculo rigoroso. Além disso, os valores estranhos apresentados no OP dificilmente podem ser considerados lixo, pois estão todos muito próximos de zero.
— sciroccorics

11
@sciroccorics você não concorda que meu segundo trecho é bastante atraente?
— Paul Panzer

Enviei um PR para verificar o tamanho da entrada.
— Catree 27/12/19

3

Definitivamente, é um bug na função Rodrigues ...

Se você ler o documento correspondente , poderá ver que cv2.Rodriguespossui 2 interfaces diferentes:

aquele que imita a interface C ++, onde o vetor de rotação (e opcionalmente o jacobiano) é passado por referência e modificado pela função

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

e um (mais Pythonic) onde o vetor de rotação e o jacobiano são retornados como uma tupla

cv2.Rodrigues(src) --> dst, jacobian

Se você usar a primeira interface, o pb desaparece ...

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()

Resultado:

[0. 0. 0.]
[0. 0. 0.]

EDITAR após uma investigação mais aprofundada:

A função é ainda mais incorreta conforme o esperado: ao usar a primeira interface, os parâmetros dste jacobiannão são modificados, o que contraria totalmente a doutrina:

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

Em outras palavras, isso claramente requer um relatório de erro ...


Outra resposta está correta. O problema vem np.eye(4). O método requer vetor de rotação (3x1 ou 1x3) ou matriz de rotação (3x3). Aqui com np.eye (4) a função cria dst com algum tamanho. Mas como o formato de entrada está errado, o método não faz nada e o deixa unitizado. Além disso, você está apontando para uma versão obsoleta do OpenCV. É melhor usar a versão principal ou apontar para uma versão específica: consulte docs.opencv.org .
— Catree 27/12/19
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.