it-swarm-pt.tech

Como projeto reverso pontos 2D em 3D?

Eu tenho 4 pontos 2D no espaço da tela e preciso projetá-los de volta para o espaço 3D. Eu sei que cada um dos 4 pontos é um canto de um retângulo rígido rotacionado em 3D e sei o tamanho do retângulo. Como posso obter coordenadas 3D com isso?

Não estou usando nenhuma API específica e não tenho uma matriz de projeção existente. Eu só estou procurando matemática básica para fazer isso. É claro que não há dados suficientes para converter um único ponto 2D em 3D sem outra referência, mas imagino que, se você tiver 4 pontos, sabe que todos estão em ângulo reto um com o outro no mesmo plano, e você sabe a distância entre eles, deve ser capaz de descobrir a partir daí. Infelizmente, não consigo entender como.

Isso pode se enquadrar na fotogrametria, mas as pesquisas no Google não me levaram a nenhuma informação útil.

56
Joshua Carmody

Tudo bem, eu vim aqui procurando uma resposta e não encontrei algo simples e direto, então fui em frente e fiz o estúpido, mas eficaz (e relativamente simples): otimização de Monte Carlo.

Em termos simples, o algoritmo é o seguinte: Perturbar aleatoriamente sua matriz de projeção até que projete suas coordenadas 3D conhecidas nas coordenadas 2D conhecidas.

Aqui está uma foto estática de Thomas the Tank Engine:

Thomas the Tank Engine

Digamos que usamos o GIMP para encontrar as coordenadas 2D do que pensamos ser um quadrado no plano do solo (se é realmente um quadrado depende ou não do seu julgamento da profundidade):

With an outline of the square

Eu recebo quatro pontos na imagem 2D: (318, 247), (326, 312), (418, 241) E (452, 303).

Por convenção, dizemos que esses pontos devem corresponder aos pontos 3D: (0, 0, 0), (0, 0, 1), (1, 0, 0) E (1, 0, 1). Em outras palavras, uma unidade quadrada no plano y = 0.

A projeção de cada uma dessas coordenadas 3D em 2D é feita multiplicando o vetor 4D [x, y, z, 1] Por uma matriz de projeção 4x4, depois dividindo os componentes xey por z para obter a correção da perspectiva. Isso é mais ou menos o que gluProject () faz, exceto gluProject() também leva em conta a viewport atual e leva em conta uma matriz modelview separada (podemos apenas assumir que a matriz modelview é a matriz de identidade). É muito útil olhar para a documentação gluProject() porque, na verdade, quero uma solução que funcione para o OpenGL, mas cuidado com o fato de a documentação estar sem a divisão por z na fórmula.

Lembre-se, o algoritmo deve começar com alguma matriz de projeção e perturbá-la aleatoriamente até que ela produza a projeção que queremos. Então, o que vamos fazer é projetar cada um dos quatro pontos 3D e ver o quão perto chegamos dos pontos 2D que queríamos. Se nossas perturbações aleatórias fizerem com que os pontos 2D projetados se aproximem dos que marcamos acima, manteremos essa matriz como uma melhoria em relação ao nosso palpite inicial (ou anterior).

Vamos definir nossos pontos:

# Known 2D coordinates of our rectangle
i0 = Point2(318, 247)
i1 = Point2(326, 312)
i2 = Point2(418, 241)
i3 = Point2(452, 303)

# 3D coordinates corresponding to i0, i1, i2, i3
r0 = Point3(0, 0, 0)
r1 = Point3(0, 0, 1)
r2 = Point3(1, 0, 0)
r3 = Point3(1, 0, 1)

Precisamos começar com alguma matriz, a matriz de identidade parece uma escolha natural:

mat = [
    [1, 0, 0, 0],
    [0, 1, 0, 0],
    [0, 0, 1, 0],
    [0, 0, 0, 1],
]

Precisamos realmente implementar a projeção (que é basicamente uma multiplicação de matrizes):

def project(p, mat):
    x = mat[0][0] * p.x + mat[0][1] * p.y + mat[0][2] * p.z + mat[0][3] * 1
    y = mat[1][0] * p.x + mat[1][1] * p.y + mat[1][2] * p.z + mat[1][3] * 1
    w = mat[3][0] * p.x + mat[3][1] * p.y + mat[3][2] * p.z + mat[3][3] * 1
    return Point(720 * (x / w + 1) / 2., 576 - 576 * (y / w + 1) / 2.)

