iOS ripristina la proiezione della fotocamera


87

Sto cercando di stimare la posizione del mio dispositivo in relazione a un codice QR nello spazio. Sto usando ARKit e il framework Vision, entrambi introdotti in iOS11, ma la risposta a questa domanda probabilmente non dipende da loro.

Con il framework Vision, sono in grado di ottenere il rettangolo che delimita un codice QR nella cornice della fotocamera. Vorrei abbinare questo rettangolo alla traslazione e rotazione del dispositivo necessarie per trasformare il codice QR da una posizione standard.

Ad esempio, se osservo la cornice:

*            *

    B
          C
  A
       D


*            *

mentre se fossi a 1 metro di distanza dal codice QR, centrato su di esso e assumendo che il codice QR abbia un lato di 10 cm vedrei:

*            *


    A0  B0

    D0  C0


*            *

qual è stata la trasformazione del mio dispositivo tra questi due frame? Capisco che un risultato esatto potrebbe non essere possibile, perché forse il codice QR osservato è leggermente non planare e stiamo cercando di stimare una trasformazione affine su qualcosa che non è perfettamente uno.

Immagino che sceneView.pointOfView?.camera?.projectionTransformsia più utile di sceneView.pointOfView?.camera?.projectionTransform?.camera.projectionMatrixpoiché il secondo tiene già conto della trasformazione dedotta da ARKit a cui non sono interessato per questo problema.

Come mi riempirei

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 
}

==== Modifica ====

Dopo aver provato un certo numero di cose, ho finito per optare per la stima della posa della fotocamera utilizzando la proiezione openCV e il risolutore prospettico, solvePnPquesto mi dà una rotazione e una traduzione che dovrebbero rappresentare la posa della fotocamera nel codice QR referenziale. Tuttavia, quando si utilizzano questi valori e si posizionano oggetti corrispondenti alla trasformazione inversa, dove il codice QR dovrebbe essere nello spazio della fotocamera, ottengo valori spostati imprecisi e non sono in grado di far funzionare la rotazione:

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

Ecco l'output:

inserisci qui la descrizione dell'immagine

dove A, B, C, D sono gli angoli del codice QR nell'ordine in cui vengono passati al programma.

L'origine prevista rimane in posizione quando il telefono ruota, ma viene spostata da dove dovrebbe essere. Sorprendentemente, se sposto i valori delle osservazioni, sono in grado di correggere questo:

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

inserisci qui la descrizione dell'immagine

e ora l'origine prevista rimane saldamente al suo posto. Tuttavia non capisco da dove provengano i valori di spostamento.

Infine, ho cercato di ottenere un orientamento fisso relativamente al codice QR referenziale:

    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)

L'orientamento va bene quando guardo il codice QR direttamente, ma poi si sposta di qualcosa che sembra essere correlato alla rotazione del telefono:inserisci qui la descrizione dell'immagine

Le domande in sospeso che ho sono:

  • Come risolvo la rotazione?
  • da dove vengono i valori di spostamento di posizione?
  • Quale semplice relazione verificano rotazione, traduzione, QRCornerCoordinatesInQRRef, osservazioni, intrisics? È O ~ K ^ -1 * (R_3x2 | T) Q? Perché se è così è sbagliato di pochi ordini di grandezza.

Se è utile, ecco alcuni valori numerici:

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

==== Modifica2 ====

Ho notato che la rotazione funziona bene quando il telefono rimane orizzontalmente parallelo al codice QR (ovvero la matrice di rotazione è [[a, 0, b], [0, 1, 0], [c, 0, d]] ), indipendentemente dall'orientamento effettivo del codice QR:

inserisci qui la descrizione dell'immagine

Altre rotazioni non funzionano.


Ehi, stai cercando di ottenere la distanza dei dispositivi tramite il codice QR? In tal caso, vedere la mia risposta di seguito.
Ephellon Dantzler

EDIT: per le tue domande in sospeso, 1. Sembra che sia semplicemente inserito un valore non necessario. Forse nel metodo di mappatura chiamato, o qualsiasi altra cosa che abbia a che fare con i cerchi disegnati (comedrawCircle(... rotation) ) 2. Non ho avuto il tempo di leggere le specifiche 3. Come 2
Ephellon Dantzler

Sarai in grado di condividere del codice?
Michal Zaborowski

Risposte:


1

Matematica (Trig.):

Equazione

Note: il fondo è l(la lunghezza del codice QR), l'angolo sinistro èk e l'angolo superiore è i(la fotocamera)

Immagine


certo, ma conosco solo l'angolo osservato ie la distanza originalel
Guig

va bene, c'è un modo per trovare l'opposto di i? Se non è un angolo retto, è necessaria lpiù matematica per trovare ko theta; i + k + theta = 180.
Ephellon Dantzler

1
Per far funzionare la trigonometria ho bisogno di due distanze e un angolo, o due angoli e una distanza. Non c'è modo di ottenere tutto da un solo angolo e una distanza
Guig

Aiuta il fatto che il codice QR sia quadrato, in modo da poter osservare due angoli, sia verticale che orizzontale?
Bob Wakefield

1

Suppongo che il problema non sia in matrice. È nel posizionamento dei vertici. Per tracciare immagini 2D è necessario posizionare i vertici ABCD in senso antiorario (il punto di partenza è un vertice situato nell'origine immaginaria x:0, y:0 ). Penso che la documentazione di Apple sia attiva classe VNRectangleObservation (informazioni sulle regioni rettangolari proiettate rilevate da una richiesta di analisi dell'immagine) sia vaga. Hai posizionato i vertici nello stesso ordine della documentazione ufficiale:

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

Ma devono essere posizionati allo stesso modo della direzione di rotazione positiva (circa Z all'asse) si verifica nel sistema di coordinate cartesiane:

inserisci qui la descrizione dell'immagine

World Coordinate Space in ARKit (così come in SceneKit e Vision) segue sempre un file right-handed convention (l' Yasse positivo punta verso l'alto, l' Zasse positivo punta verso lo spettatore e l' Xasse positivo punta verso la destra dello spettatore), ma è orientato in base alla configurazione della tua sessione . La fotocamera funziona nello spazio delle coordinate locali.

La direzione di rotazione attorno a qualsiasi asse è positiva (in senso antiorario) e negativa (in senso orario). Per il monitoraggio in ARKit e Vision è di fondamentale importanza.

inserisci qui la descrizione dell'immagine

Anche l'ordine di rotazione ha senso. ARKit, così come SceneKit, applica la rotazione relativa alla proprietà pivot del nodo nell'ordine inverso dei componenti: prima roll(intorno Zall'asse), poi yaw(intorno Yall'asse), poipitch (attorno Xall'asse). Quindi l'ordine di rotazione è ZYX.

Inoltre, c'è un post utile sulle operazioni con le matrici su Nukepedia.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.