Próbuję oszacować położenie mojego urządzenia związane z kodem QR w przestrzeni. Używam ARKit i frameworka Vision, oba wprowadzone w iOS11, ale odpowiedź na to pytanie prawdopodobnie nie zależy od nich.
Dzięki frameworkowi Vision jestem w stanie uzyskać prostokąt, który ogranicza kod QR w ramce aparatu. Chciałbym dopasować ten prostokąt do translacji i obrotu urządzenia niezbędnego do przekształcenia kodu QR ze standardowej pozycji.
Na przykład, jeśli obserwuję ramkę:
* *
B
C
A
D
* *
podczas gdy gdybym był 1 m od kodu QR, wyśrodkowany na nim i zakładając, że kod QR ma bok 10 cm, zobaczyłbym:
* *
A0 B0
D0 C0
* *
jaka była transformacja mojego urządzenia między tymi dwiema ramkami? Rozumiem, że dokładny wynik może nie być możliwy, ponieważ może obserwowany kod QR jest nieco nieplanarny i próbujemy oszacować transformację afiniczną na czymś, co nie jest idealnie.
Myślę, że sceneView.pointOfView?.camera?.projectionTransform
jest bardziej pomocny niż ten, sceneView.pointOfView?.camera?.projectionTransform?.camera.projectionMatrix
ponieważ później uwzględnia już transformację wywnioskowaną z ARKita, która mnie nie interesuje w przypadku tego problemu.
Jak bym wypełnił
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
}
==== Edytuj ====
Po wypróbowaniu wielu rzeczy, w końcu zdecydowałem się na oszacowanie pozycji kamery za pomocą projekcji openCV i solvera perspektywicznego. solvePnP
To daje mi obrót i tłumaczenie, które powinny odzwierciedlać pozę kamery w referencji kodu QR. Jednak podczas korzystania z tych wartości i umieszczania obiektów odpowiadających transformacji odwrotnej, w których kod QR powinien znajdować się w przestrzeni kamery, otrzymuję niedokładne przesunięte wartości i nie jestem w stanie uzyskać obrotu do pracy:
// 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
}
Oto wynik:
gdzie A, B, C, D to rogi kodu QR w kolejności, w jakiej są przekazywane do programu.
Przewidywane źródło pozostaje na miejscu, gdy telefon się obraca, ale jest przesunięte z miejsca, w którym powinno. O dziwo, jeśli zmienię wartości obserwacji, jestem w stanie to poprawić:
// (imageSize.height * (1 - $0.y), imageSize.width * $0.x)
// replaced by:
(imageSize.height * (1.35 - $0.y), imageSize.width * ($0.x - 0.2))
a teraz przewidywane pochodzenie pozostaje na swoim miejscu. Jednak nie rozumiem, skąd pochodzą wartości przesunięcia.
Wreszcie próbowałem ustalić orientację względem referencji kodu QR:
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)
Orientacja jest w porządku, gdy patrzę prosto na kod QR, ale potem przesuwa się o coś, co wydaje się być związane z obrotem telefonu:
Wybitne pytania, które mam, to:
- Jak rozwiązać rotację?
- skąd się biorą wartości przesunięcia pozycji?
- Jaką prostą zależność weryfikuje rotacja, tłumaczenie, QRCornerCoordinatesInQRRef, obserwacje, intrisics? Czy to O ~ K ^ -1 * (R_3x2 | T) Q? Bo jeśli tak, to jest to o kilka rzędów wielkości.
Jeśli to pomoże, oto kilka wartości liczbowych:
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 ====
Zauważyłem, że obrót działa dobrze, gdy telefon pozostaje poziomo równolegle do kodu QR (tj. Macierz obrotu to [[a, 0, b], [0, 1, 0], [c, 0, d]] ), bez względu na rzeczywistą orientację kodu QR:
Inna rotacja nie działa.