Isso é basicamente o que gluProject() faz, 720 e 576 são a largura e a altura da imagem, respectivamente (ou seja, a viewport), e subtraímos de 576 para contar o fato de contarmos as coordenadas y a partir do topo enquanto o OpenGL normalmente os conta da parte inferior. Você notará que não estamos calculando z, é porque realmente não precisamos dele aqui (embora possa ser útil garantir que ele esteja dentro do intervalo que o OpenGL usa para o buffer de profundidade).

Agora precisamos de uma função para avaliar quão próximos estamos da solução correta. O valor retornado por esta função é o que usaremos para verificar se uma matriz é melhor que outra. Eu escolhi ir pela soma das distâncias ao quadrado, ou seja:

# The squared distance between two points a and b
def norm2(a, b):
    dx = b.x - a.x
    dy = b.y - a.y
    return dx * dx + dy * dy

def evaluate(mat): 
    c0 = project(r0, mat)
    c1 = project(r1, mat)
    c2 = project(r2, mat)
    c3 = project(r3, mat)
    return norm2(i0, c0) + norm2(i1, c1) + norm2(i2, c2) + norm2(i3, c3)

Para perturbar a matriz, simplesmente escolhemos um elemento para perturbar por uma quantidade aleatória dentro de um intervalo:

def perturb(amount):
    from copy import deepcopy
    from random import randrange, uniform
    mat2 = deepcopy(mat)
    mat2[randrange(4)][randrange(4)] += uniform(-amount, amount)

(Vale a pena notar que nossa função project() na verdade não usa mat[2], Pois não computamos z, e como todas as nossas coordenadas y são 0, o mat[*][1] também são irrelevantes. Poderíamos usar esse fato e nunca tentar perturbar esses valores, o que daria uma pequena aceleração, mas isso é deixado como um exercício ...)

Por conveniência, vamos adicionar uma função que faz a maior parte da aproximação chamando perturb() repetidamente sobre a melhor matriz que encontramos até agora:

def approximate(mat, amount, n=100000):
    est = evaluate(mat)

    for i in xrange(n):
        mat2 = perturb(mat, amount)
        est2 = evaluate(mat2)
        if est2 < est:
            mat = mat2
            est = est2

    return mat, est

Agora tudo o que resta a fazer é executá-lo ...:

for i in xrange(100):
    mat = approximate(mat, 1)
    mat = approximate(mat, .1)

Acho que isso já dá uma resposta bastante precisa. Depois de um tempo, a matriz que encontrei foi:

[
    [1.0836000765696232,  0,  0.16272110011060575, -0.44811064935115597],
    [0.09339193527789781, 1, -0.7990570384334473,   0.539087345090207  ],
    [0,                   0,  1,                    0                  ],
    [0.06700844759602216, 0, -0.8333379578853196,   3.875290562060915  ],
]

com um erro de cerca de 2.6e-5. (Observe como os elementos que dissemos que não foram usados ​​no cálculo não foram realmente alterados de nossa matriz inicial; isso ocorre porque alterar essas entradas não alteraria o resultado da avaliação e, portanto, a alteração nunca seria levada adiante.)

Podemos passar a matriz para o OpenGL usando glLoadMatrix() (mas lembre-se de transpor primeiro e lembre-se de carregar sua matriz modelview com a matriz de identidade):

def transpose(m):
    return [
        [m[0][0], m[1][0], m[2][0], m[3][0]],
        [m[0][1], m[1][1], m[2][1], m[3][1]],
        [m[0][2], m[1][2], m[2][2], m[3][2]],
        [m[0][3], m[1][3], m[2][3], m[3][3]],
    ]

glLoadMatrixf(transpose(mat))

Agora, por exemplo, podemos traduzir ao longo do eixo z para obter posições diferentes ao longo das trilhas:

glTranslate(0, 0, frame)
frame = frame + 1

glBegin(GL_QUADS)
glVertex3f(0, 0, 0)
glVertex3f(0, 0, 1)
glVertex3f(1, 0, 1)
glVertex3f(1, 0, 0)
glEnd()

With 3D translation

