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 2