Il risultato di Python cambia durante il calcolo di cv2.Rodrigues


19

Se corro:

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

Ottengo:

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

Quindi il risultato dai changes()cambiamenti.

Non capisco perché, e il fatto che smetta di cambiare se la tvec=np.zeros(3)riga viene commentata, mi fa sentire che si tratta di un bug nel sistema.


"e-310" sono numeri fluttuanti molto vicini a 0. Sembra il problema generale con la rappresentazione di numeri fluttuanti in python, che può variare su ogni allocazione di memoria.
Aryerez,

Questo è davvero strano ... sembra anche un bug per me.
Julien

1
La cosa principale IMO è che definire tvec come un array (ma non come un int o una stringa) ha un effetto ... E una volta che lo hai fatto, non tornare indietro ... La mia ipotesi è che tvec sia uno stato interno di cv2.Rodrighi che non dovrebbero essere manomessi, tuttavia l'interfaccia sembra consentire tale manomissione per effetto collaterale ...
Julien

Questo è confusionario. Se srotondo il ciclo, funzionerà quando memorizzerò il risultato np.zeros(3)in due diverse variabili. Se non memorizzo il risultato o non uso la stessa variabile due volte, non lo farà. Forse qualcuno con una conoscenza più insensibile può far luce su questo.
bradipo

1
Cordiali saluti, vedo la stessa cosa in Python3 su Windows ...
Julien

Risposte:


8

Questo è molto probabilmente un array non inizializzato come restituito da np.empty. Questo insieme al riciclo della memoria può portare al tipo di effetto che stai vedendo. Un esempio minimo sarebbe:

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]

Osserva come alla prima iterazione ycontenga spazzatura e ad ogni iterazione successiva contiene il valore del precedente xperché gli è stata assegnata la memoria che è stata liberata poco prima.

Possiamo facilmente verificare che nell'esempio originale compaia anche il precedente 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]]

Potremmo inoltre ipotizzare che sia la scelta peculiare di rmatciò che innesca l'errore.

Probabilmente si tratta di un bug eye(4)accettato perché, ufficialmente, rmatdovrebbe essere 3x1 1x3 o 3x3. In effetti, una 1D rmatche non ha 3 elementi viene rifiutata correttamente dal wrapper Python. Il mio sospetto è che i 2D 2D non siano controllati correttamente a livello di Python. Il codice C quindi rileva che la forma sbagliata non fa altro che restituire un codice di errore che il codice Python non controlla.

Effettivamente usando un rmat=eye(3)effetto scompare:

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

Per np.emptyquesto comportamento è ben noto, perché prende byte di memoria come vengono, senza aggiornare i valori esistenti. Ma la cv2.Rodriguesfunzione dovrebbe restituire alcuni valori significativi, dopo un rigoroso calcolo. Inoltre, gli strani valori presentati nell'OP possono difficilmente essere considerati spazzatura, poiché sono tutti molto vicini allo zero.
sciroccorics,

1
@sciroccorics non saresti d'accordo sul fatto che il mio secondo frammento sia abbastanza convincente?
Paul Panzer,

Ho inviato un PR per verificare la dimensione dell'input.
Catree,

3

Sicuramente, è un bug nella funzione Rodrigues ...

Se leggi il documento corrispondente , potresti vedere che cv2.Rodriguesha 2 interfacce diverse:

uno che imita l'interfaccia C ++, in cui il vettore di rotazione (e facoltativamente il jacobian) viene passato per riferimento e modificato dalla funzione

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

e uno (più Pythonic) in cui il vettore di rotazione e il jacobian vengono restituiti come tupla

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

Se usi la prima interfaccia, il pb svanisce ...

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

Risultato:

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

MODIFICA dopo ulteriori indagini:

La funzione è ancora più buggy come previsto: quando si utilizza la prima interfaccia, i parametri dste jacobiannon vengono modificati, il che è in totale contraccezione con il 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 altre parole, ciò richiede chiaramente una segnalazione di bug ...


L'altra risposta è corretta. Il problema viene np.eye(4). Il metodo richiede (3x1 o 1x3) vettore di rotazione o (3x3) matrice di rotazione. Qui con np.eye (4) la funzione crea dst con alcune dimensioni. Ma poiché la forma di input è errata, il metodo non fa nulla e lo lascia unitizzato. Inoltre, stai indicando una versione obsoleta di OpenCV. È meglio utilizzare la versione principale o puntare a una versione specifica: consultare docs.opencv.org .
Catree,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.