Com certeza isso não é muito elegante do ponto de vista matemático; você não obtém uma equação de formulário fechado na qual pode simplesmente conectar seus números e obter uma resposta direta (e precisa). NO ENTANTO, permite adicionar restrições adicionais sem ter que se preocupar em complicar suas equações; por exemplo, se quiséssemos incorporar a altura também, poderíamos usar esse canto da casa e dizer (em nossa função de avaliação) que a distância do chão ao telhado deve ser assim e executar o algoritmo novamente. Então, sim, é uma espécie de força bruta, mas funciona e funciona bem.

Choo choo!

67
Vegard

Esse é o problema clássico da realidade aumentada baseada em marcadores.

Você tem um marcador quadrado (código de barras 2D) e deseja encontrar sua pose (translação e rotação em relação à câmera), depois de encontrar as quatro bordas do marcador. Visão geral da imagem

Não estou ciente das mais recentes contribuições para o campo, mas pelo menos até certo ponto (2009) o RPP deveria superar o POSIT mencionado acima (e é realmente uma abordagem clássica para isso). Consulte os links, eles também fornecer fonte.

(PS - eu sei que é um tópico um pouco antigo, mas de qualquer maneira, o post pode ser útil para alguém)

7
dim_tz

D. DeMenthon criou um algoritmo para calcular a pose de um objeto (sua posição e orientação no espaço) a partir de pontos de recurso em uma imagem 2D ao conhecer o modelo do objeto - este é o seu problema exato :

Descrevemos um método para encontrar a pose de um objeto a partir de uma única imagem. Assumimos que podemos detectar e combinar na imagem quatro ou mais pontos de característica não-planares do objeto, e que sabemos sua geometria relativa no objeto.

O algoritmo é conhecido como Posit e é descrito no artigo clássico "Pose de objeto baseado em modelo em 25 linhas de código" (disponível em seu site , seção 4).

Link direto ao artigo: http://www.cfar.umd.edu/~daniel/daniel_papersfordownload/Pose25Lines.pdf Implementação do OpenCV: http://opencv.willowgarage.com/ wiki/Posit

A idéia é aproximar repetidamente a projeção em perspectiva através de uma projeção ortográfica em escala até convergir para uma pose precisa.

5
Julien-L

No espaço 2-D, haverá 2 retângulos válidos que podem ser construídos. Sem conhecer a projeção da matriz original, você não saberá qual é a correta. É o mesmo que o problema da "caixa": você vê dois quadrados, um dentro do outro, com os 4 vértices internos conectados aos 4 respectivos vértices externos. Você está olhando para uma caixa de cima para baixo ou de baixo para cima?

Dito isto, você está procurando uma transformação de matriz T onde ...

{{x1, y1, z1}, {x2, y2, z2}, {x3, y3, z3}, {x4, y4, z4}} x T = {{x1, y1}, {x2, y2}, { x3, y3}, {x4, y4}}

(4 x 3) x T = (4 x 2)

Então T deve ser uma matriz (3 x 2). Então, temos 6 incógnitas.

Agora crie um sistema de restrições em T e resolva com o Simplex. Para criar restrições, você sabe que uma linha que passa pelos dois primeiros pontos deve ser paralela à linha que passa aos dois segundos pontos. Você sabe que uma linha que passa pelos pontos 1 e 3 deve ser paralela às linhas que passam pelos pontos 2 e 4. Você sabe que uma linha que passa pelos pontos 1 e 2 deve ser ortogonal a uma linha que passa pelos pontos 2 e 3. Você sabe que o comprimento da linha de 1 e 2 deve ser igual ao comprimento da linha de 3 e 4. Você sabe que o comprimento da linha de 1 e 3 deve ser igual ao comprimento da linha de 2 e 4.

Para tornar isso ainda mais fácil, você conhece o retângulo e o comprimento de todos os lados.

Isso deve lhe dar muitas restrições para resolver esse problema.

Claro, para voltar, você pode encontrar T-inverso.

@ Rob: Sim, há um número infinito de projeções, mas não um número infinito de projetos em que os pontos devem atender aos requisitos de um retângulo.

@nlucaroni: Sim, isso só pode ser solucionado se você tiver quatro pontos na projeção. Se o retângulo se projetar para apenas 2 pontos (ou seja, o plano do retângulo for ortogonal à superfície da projeção), isso não poderá ser resolvido.

Hummm ... Eu deveria ir para casa e escrever esta pequena jóia. Isso parece divertido.

