iOS-Kameraprojektion zurücksetzen

87

Ich versuche, meine Geräteposition in Bezug auf einen QR-Code im Weltraum zu schätzen. Ich verwende ARKit und das Vision-Framework, die beide in iOS11 eingeführt wurden, aber die Antwort auf diese Frage hängt wahrscheinlich nicht von ihnen ab.

Mit dem Vision-Framework kann ich das Rechteck erhalten, das einen QR-Code im Kamerarahmen begrenzt. Ich möchte dieses Rechteck an die Geräteübersetzung und -rotation anpassen, die zum Transformieren des QR-Codes von einer Standardposition erforderlich sind.

Zum Beispiel, wenn ich den Rahmen beobachte:

*            *

    B
          C
  A
       D


*            *

Wenn ich 1 m vom QR-Code entfernt wäre, zentriert darauf und davon ausgehen würde, dass der QR-Code eine Seite von 10 cm hat, würde ich sehen:

*            *


    A0  B0

    D0  C0


*            *

Was war meine Gerätetransformation zwischen diesen beiden Frames? Ich verstehe, dass ein genaues Ergebnis möglicherweise nicht möglich ist, da der beobachtete QR-Code möglicherweise nicht planar ist und wir versuchen, eine affine Transformation für etwas zu schätzen, das nicht perfekt ist.

Ich denke, das sceneView.pointOfView?.camera?.projectionTransformist hilfreicher als das, sceneView.pointOfView?.camera?.projectionTransform?.camera.projectionMatrixda das spätere bereits die aus dem ARKit abgeleitete Transformation berücksichtigt, an der ich für dieses Problem nicht interessiert bin.

Wie würde ich füllen

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 
}

==== Bearbeiten ====

Nachdem ich einige Dinge ausprobiert hatte, entschied ich mich für die Schätzung der Kameraposition mithilfe von openCV-Projektion und Perspektivlöser. solvePnPDies gibt mir eine Drehung und Übersetzung, die die Kameraposition im QR-Code-Referential darstellen sollte. Wenn ich jedoch diese Werte verwende und Objekte platziere, die der inversen Transformation entsprechen, wobei sich der QR-Code im Kameraraum befinden sollte, erhalte ich ungenaue verschobene Werte und kann die Drehung nicht zum Laufen bringen:

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

Hier ist die Ausgabe:

Geben Sie hier die Bildbeschreibung ein

Dabei sind A, B, C, D die QR-Code-Ecken in der Reihenfolge, in der sie an das Programm übergeben werden.

Der vorhergesagte Ursprung bleibt an Ort und Stelle, wenn sich das Telefon dreht, aber er wird von der Stelle verschoben, an der er sein sollte. Wenn ich die Beobachtungswerte verschiebe, kann ich dies überraschenderweise korrigieren:

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

Geben Sie hier die Bildbeschreibung ein

und jetzt bleibt der vorhergesagte Ursprung robust an Ort und Stelle. Ich verstehe jedoch nicht, woher die Verschiebungswerte kommen.

Schließlich habe ich versucht, eine Ausrichtung relativ zum QR-Code-Referential festzulegen:

    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)

Die Ausrichtung ist in Ordnung, wenn ich den QR-Code gerade betrachte, aber dann verschiebt er sich um etwas, das mit der Drehung des Telefons in Zusammenhang zu stehen scheint:Geben Sie hier die Bildbeschreibung ein

Hervorragende Fragen, die ich habe, sind:

  • Wie löse ich die Rotation?
  • Woher kommen die Positionsverschiebungswerte?
  • Welche einfache Beziehung überprüfen Rotation, Translation, QRCornerCoordinatesInQRRef, Beobachtungen und Intrisics? Ist es O ~ K ^ -1 * (R_3x2 | T) Q? Denn wenn ja, ist das um ein paar Größenordnungen niedriger.

Wenn dies hilfreich ist, sind hier einige numerische Werte:

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

Ich habe festgestellt, dass die Drehung gut funktioniert, wenn das Telefon horizontal parallel zum QR-Code bleibt (dh die Rotationsmatrix lautet [[a, 0, b], [0, 1, 0], [c, 0, d]]. ), unabhängig von der tatsächlichen Ausrichtung des QR-Codes:

