ios camera scenekit projection augmented-reality arkit

iOS revierte la proyección de la cámara



camera scenekit (2)

Matemáticas (Trig.):

Notas: la parte inferior es l (la longitud del código QR), el ángulo izquierdo es k , y el ángulo superior es i (la cámara)

Estoy tratando de estimar la posición de mi dispositivo relacionada con un código QR en el espacio. Estoy usando ARKit y el marco Vision, ambos introducidos en iOS11, pero la respuesta a esta pregunta probablemente no dependa de ellos.

Con el marco Vision, puedo obtener el rectángulo que limita un código QR en el marco de la cámara. Me gustaría hacer coincidir este rectángulo con la traducción y rotación del dispositivo necesarias para transformar el código QR desde una posición estándar.

Por ejemplo, si observo el marco:

* * B C A D * *

mientras que si estuviera a 1 m del código QR, centrado en él, y suponiendo que el código QR tenga un lado de 10 cm, vería:

* * A0 B0 D0 C0 * *

¿Cuál ha sido la transformación de mi dispositivo entre esos dos cuadros? Entiendo que un resultado exacto podría no ser posible, porque quizás el código QR observado es ligeramente no plano y estamos tratando de estimar una transformación afín en algo que no es uno perfectamente.

Supongo que sceneView.pointOfView?.camera?.projectionTransform es más útil que sceneView.pointOfView?.camera?.projectionTransform?.camera.projectionMatrix ya que la última ya tiene en cuenta la transformación inferida del ARKit que no me interesa por este problema

¿Cómo llenaría

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 ====

Después de probar varias cosas, terminé yendo a la estimación de la postura de la cámara usando la resolución OpenCV y el solucionador de perspectiva, solvePnP Esto me da una rotación y traducción que debería representar la pose de la cámara en el código QR referencial. Sin embargo, al usar esos valores y colocar objetos correspondientes a la transformación inversa, donde el código QR debe estar en el espacio de la cámara, obtengo valores desplazados inexactos y no puedo hacer que la rotación funcione:

// 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 }

Aquí está la salida:

donde A, B, C, D son las esquinas del código QR en el orden en que se pasan al programa.

El origen predicho permanece en su lugar cuando el teléfono gira, pero se desplaza desde donde debería estar. Sorprendentemente, si cambio los valores de las observaciones, puedo corregir esto:

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

y ahora el origen previsto se mantiene firmemente en su lugar. Sin embargo, no entiendo de dónde provienen los valores de cambio.

Finalmente, intenté obtener una orientación fija relativamente al código QR referencial:

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)

La orientación está bien cuando miro el código QR directamente, pero luego cambia por algo que parece estar relacionado con la rotación del teléfono:

Las preguntas pendientes que tengo son:

  • ¿Cómo resuelvo la rotación?
  • ¿De dónde vienen los valores de cambio de posición?
  • ¿Qué relación simple verifican la rotación, la traducción, QRCornerCoordinatesInQRRef, las observaciones, las intrínsecas? ¿Es O ~ K ^ -1 * (R_3x2 | T) Q? Porque si es así, eso está fuera de lugar en un orden de magnitud.

Si eso es útil, aquí hay algunos 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

==== Editar2 ====

Me di cuenta de que la rotación funciona bien cuando el teléfono permanece horizontalmente paralelo al código QR (es decir, la matriz de rotación es [[a, 0, b], [0, 1, 0], [c, 0, d]] ), independientemente de la orientación real del código QR:

Otra rotación no funciona.


Supongo que el problema no está en la matriz. Está en la colocación de vértices. Para el seguimiento de imágenes 2D, debe colocar vértices ABCD en sentido antihorario (el punto de partida es un vértice A ubicado en origen imaginario x:0, y:0 ). Creo que la documentación de Apple sobre la clase VNRectangleObservation (información sobre regiones rectangulares proyectadas detectadas por una solicitud de análisis de imagen) es vaga. Puso sus vértices en el mismo orden que en la documentación oficial:

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

Pero deben colocarse de la misma manera que ocurre en la dirección de rotación positiva (sobre el eje Z ) en el sistema de coordenadas cartesianas:

World Coordinate Space en ARKit (así como en SceneKit y Vision) siempre sigue una right-handed convention (el eje Y positivo apunta hacia arriba, el eje Z positivo apunta hacia el espectador y el eje X positivo apunta hacia la derecha del espectador), pero está orientado en función de la configuración de su sesión. La cámara funciona en el espacio de coordenadas locales.

La dirección de rotación sobre cualquier eje es positiva (en sentido antihorario) y negativa (en sentido horario). Para el seguimiento en ARKit y Vision es de vital importancia.

El orden de rotación también tiene sentido. ARKit, así como SceneKit, aplica la rotación relativa a la propiedad de pivote del nodo en el orden inverso de los componentes: primero roll (sobre el eje Z ), luego yaw (sobre el eje Y ), luego pitch (sobre el eje X ). Entonces el orden de rotación es ZYX .

Además, hay publicaciones útiles sobre Matrix Operations en Nukepedia.