Atualizações:

  1. Há um número infinito de projeções, a menos que você fixe um dos pontos. Se você fixar os pontos do retângulo original, existem dois possíveis retângulos originais.
4
Jarrett Meyer

Para o meu mecanismo OpenGL, o seguinte fragmento converterá as coordenadas do mouse/tela em coordenadas do mundo 3D. Leia os comentários para obter uma descrição real do que está acontecendo.

/* FUNÇÃO: YCamera :: CalculateWorldCoordinates 
 ARGUMENTOS: x mouse x coordenada 
 Y mouse y coordenada 
 Vec onde armazenar as coordenadas 
 RETORNAR: n/a 
 DESCRIÇÃO: Converter coordenadas do mouse em coordenadas do mundo 
 */

void YCamera :: CalculateWorldCoordinates(float x, float y, YVector3 *vec) { // START GLint viewport[4]; GLdouble mvmatrix[16], projmatrix[16];

GLint real_y;
GLdouble mx, my, mz;

glGetIntegerv(GL_VIEWPORT, viewport);
glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix);
glGetDoublev(GL_PROJECTION_MATRIX, projmatrix);

real_y = viewport[3] - (GLint) y - 1;   // viewport[3] is height of window in pixels
gluUnProject((GLdouble) x, (GLdouble) real_y, 1.0, mvmatrix, projmatrix, viewport, &mx, &my, &mz);

/*  'mouse' is the point where mouse projection reaches FAR_PLANE.
    World coordinates is intersection of line(camera->mouse) with plane(z=0) (see LaMothe 306)

    Equation of line in 3D:
        (x-x0)/a = (y-y0)/b = (z-z0)/c      

    Intersection of line with plane:
        z = 0
        x-x0 = a(z-z0)/c  <=> x = x0+a(0-z0)/c  <=> x = x0 -a*z0/c
        y = y0 - b*z0/c

*/
double lx = fPosition.x - mx;
double ly = fPosition.y - my;
double lz = fPosition.z - mz;
double sum = lx*lx + ly*ly + lz*lz;
double normal = sqrt(sum);
double z0_c = fPosition.z / (lz/normal);

vec->x = (float) (fPosition.x - (lx/normal)*z0_c);
vec->y = (float) (fPosition.y - (ly/normal)*z0_c);
vec->z = 0.0f;
</code>

}

4
user14208

Para acompanhar a abordagem de Rons: Você pode encontrar seus valores-z se souber como girou seu retângulo.

O truque é encontrar a matriz projetiva que fez a projeção. Felizmente, isso é possível e até barato. A matemática relevante pode ser encontrada no artigo "Mapeamentos projetivos para distorção de imagem", de Paul Heckbert.

http://pages.cs.wisc.edu/~dyer/cs766/readings/heckbert-proj.pdf

Dessa forma, você pode recuperar a parte homogênea de cada vértice que foi perdida durante a projeção.

Agora você ainda tem quatro linhas em vez de pontos (como Ron explicou). Como você sabe o tamanho do seu retângulo original, no entanto, nada se perde. Agora você pode conectar os dados do método de Ron e da abordagem 2D em um solucionador de equações linear e resolver z. Você obtém os valores z exatos de cada vértice dessa maneira.

Nota: Isso funciona porque:

  1. A forma original era um retângulo
  2. Você sabe o tamanho exato do retângulo no espaço 3D.

É realmente um caso especial.

Espero que ajude, Nils

2
Nils Pipenbrinck

Supondo que os pontos realmente façam parte de um retângulo, estou dando uma ideia genérica:

Encontre dois pontos com inter-distância máxima: eles provavelmente definem uma diagonal (exceção: casos especiais em que o retângulo é quase paralelo ao plano YZ, deixado para o aluno). Chame-os de A, C. Calcule os ângulos BAD, BCD. Estes, em comparação com ângulos retos, dão orientação no espaço 3D. Para descobrir a distância z, é necessário correlacionar os lados projetados com os lados conhecidos e, com base no método de projeção em 3D (é 1/z?), Você está no caminho certo para saber as distâncias.

2
tzot

Vou pegar meu livro de álgebra linear quando chegar em casa se ninguém responder. Mas, nem todas as matrizes são invertíveis. Matrizes singulares não são invertíveis (quando determinante = 0). Isso realmente acontece o tempo todo, uma vez que uma matriz de projeção deve ter autovalores de 0 e 1 e ser quadrada (uma vez que é idempotente, então p ^ 2 = p).

