在指定的时间段内运行和暂停ARSession

时间:2018-12-08 17:29:09

标签: ios swift augmented-reality arkit dispatch-queue

我正在开发具有手势识别功能的ARKit / Vision iOS应用。我的应用程序具有一个简单的UI,其中包含单个UIView。根本没有ARSCNView / ARSKView。我将捕获的ARFrames序列放入CVPixelBuffer中,然后将其用于VNRecognizedObjectObservation

我不需要会话中的任何跟踪数据。我只需要currentFrame.capturedImage的{​​{1}}。我需要以30 fps捕获ARFrame。 60 fps是过高的帧速率。

  在我的情况下,

CVPixelBuffer 实例属性绝对没有用,因为它控制渲染preferredFramesPerSecond / ARSCNView的帧速率。我没有ARViews。而且它不会影响会话的帧速率。

因此,我决定使用ARSKViewrun()方法来降低会话的帧频。

问题

我想知道如何在指定的时间段内自动pause()run一个ARSession吗? pauserun方法的持续时间必须为 pause (或0.016秒)。我想也许可以通过16 ms。但是我不知道如何实现。

如何做到?

enter image description here

这是一个伪代码:

DispatchQueue

P.S。 我无法在我的应用中使用CocoaPod或Carthage

更新:关于如何获取和使用ARSession的session.run(configuration) /* run lasts 16 ms */ session.pause() /* pause lasts 16 ms */ session.run(session.configuration!) /* etc... */

currentFrame.capturedImage

3 个答案:

答案 0 :(得分:5)

如果要将帧速率从60降低到30,则应使用preferredFramesPerSecond的{​​{1}}属性。我假设您使用的是SCNView的子类ARSCNView

Property documentation.

答案 1 :(得分:3)

我不认为run()pause()策略是可行的,因为DispatchQueue API并非为实时准确性而设计。这意味着不能保证每次暂停都会为16ms。最重要的是,重新启动会话可能不是立即的,并且可能会增加更多的延迟。

此外,您共享的代码最多只能捕获一个图像,并且由于session.run(configuration)是异步的,因此可能不会捕获任何帧。

由于您没有使用ARSCNView/ARSKView,所以唯一的方法是实现ARSession委托,以便将每个捕获的帧通知给我们。

当然,最有可能每16毫秒调用一次代表,因为这就是摄像头的工作方式。但是您可以决定要处理的帧。通过使用帧的时间戳,您可以每32ms处理一个帧,然后丢弃其他帧。相当于30 fps的处理速度。

这里有一些代码可以帮助您入门,请确保dispatchQueue不是并发的,以便按顺序处理缓冲区:

var lastProcessedFrame: ARFrame?

func session(_ session: ARSession, didUpdate frame: ARFrame) {
  dispatchQueue.async {
    self.updateCoreML(with: frame)
  }
}

private func shouldProcessFrame(_ frame: ARFrame) -> Bool {
  guard let lastProcessedFrame = lastProcessedFrame else {
    // Always process the first frame
    return true
  }
  return frame.timestamp - lastProcessedFrame.timestamp >= 0.032 // 32ms for 30fps
}

func updateCoreML(with frame: ARFrame) {

  guard shouldProcessFrame(frame) else {
    // Less than 32ms with the previous frame
    return
  }
  lastProcessedFrame = frame
  let pixelBuffer = frame.capturedImage
  let imageRequestHandler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:])
  do {
    try imageRequestHandler.perform(self.visionRequests)
  } catch {
    print(error)
  }
}

答案 2 :(得分:1)

如果我正确理解它,则可以通过DispatchQueue实现它。如果您运行下面的代码,它将首先打印HHH,然后等待1秒钟,然后打印ABC。您可以放置​​自己的功能以使其适合您。当然,可以将时间间隔从1更改为所需的值。

let syncConc = DispatchQueue(label:"con",attributes:.concurrent)

DispatchQueue.global(qos: .utility).async {
syncConc.async {
    for _ in 0...10{
        print("HHH - \(Thread.current)")
        Thread.sleep(forTimeInterval: 1)
        print("ABC - \(Thread.current)")

    }
}

PS:我仍然不确定Thread.sleep是否会阻止您的进程,如果是,我将编辑答案。