87

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

使用 Vision 框架,我能够获得在相机框架中限定二维码的矩形。我想将此矩形与从标准位置转换 QR 码所需的设备平移和旋转相匹配。

例如,如果我观察框架:

*            *

    B
          C
  A
       D


*            *

而如果我距离 QR 码 1m,以它为中心,并假设 QR 码的边长为 10cm,我会看到:

*            *


    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 码应该在相机空间中,我得到不准确的移位值,并且我无法让旋转工作:

// 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)

当我直视 QR 码时,方向很好,但随后它发生了一些似乎与手机旋转有关的变化:在此处输入图像描述

我的悬而未决的问题是:

  • 如何解决旋转问题?
  • 位置偏移值来自哪里?
  • 旋转、平移、QRCornerCoordinatesInQRRef、观察、内在函数验证了哪些简单的关系?是 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 ====

我注意到当手机与二维码保持水平平行时,旋转效果很好(即旋转矩阵为 [[a, 0, b], [0, 1, 0], [c, 0, d]] ),无论实际二维码方向是什么:

在此处输入图像描述

其他旋转不起作用。

4

2 回答 2

2

坐标系对应

考虑到Vision/CoreML坐标系不对应ARKit/SceneKit坐标系。有关详细信息,请查看此帖子

旋转方向

我想问题不在矩阵中。它位于顶点位置。为了跟踪 2D 图像,您需要逆时针放置 ABCD 顶点(起点是位于虚原点 x:0, y:0的 A 顶点)。我认为关于VNRectangleObservation类的 Apple 文档(有关由图像分析请求检测到的投影矩形区域的信息)是模糊的。您按照与官方文档中相同的顺序放置顶点:

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

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

在此处输入图像描述

ARKit(以及 SceneKit 和 Vision)中的世界坐标空间始终遵循 a right-handed convention(正Y轴指向上方,正Z轴指向查看者,正X轴指向查看者的右侧),但基于会话的配置定向. 相机在局部坐标空间中工作。

绕任何轴的旋转方向为正(逆时针)和负(顺时针)。对于 ARKit 和 Vision 中的跟踪而言,这一点至关重要。

在此处输入图像描述

旋转的顺序也很有意义。ARKit 和 SceneKit 以组件的相反顺序相对于节点的 pivot 属性应用旋转:首先roll(绕Z轴),然后yaw(绕Y轴),然后pitch(绕X轴)。所以轮换顺序是ZYX

于 2018-06-07T11:58:18.333 回答
1

数学(三角):

方程

注:底部为l(二维码长度),左角为k,上角为i(摄像头)

图片

于 2017-08-13T01:17:13.293 回答