Um exemplo fácil é, [[0 1] [0 1]] desde o determinante = 0, e essa é uma projeção na linha x = y!

1
nlucaroni

A projeção que você tem na superfície 2D possui infinitos retângulos 3D que serão projetados para a mesma forma 2D.

Pense desta maneira: você tem quatro pontos 3D que compõem o retângulo 3D. Chame-os (x0, y0, z0), (x1, y1, z1), (x2, y2, z2) e (x3, y3, z3). Ao projetar esses pontos no plano x-y, você solta as coordenadas z: (x0, y0), (x1, y1), (x2, y2), (x3, y3).

Agora, você deseja projetar novamente no espaço 3D, é necessário fazer engenharia reversa do que eram z0, .., z3. Mas qualquer conjunto de coordenadas z que a) mantenha a mesma distância x-y entre os pontos eb) mantenha a forma em que um retângulo funcionará. Portanto, qualquer membro desse conjunto (infinito) fará: {(z0 + i, z1 + i, z2 + i, z3 + i) | i <- R}.

Edit @Jarrett: Imagine que você resolveu isso e acabou com um retângulo no espaço 3D. Agora, imagine deslizar esse retângulo para cima e para baixo no eixo z. Essa quantidade infinita de retângulos traduzidos tem a mesma projeção x-y. Como você sabe que encontrou o "certo"?

Edit # 2: Tudo bem, isso é de um comentário que fiz sobre essa questão - uma abordagem mais intuitiva para argumentar sobre isso.

Imagine segurando um pedaço de papel sobre sua mesa. Finja que cada canto do papel possui um ponteiro laser sem peso, que aponta para a mesa. O papel é o objeto 3D e os pontos do ponteiro laser na mesa são a projeção 2D.

Agora, como você pode dizer a que altura da mesa está o papel olhando para apenas os pontos do ponteiro laser?

Você não pode. Mova o papel para cima e para baixo. Os ponteiros laser ainda brilham nos mesmos pontos da mesa, independentemente da altura do papel.

Encontrar as coordenadas z na projeção reversa é como tentar encontrar a altura do papel com base nos pontos do ponteiro laser somente na mesa.

1
Rob Dickerson
1
Ray Tayek

Se você souber que a forma é um retângulo em um plano, poderá restringir ainda mais o problema. Você certamente não pode descobrir "qual" plano; portanto, pode escolher que ele esteja no plano em que z = 0 e um dos cantos está em x = y = 0, e as arestas são paralelas ao eixo x/y.

Os pontos em 3d são, portanto, {0,0,0}, {w, 0,0}, {w, h, 0} e {0, h, 0}. Tenho certeza de que o tamanho absoluto não será encontrado, portanto, apenas a proporção w/h é relivante, portanto este é um desconhecido.

Em relação a esse plano, a câmera deve estar em algum ponto cx, cy, cz no espaço, deve estar apontando em uma direção nx, ny, nz (um vetor de comprimento um, para que um deles seja redundante) e ter um comprimento focal/largura_de_imagem fator de w. Esses números se transformam em uma matriz de projeção 3x3.

Isso fornece um total de 7 incógnitas: w/h, cx, cy, cz, nx, ny e w.

Você tem um total de 8 conhecidos: os 4 pares x + y.

Então, isso pode ser resolvido.

O próximo passo é usar o Matlab ou o Mathmatica.

1
spitzak

Sim, Monte Carlo funciona, mas encontrei uma solução melhor para esse problema. Este código funciona perfeitamente (e usa o OpenCV):

Cv2.CalibrateCamera(new List<List<Point3f>>() { points3d }, new List<List<Point2f>>() { points2d }, new Size(height, width), cameraMatrix, distCoefs, out rvecs, out tvecs, CalibrationFlags.ZeroTangentDist | CalibrationFlags.FixK1 | CalibrationFlags.FixK2 | CalibrationFlags.FixK3);

Esta função leva pontos 3d e 2d conhecidos, tamanho da tela e retorna rotação (rvecs [0]), tradução (tvecs [0]) e matriz de valores intrínsecos da câmera. É tudo que você precisa.

1
Inflight

Ao projetar de 3D para 2D, você perde informações.

No caso simples de um único ponto, a projeção inversa forneceria um raio infinito no espaço 3D.

