iOS相机投影反转

87

我正在尝试估算我的设备在空间中与QR码的位置关系。我正在使用iOS11中引入的ARKit和Vision框架,但是这个问题的答案可能不取决于它们。

使用Vision框架,我能够获取相机帧中包围QR码的矩形。我想将此矩形与设备平移和旋转匹配,以将QR码从标准位置转换。

例如,如果我观察到该帧:

*            *

    B
          C
  A
       D


*            *

如果我距离QR码1米,位于其中心,并假设QR码的一边为10厘米,那么我会看到:

*            *


    A0  B0

    D0  C0


*            *

在这两个帧之间,我的设备转换是什么?我知道可能无法得到精确的结果,因为观察到的QR码可能略微非平面,我们正在尝试估计一个不完美的仿射变换。

我猜sceneView.pointOfView?.camera?.projectionTransformsceneView.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
}

这是输出结果:

enter image description here

其中,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))

enter image description here

现在预测的起点稳定不动了。但是我不明白偏移值从哪里来。

最后,我尝试相对于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)

当我直视二维码时,方向很好,但是它随后会因为手机的旋转而发生偏移: enter image description here

我有一些未解决的问题:

  • 如何解决旋转问题?
  • 位置偏移值从哪里来?
  • 旋转、平移、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码的实际方向如何,旋转都可以正常工作:

enter image description here

其他旋转不起作用。


嘿,你是想通过QR码获取设备距离吗?如果是的话,请看下面我的回答。 - Ephellon Grey
  1. 看起来只是插入了一个不必要的值。可能是在调用映射方法时,或者与绘制圆形有关的任何其他事情(例如 drawCircle(... rotation))。
  2. 没有时间阅读规格说明。
  3. 与第二条相同。
- Ephellon Grey
你能分享一些代码吗? - Michal Zaborowski
2个回答

2

坐标系统的对应关系

请注意,Vision/CoreML的坐标系统与ARKit/SceneKit的坐标系统不相对应。详细信息请参考此帖子

旋转方向

我认为问题并不源于矩阵,而是顶点的放置导致了这个问题。要跟踪2D图像,您需要按逆时针方向放置ABCD顶点(起始点是位于QR码左下角的虚拟原点 x:0, y:0的A顶点)。苹果关于VNRectangleObservation类的文档(关于图像分析请求检测到的投影矩形区域的信息)比较模糊。您将顶点按照官方文档中所述的顺序放置:

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

但是它们需要以与笛卡尔坐标系中正旋转(关于Z轴)相同的方式放置:

enter image description here

ARKit中的世界坐标空间遵循“右手定则” - 正Y轴向上,正Z轴指向观察者,正X轴指向观察者的右侧,但其方向取决于会话的配置。
绕任意轴的旋转为正(逆时针)和负(顺时针)。对于ARKit和Vision的跟踪来说,这非常重要。

enter image description here

旋转的顺序也是有道理的。ARKit根据节点的枢轴点相对于组件的反向顺序应用旋转:首先是roll(绕Z轴),然后是yaw(绕Y轴),最后是pitch(绕X轴)。因此,旋转顺序是ZYX。

1

数学(三角函数):

Equation

注意:底部是l(指二维码长度),左侧角度为k,顶部角度为i(指摄像头)。

Picture


当然可以,但是我只知道观察角度 i 和原始距离 l - Guig
没问题,有没有办法找到i的相反数?如果它不是直角对于l来说,那么就需要更多的数学运算来找到kthetai + k + theta = 180 - Ephellon Grey
1
为了让三角函数正常工作,我需要两个距离和一个角度,或者两个角度和一个距离。仅凭一个角度和一个距离是无法得到所有信息的。 - Guig
QR码是正方形的,这样你就可以观察到两个角度,垂直和水平,这有帮助吗? - Bob Wakefield

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接