Geben Sie hier die Bildbeschreibung ein

Andere Rotationen funktionieren nicht.

Guig
quelle
Hey, versuchen Sie, die Entfernung der Geräte über den QR-Code zu ermitteln? Wenn ja, siehe meine Antwort unten.
Ephellon Dantzler
BEARBEITEN: für Ihre offenen Fragen 1. Es klingt so, als ob einfach ein unnötiger Wert eingefügt wird. Möglicherweise in der aufgerufenen Mapping-Methode oder irgendetwas anderem, das sich mit den gezeichneten Kreisen befasst (z. B. drawCircle(... rotation)) 2. Hatte keine Zeit, die Spezifikationen zu lesen 3. Wie 2
Ephellon Dantzler
Können Sie Code freigeben?
Michal Zaborowski

Antworten:

1

Mathe (Trig.):

Gleichung

Hinweise: Der untere Winkel ist (die lLänge des QR-Codes), der linke Winkel ist kund der obere Winkel ist i(die Kamera)

Bild

Ephellon Dantzler
quelle
sicher, aber ich kenne nur den beobachteten Winkel iund die ursprüngliche Entfernungl
Guig
Ist das in Ordnung i? Gibt es eine Möglichkeit, das Gegenteil von zu finden ? Wenn es kein rechter Winkel ist, list mehr Mathematik erforderlich, um entweder koder zu finden theta. i + k + theta = 180.
Ephellon Dantzler
1
Damit die Trigonometrie funktioniert, benötige ich entweder zwei Abstände und einen Winkel oder zwei Winkel und einen Abstand. Es gibt keine Möglichkeit, alles aus nur einem Winkel und einer Entfernung zu erreichen
Guig
Hilft es, dass der QR-Code quadratisch ist, sodass Sie zwei Winkel beobachten können, sowohl vertikal als auch horizontal?
Bob Wakefield
1

Ich nehme an, das Problem liegt nicht in der Matrix. Es ist in Scheitelpunktplatzierung. Zum Verfolgen von 2D-Bildern müssen Sie ABCD-Scheitelpunkte gegen den Uhrzeigersinn platzieren (der Startpunkt ist ein Scheitelpunkt, der sich im imaginären Ursprung befindet x:0, y:0 ). Ich denke, die Apple-Dokumentation zur VNRectangleObservation- Klasse (Informationen zu projizierten rechteckigen Bereichen, die durch eine Bildanalyseanforderung erkannt wurden) ist vage. Sie haben Ihre Scheitelpunkte in derselben Reihenfolge wie in der offiziellen Dokumentation platziert:

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

Sie müssen jedoch genauso platziert werden wie die positive Drehrichtung (um die ZAchse) im kartesischen Koordinatensystem:

Geben Sie hier die Bildbeschreibung ein

Der Weltkoordinatenraum in ARKit (sowie in SceneKit und Vision) folgt immer a right-handed convention(die positive YAchse zeigt nach oben, die positive ZAchse zeigt zum Betrachter und die positive XAchse zeigt nach rechts des Betrachters), richtet sich jedoch nach der Konfiguration Ihrer Sitzung . Die Kamera arbeitet im lokalen Koordinatenraum.

Die Drehrichtung um eine Achse ist positiv (gegen den Uhrzeigersinn) und negativ (im Uhrzeigersinn). Für die Verfolgung in ARKit und Vision ist dies von entscheidender Bedeutung.

Geben Sie hier die Bildbeschreibung ein

Die Reihenfolge der Drehung ist ebenfalls sinnvoll. ARKit und SceneKit wenden die Drehung relativ zur Pivot-Eigenschaft des Knotens in umgekehrter Reihenfolge der Komponenten an: zuerst roll(um die ZAchse), dann yaw(um die YAchse), dann pitch(um die XAchse). Die Rotationsreihenfolge ist also ZYX.

Außerdem gibt es nützliche Beiträge zu Matrix-Operationen auf Nukepedia.

Andy Fedoroff
quelle