A reconstrução estereoscópica normalmente começa com duas imagens 2D e projeta ambas de volta ao 3D. Em seguida, procure uma interseção dos dois raios 3D produzidos.

A projeção pode assumir diferentes formas. Ortogonal ou em perspectiva. Eu estou supondo que você está assumindo projeção ortogonal?

No seu caso, supondo que você tivesse a matriz original, você teria 4 raios no espaço 3D. Você seria capaz de restringir o problema pelas dimensões do retângulo 3d e tentar resolver.

A solução não será única, pois uma rotação em torno de qualquer eixo paralelo ao plano de projeção 2D será ambígua na direção. Em outras palavras, se a imagem 2d for perpendicular ao eixo z, girar o retângulo 3d no sentido horário ou anti-horário ao redor do eixo x produziria a mesma imagem. Da mesma forma para o eixo y.

No caso em que o plano do retângulo é paralelo ao eixo z, você tem ainda mais soluções.

Como você não possui a matriz de projeção original, mais ambiguidade é introduzida por um fator de escala arbitrário que existe em qualquer projeção. Você não pode distinguir entre uma escala na projeção e uma translação em 3d na direção do eixo z. Isso não é um problema se você estiver interessado apenas nas posições relativas dos 4 pontos no espaço 3D quando relacionadas entre si e não no plano da projeção 2D.

Numa projeção em perspectiva, as coisas ficam mais difíceis ...

1
morechilli

Obrigado a @Vegard por uma excelente resposta. Limpei o código um pouco:

import pandas as pd
import numpy as np

class Point2:
    def __init__(self,x,y):
        self.x = x
        self.y = y

class Point3:
    def __init__(self,x,y,z):
        self.x = x
        self.y = y
        self.z = z

# Known 2D coordinates of our rectangle
i0 = Point2(318, 247)
i1 = Point2(326, 312)
i2 = Point2(418, 241)
i3 = Point2(452, 303)

# 3D coordinates corresponding to i0, i1, i2, i3
r0 = Point3(0, 0, 0)
r1 = Point3(0, 0, 1)
r2 = Point3(1, 0, 0)
r3 = Point3(1, 0, 1)

mat = [
    [1, 0, 0, 0],
    [0, 1, 0, 0],
    [0, 0, 1, 0],
    [0, 0, 0, 1],
]

def project(p, mat):
    #print mat
    x = mat[0][0] * p.x + mat[0][1] * p.y + mat[0][2] * p.z + mat[0][3] * 1
    y = mat[1][0] * p.x + mat[1][1] * p.y + mat[1][2] * p.z + mat[1][3] * 1
    w = mat[3][0] * p.x + mat[3][1] * p.y + mat[3][2] * p.z + mat[3][3] * 1
    return Point2(720 * (x / w + 1) / 2., 576 - 576 * (y / w + 1) / 2.)

# The squared distance between two points a and b
def norm2(a, b):
    dx = b.x - a.x
    dy = b.y - a.y
    return dx * dx + dy * dy

def evaluate(mat): 
    c0 = project(r0, mat)
    c1 = project(r1, mat)
    c2 = project(r2, mat)
    c3 = project(r3, mat)
    return norm2(i0, c0) + norm2(i1, c1) + norm2(i2, c2) + norm2(i3, c3)    

def perturb(mat, amount):
    from copy import deepcopy
    from random import randrange, uniform
    mat2 = deepcopy(mat)
    mat2[randrange(4)][randrange(4)] += uniform(-amount, amount)
    return mat2

def approximate(mat, amount, n=1000):
    est = evaluate(mat)
    for i in xrange(n):
        mat2 = perturb(mat, amount)
        est2 = evaluate(mat2)
        if est2 < est:
            mat = mat2
            est = est2

    return mat, est

for i in xrange(1000):
    mat,est = approximate(mat, 1)
    print mat
    print est

A ligação aproximada com 0,1 não funcionou para mim, então eu atendi. Também corri por um tempo, e a última vez que verifiquei foi em

[[0.7576315397559887, 0, 0.11439449272592839, -0.314856490473439], 
[0.06440497208710227, 1, -0.5607502645413118, 0.38338196981556827], 
[0, 0, 1, 0], 
[0.05421620936883742, 0, -0.5673977598434641, 2.693116299312736]]

com um erro em torno de 0,02.

1
BBDynSys