iOS reverter projeção de câmera

87

Estou tentando estimar a posição do meu dispositivo em relação a um código QR no espaço. Estou usando o ARKit e o framework Vision, ambos introduzidos no iOS11, mas a resposta a essa pergunta provavelmente não depende deles.

Com o framework Vision, consigo obter o retângulo que delimita um código QR no frame da câmera. Eu gostaria de combinar este retângulo com a translação e rotação do dispositivo necessária para transformar o código QR de uma posição padrão.

Por exemplo, se eu observar o quadro:

*            *

    B
          C
  A
       D


*            *

enquanto se eu estivesse a 1m de distância do código QR, centralizado nele, e assumindo que o código QR tem um lado de 10 cm eu veria:

*            *


    A0  B0

    D0  C0


*            *

qual foi a transformação do meu dispositivo entre esses dois quadros? Eu entendo que um resultado exato pode não ser possível, porque talvez o código QR observado seja ligeiramente não plano e estejamos tentando estimar uma transformação afim em algo que não é perfeito.

Acho que o sceneView.pointOfView?.camera?.projectionTransformé mais útil do que o, sceneView.pointOfView?.camera?.projectionTransform?.camera.projectionMatrixvisto que o último já leva em consideração a transformação inferida do ARKit na qual não estou interessado para esse problema.

Como eu encheria

func get transform(
  qrCodeRectangle: VNBarcodeObservation,
  cameraTransform: SCNMatrix4) {
  // qrCodeRectangle.topLeft etc is the position in [0, 1] * [0, 1] of A0

  // expected real world position of the QR code in a referential coordinate system
  let a0 = SCNVector3(x: -0.05, y: 0.05, z: 1)
  let b0 = SCNVector3(x: 0.05, y: 0.05, z: 1)
  let c0 = SCNVector3(x: 0.05, y: -0.05, z: 1)
  let d0 = SCNVector3(x: -0.05, y: -0.05, z: 1)

  let A0, B0, C0, D0 = ?? // CGPoints representing position in
                          // camera frame for camera in 0, 0, 0 facing Z+

  // then get transform from 0, 0, 0 to current position/rotation that sees
  // a0, b0, c0, d0 through the camera as qrCodeRectangle 
}

==== Editar ====

Depois de tentar várias coisas, acabei optando por estimar a pose da câmera usando projeção openCV e solucionador de perspectiva. solvePnPIsso me dá uma rotação e translação que deve representar a pose da câmera no referencial do código QR. No entanto, ao usar esses valores e colocar objetos correspondentes à transformação inversa, onde o código QR deveria estar no espaço da câmera, recebo valores deslocados imprecisos e não consigo fazer a rotação funcionar:

// some flavor of pseudo code below
func renderer(_ sender: SCNSceneRenderer, updateAtTime time: TimeInterval) {
  guard let currentFrame = sceneView.session.currentFrame, let pov = sceneView.pointOfView else { return }
  let intrisics = currentFrame.camera.intrinsics
  let QRCornerCoordinatesInQRRef = [(-0.05, -0.05, 0), (0.05, -0.05, 0), (-0.05, 0.05, 0), (0.05, 0.05, 0)]

  // uses VNDetectBarcodesRequest to find a QR code and returns a bounding rectangle
  guard let qr = findQRCode(in: currentFrame) else { return }

  let imageSize = CGSize(
    width: CVPixelBufferGetWidth(currentFrame.capturedImage),
    height: CVPixelBufferGetHeight(currentFrame.capturedImage)
  )

  let observations = [
    qr.bottomLeft,
    qr.bottomRight,
    qr.topLeft,
    qr.topRight,
  ].map({ (imageSize.height * (1 - $0.y), imageSize.width * $0.x) })
  // image and SceneKit coordinated are not the same
  // replacing this by:
  // (imageSize.height * (1.35 - $0.y), imageSize.width * ($0.x - 0.2))
  // weirdly fixes an issue, see below

  let rotation, translation = openCV.solvePnP(QRCornerCoordinatesInQRRef, observations, intrisics)
  // calls openCV solvePnP and get the results

  let positionInCameraRef = -rotation.inverted * translation
  let node = SCNNode(geometry: someGeometry)
  pov.addChildNode(node)
  node.position = translation
  node.orientation = rotation.asQuaternion
}

Aqui está o resultado:

insira a descrição da imagem aqui

onde A, B, C, D são os cantos do código QR na ordem em que são passados ​​para o programa.

A origem prevista permanece no lugar quando o telefone gira, mas é deslocada de onde deveria estar. Surpreendentemente, se eu mudar os valores das observações, posso corrigir isso:

  // (imageSize.height * (1 - $0.y), imageSize.width * $0.x)
  // replaced by:
  (imageSize.height * (1.35 - $0.y), imageSize.width * ($0.x - 0.2))

insira a descrição da imagem aqui

e agora a origem prevista permanece robusta no lugar. No entanto, não entendo de onde vêm os valores de mudança.

