Как извлечь глаз/цель/вверх из матрицы вида с помощью sympy?

Мы долгое время пытались найти в канале #python, как вычислять векторы eye/target/up из матрицы вида с помощью sympy. Один из возможных способов сделать это можно:

from sympy import *
from pprint import pprint

v1, v2, v3, v4 = symbols('v1 v2 v3 v4')
v5, v6, v7, v8 = symbols('v5 v6 v7 v8')
v9, v10, v11, v12 = symbols('v9 v10 v11 v12')
v13, v14, v15, v16 = symbols('v13 v14 v15 v16')

V = Matrix([
    [v1, v2, v3, v4],
    [v5, v6, v7, v8],
    [v9, v10, v11, v12],
    [v13, v14, v15, v16],
    ])
u1, u2, u3 = symbols('u1 u2 u3', real=True)
t1, t2, t3 = symbols('t1 t2 t3', real=True)
e1, e2, e3 = symbols('e1 e2 e3', real=True)

U = Matrix([u1, u2, u3])
T = Matrix([t1, t2, t2])
E = Matrix([e1, e2, e3])

def calculate_view_matrix(up, eye, target):
    zaxis = (eye - target).normalized()
    xaxis = up.cross(zaxis).normalized()
    yaxis = zaxis.cross(xaxis)

    orientation = Matrix([
        [xaxis[0], yaxis[0], zaxis[0], 0],
        [xaxis[1], yaxis[1], zaxis[1], 0],
        [xaxis[2], yaxis[2], zaxis[2], 0],
        [0, 0, 0, 1],
            ])

    translation = Matrix([
            [1, 0, 0, 0],
            [0, 1, 0, 0],
            [0, 0, 1, 0],
            [-eye[0], -eye[1], -eye[2], 1],
            ])

    return orientation * translation

print(V - calculate_view_matrix(U, E, T))

s = solve([
    V - calculate_view_matrix(U, E, T),
    U.norm() - 1,
    T.norm() - 1],
    [u1, u2, u3, t1, t2, t3, e1, e2, e3])

print(s)

Но по какой-то причине script работает в течение ~ 20 минут, а sympy пока не может дать какое-либо решение.

Еще одна попытка также попыталась упростить описанную выше общую проблему до более простого, как вы могли бы вычислить вектор вверх?

В более простом контексте определение проблемы будет примерно таким:

  • u,z,x - это 3D-векторы, которые образуют ортогональный базис .
  • z, x - постоянные векторы
  • u - неизвестный вектор

И уравнение для решения этого вопроса:

u.cross(z).normalized() - x

Если вы попытались решить простой частный случай вышеупомянутого общего уравнения, подобного этому...

from sympy import *
u1,u2,u3=symbols('u1 u2 u3', real = True)
x=Matrix([1,0,0])
z=Matrix([0,0,1])
u=Matrix([u1,u2,u3])

print(solve(u.cross(z).normalized() - x, u))

вы получите NotImplementedError: could not solve u2 - Abs(u2).

NS: Thing для извлечения входных данных из матрицы просмотра требуется, чем функция вычисления матрицы, которая является инъективной или биективной, иначе исходная информация будет потеряна. Если вы не добавляете никаких ограничений, вышеуказанные функции определенно не являются инъекционными, потому что в тот момент, когда используется операция нормализации, функция становится автоматически неинъекционной, например:

a) normalize(x) = x/|x|
b) To prove normalize is injective then normalize(a)=normalize(b) should give a=b
c) normalize(a)=normalize(b) => a/|a|=b/|b| , which is not true then normalize is not injective

Конечно, это может быть тривиально доказано, просто говоря, что бесконечно векторы могут обеспечить тот же нормализованный вектор.

В этой причине для calculate_view_matrix было добавлено несколько ограничений. Т.е.: U.norm() - 1, T.norm() - 1. Теоретически это должно позволить calculate_view_matrix стать инъективным... (или нет:))

Итак, главный вопрос заключается в том, как вы можете ограничить/изменить правильно calculate_view_matrix, чтобы он мог вычислять векторы eye/target/up из матрицы вида?

Ответ 1

Помимо опечатки (T = Matrix([t1, t2, t2])) в вашем ансатце встают несколько недостатков: встать, векторы и целевые векторы обратно из матрицы вида:

  • Матрица представления описывает жесткое преобразование в 3D, которое имеет только 6 степеней свободы (3-осевые вращения, 3-осевые переводы). Это примерно означает, что только 6 из 16 значений v1, v2, ..., v16 могут быть выбраны (более или менее произвольно), другие зависят или определяются каким-либо образом (например, v4 = v8 = v12 = 0, v16 = 1, v3**2 = 1 - v1**2 - v2**2,...), Таким образом, в общем случае уравнения из разности матриц противоречат.
  • Даже если требуется U.norm() - 1 = 0, верхний вектор U может принимать бесконечно много значений (один угол не определяется). Чтобы уменьшить возможные решения для U до конечного числа случаев, можно было бы добавить условие U*(E-T) = 0.
  • Условие T.norm() - 1 = 0 неверно. Это T - E (направление просмотра), которое может/должно быть запрошено иметь длину 1.

В целом я не вижу способа исправить Ansatz s.t. U, E, T может быть вычислен из матрицы вида с использованием уравнений и симпй. Но U, E, T можно легко извлечь из матрицы вида:

  • нормализованный U (выполняющий вышеуказанные требования) можно прочитать из второго столбца
  • -E можно прочитать из последней строки
  • нормализованный вектор представления E - T можно прочитать из третьего столбца

В коде sympy/Python:

def calculate_up_eye_target(viewMat):
  eye = -viewMat[3,0:3].T
  target = eye - viewMat[0:3,2]
  up = viewMat[0:3,1]
  return up, eye, target