我正在开发具有手势识别功能的ARKit
/ Vision
iOS应用。我的应用程序具有一个简单的UI,其中包含单个UIView
。根本没有ARSCNView
/ ARSKView
。我将捕获的ARFrames
序列放入CVPixelBuffer
中,然后将其用于VNRecognizedObjectObservation
。
我不需要会话中的任何跟踪数据。我只需要currentFrame.capturedImage
的{{1}}。我需要以30 fps捕获ARFrame。 60 fps是过高的帧速率。
在我的情况下,
CVPixelBuffer
实例属性绝对没有用,因为它控制渲染preferredFramesPerSecond
/ARSCNView
的帧速率。我没有ARViews。而且它不会影响会话的帧速率。
因此,我决定使用ARSKView
和run()
方法来降低会话的帧频。
问题
我想知道如何在指定的时间段内自动pause()
和run
一个ARSession吗? pause
和run
方法的持续时间必须为 pause
(或0.016秒)。我想也许可以通过16 ms
。但是我不知道如何实现。
如何做到?
这是一个伪代码:
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
答案 0 :(得分:5)
如果要将帧速率从60降低到30,则应使用preferredFramesPerSecond
的{{1}}属性。我假设您使用的是SCNView
的子类ARSCNView
。
答案 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是否会阻止您的进程,如果是,我将编辑答案。