Finalmente, tentei obter uma orientação fixa em relação ao referencial do código QR:

    var n = SCNNode(geometry: redGeometry)
    node.addChildNode(n)
    n.position = SCNVector3(0.1, 0, 0)
    n = SCNNode(geometry: blueGeometry)
    node.addChildNode(n)
    n.position = SCNVector3(0, 0.1, 0)
    n = SCNNode(geometry: greenGeometry)
    node.addChildNode(n)
    n.position = SCNVector3(0, 0, 0.1)

A orientação é boa quando eu olho para o código QR diretamente, mas então ele muda para algo que parece estar relacionado à rotação do telefone:insira a descrição da imagem aqui

As dúvidas pendentes que tenho são:

  • Como faço para resolver a rotação?
  • de onde vêm os valores de mudança de posição?
  • Que relação simples a rotação, translação, QRCornerCoordinatesInQRRef, observações e intrísicos verificam? É O ~ K ^ -1 * (R_3x2 | T) Q? Porque se for assim, isso está errado por algumas ordens de magnitude.

Se isso for útil, aqui estão alguns valores numéricos:

Intrisics matrix
Mat 3x3
1090.318, 0.000, 618.661
0.000, 1090.318, 359.616
0.000, 0.000, 1.000

imageSize
1280.0, 720.0
screenSize
414.0, 736.0

==== Edit2 ====

Notei que a rotação funciona bem quando o telefone permanece horizontalmente paralelo ao código QR (ou seja, a matriz de rotação é [[a, 0, b], [0, 1, 0], [c, 0, d]] ), não importa qual seja a orientação real do código QR:

insira a descrição da imagem aqui

Outra rotação não funciona.

Guig
fonte
Ei, você está tentando diminuir a distância dos dispositivos por meio do código QR? Se sim, veja minha resposta abaixo.
Ephellon Dantzler
EDIT: para suas perguntas pendentes, 1. Parece que simplesmente um valor desnecessário foi inserido. Possivelmente no método de mapeamento chamado, ou qualquer outra coisa que lide com os círculos sendo desenhados (como drawCircle(... rotation)) 2. Não tive tempo de ler as especificações 3. O mesmo que 2
Ephellon Dantzler
Você será capaz de compartilhar algum código?
Michal Zaborowski

Respostas:

1

Matemática (Trig.):

Equação

Observações: a parte inferior é l(o comprimento do código QR), o ângulo esquerdo é ke o ângulo superior é i(a câmera)

Cenário

Ephellon Dantzler
fonte
com certeza, mas eu só sei o ângulo observado ie a distância originall
Guig
tudo bem, existe uma maneira de encontrar o oposto de i? Se não for um ângulo correto para, lentão há mais matemática envolvida para encontrar kou theta; i + k + theta = 180.
Ephellon Dantzler de
1
Para fazer a trigonometria funcionar, preciso de duas distâncias e um ângulo ou de dois ângulos e uma distância. Não há como obter tudo de apenas um ângulo e uma distância
Guig
Ajuda o fato de o código QR ser quadrado, de modo que você possa observar dois ângulos, tanto verticais quanto horizontais?
Bob Wakefield
1

Suponho que o problema não esteja na matriz. Está na colocação dos vértices. Para rastrear imagens 2D, você precisa colocar os vértices ABCD no sentido anti-horário (o ponto de partida é um vértice localizado na origem imaginária x:0, y:0 ). Acho que a documentação da Apple sobre a classe VNRectangleObservation (informações sobre regiões retangulares projetadas detectadas por uma solicitação de análise de imagem) é vaga. Você colocou seus vértices na mesma ordem da documentação oficial:

var bottomLeft: CGPoint
var bottomRight: CGPoint
var topLeft: CGPoint
var topRight: CGPoint

Mas eles precisam ser colocados da mesma forma que a direção de rotação positiva (em torno do Zeixo) ocorre no sistema de coordenadas cartesianas:

insira a descrição da imagem aqui

O World Coordinate Space no ARKit (bem como no SceneKit e Vision) sempre segue um right-handed convention(o Yeixo positivo aponta para cima, o Zeixo positivo aponta para o visualizador e o Xeixo positivo aponta para a direita do visualizador), mas é orientado com base na configuração da sua sessão . A câmera funciona no Espaço Coordenado Local.

A direção de rotação sobre qualquer eixo é positiva (sentido anti-horário) e negativa (sentido horário). Para rastrear no ARKit e Vision é extremamente importante.

insira a descrição da imagem aqui

A ordem de rotação também faz sentido. O ARKit, assim como o SceneKit, aplica a rotação relativa à propriedade pivot do nó na ordem inversa dos componentes: primeiro roll(sobre o Zeixo), depois yaw(sobre o Yeixo) e depois pitch(sobre o Xeixo). Portanto, a ordem de rotação é ZYX.

Além disso, há um post útil sobre Operações de matriz no Nukepedia.

Andy Fedoroff
fonte