我正在尝试估算我的设备在空间中与QR码的位置关系。我正在使用iOS11中引入的ARKit和Vision框架,但是这个问题的答案可能不取决于它们。
使用Vision框架,我能够获取相机帧中包围QR码的矩形。我想将此矩形与设备平移和旋转匹配,以将QR码从标准位置转换。
例如,如果我观察到该帧:
* *
B
C
A
D
* *
如果我距离QR码1米,位于其中心,并假设QR码的一边为10厘米,那么我会看到:
* *
A0 B0
D0 C0
* *
在这两个帧之间,我的设备转换是什么?我知道可能无法得到精确的结果,因为观察到的QR码可能略微非平面,我们正在尝试估计一个不完美的仿射变换。
我猜sceneView.pointOfView?.camera?.projectionTransform
比sceneView.pointOfView?.camera?.projectionTransform?.camera.projectionMatrix
更有帮助,因为后者已经考虑了从ARKit推断出的变换,而我对此问题不感兴趣。
我该如何填写?
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
}
====编辑====
经过尝试多种方案,我最终选择使用openCV投影和透视解算器进行相机位姿估计,solvePnP
。这给我一个旋转和平移量,应该代表QR码参照系中的相机位姿。然而,当使用这些值并放置与反向变换相对应的物体,在相机空间中应该是QR码位置时,我得到了不准确的偏移值,并且我无法使旋转起作用:
// 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
}
这是输出结果:
其中,A、B、C、D 是按照传递给程序的顺序排列的 QR 码角落坐标。
当手机旋转时,预测的原点位置不变,但与其应该在的位置有所偏差。出人意料的是,如果我调整观察值,就能够纠正这个问题:
// (imageSize.height * (1 - $0.y), imageSize.width * $0.x)
// replaced by:
(imageSize.height * (1.35 - $0.y), imageSize.width * ($0.x - 0.2))
现在预测的起点稳定不动了。但是我不明白偏移值从哪里来。
最后,我尝试相对于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)
当我直视二维码时,方向很好,但是它随后会因为手机的旋转而发生偏移:
我有一些未解决的问题:
- 如何解决旋转问题?
- 位置偏移值从哪里来?
- 旋转、平移、QRRef中的QRCornerCoordinates、观测值和内部验证之间的简单关系是什么?是否为O ~ K^-1 * (R_3x2 | T) Q?如果是这样,那么误差可能会有几个数量级。
如果有帮助的话,这里有一些数值:
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
==== 编辑2 ====
我注意到当手机与QR码保持水平平行时(即旋转矩阵为[[a, 0, b], [0, 1, 0], [c, 0, d]]),无论QR码的实际方向如何,旋转都可以正常工作:
其他旋转不起作用。
drawCircle(... rotation)
)。