Результат Python змінюється під час обчислення cv2.Rodrigues


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, яка може змінюватись у кожному розподілі пам'яті.
Ар'єрез

Це серйозно дивно ... схоже на помилку і для мене.
Жульєн

1
Головне, що IMO - це те, що визначення tvec як масиву (але не як int або string) має ефект взагалі ... І як тільки ви це зробили, не повертаючи назад ... Моя здогадка tvec - це внутрішній стан cv2.Rodrigues, який не слід підробляти, але, здається, інтерфейс дозволяє такі підробки внаслідок побічного ефекту ...
Julien

Це заплутано. Якщо я розкручую цикл, він буде працювати, коли я зберігаю результат np.zeros(3)у двох різних змінних. Якщо я не зберігаю результат або використовую одну і ту ж змінну двічі, це не буде. Можливо, хтось із більш огидними знаннями може пролити щось на це.
лінощі

1
FYI, те саме я бачу і в Python3 у Windows ...
Julien

Відповіді:


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. Дійсно, 1D, rmatщо не має 3 елементів, правильно відхиляється обгорткою 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функція повинна повертати деякі значущі значення після суворого обчислення. Більше того, дивні значення, представлені в ОП, навряд чи можна вважати сміттям, оскільки всі вони дуже близькі до нуля.
sciroccorics

1
@sciroccorics Ви не згодні з тим, що мій другий фрагмент досить переконливий?
Пол Панцер

Я подав PR, щоб перевірити розмір вводу.
Кетрі

3

Однозначно, це помилка у функції Rodrigues ...

Якщо ви прочитаєте відповідний документ , можливо, ви побачите, що він cv2.Rodriguesмає два різних інтерфейси:

той, що імітує інтерфейс C ++, де вектор обертання (і, можливо, якобіан) передається шляхом посилання та модифікується функцією

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

і один (більше пітонічний), де вектор обертання і якобій повертаються як кортеж

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

EDIT після подальшого розслідування:

Функція ще більш глючная , як і очікувалося: при використанні першого інтерфейсу, параметри dstі jacobianне змінюються, що в загальному contracdiction з рядок документації:

>>> 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 .
Кетрі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.