-6

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

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

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

因此,我决定使用run()pause()方法来降低会话的帧速率。

问题

我想知道如何在指定的时间段内自动run和一个ARSession?和方法pause的持续时间必须是(或 0.016 秒)。我想可能通过. 但我不知道如何实现它。runpause16 msDispatchQueue

怎么做?

在此处输入图像描述

这是一个伪代码:

session.run(configuration)

    /*  run lasts 16 ms  */

session.pause()

    /*  pause lasts 16 ms  */

session.run(session.configuration!)

    /*  etc...  */

PS我的应用程序中既不能使用 CocoaPod 也不能使用 Carthage

更新:关于如何currentFrame.capturedImage检索和使用 ARSession。

let session = ARSession()

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    session.delegate = self
    let configuration = ARImageTrackingConfiguration() // 6DOF
    configuration.providesAudioData = false
    configuration.isAutoFocusEnabled = true            
    configuration.isLightEstimationEnabled = false
    configuration.maximumNumberOfTrackedImages = 0
    session.run(configuration)  

    spawnCoreMLUpdate()
}

func spawnCoreMLUpdate() {    // Spawning new async tasks

    dispatchQueue.async {
        self.spawnCoreMLUpdate()
        self.updateCoreML()
    }
}

func updateCoreML() {

    let pixelBuffer: CVPixelBuffer? = (session.currentFrame?.capturedImage)
    if pixelBuffer == nil { return }
    let ciImage = CIImage(cvPixelBuffer: pixelBuffer!)
    let imageRequestHandler = VNImageRequestHandler(ciImage: ciImage, options: [:])
    do {
        try imageRequestHandler.perform(self.visionRequests)
    } catch {
        print(error)
    }
}
4

3 回答 3

5

如果您想要将帧速率从 60 降低到 30 ,则应使用preferredFramesPerSecond. SCNView我假设您使用的是ARSCNView,它是 的子类SCNView

财产文件。

于 2018-12-08T19:35:36.520 回答
4

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

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

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

当然,委托很可能每 16 毫秒被调用一次,因为这就是相机的工作方式。但是您可以决定要处理哪些帧。通过使用帧的时间戳,您可以每 32 毫秒处理一个帧并丢弃其他帧。这相当于 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)
  }
}
于 2018-12-18T19:36:38.613 回答
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 是否会阻止您的进程,如果是,我将编辑我的答案。

于 2018-12-18T15:33:16.313 回答