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?.projectionTransform
ist hilfreicher als das, sceneView.pointOfView?.camera?.projectionTransform?.camera.projectionMatrix
da 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. solvePnP
Dies 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:
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))
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:
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:
Andere Rotationen funktionieren nicht.
drawCircle(... rotation)
) 2. Hatte keine Zeit, die Spezifikationen zu lesen 3. Wie 2Antworten:
Mathe (Trig.):
Hinweise: Der untere Winkel ist (die
l
Länge des QR-Codes), der linke Winkel istk
und der obere Winkel isti
(die Kamera)quelle
i
und die ursprüngliche Entfernungl
i
? Gibt es eine Möglichkeit, das Gegenteil von zu finden ? Wenn es kein rechter Winkel ist,l
ist mehr Mathematik erforderlich, um entwederk
oder zu findentheta
.i + k + theta = 180
.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:Sie müssen jedoch genauso platziert werden wie die positive Drehrichtung (um die
Z
Achse) im kartesischen Koordinatensystem:Der Weltkoordinatenraum in ARKit (sowie in SceneKit und Vision) folgt immer a
right-handed convention
(die positiveY
Achse zeigt nach oben, die positiveZ
Achse zeigt zum Betrachter und die positiveX
Achse 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.
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 dieZ
Achse), dannyaw
(um dieY
Achse), dannpitch
(um dieX
Achse). Die Rotationsreihenfolge ist alsoZYX
.quelle