运行 并在指定时间段内暂停 ARSession
Run and Pause an ARSession in a specified period of time
我正在开发具有手势识别功能的 ARKit
/Vision
iOS 应用程序。我的应用程序有一个简单的 UI,其中包含单个 UIView
。根本没有 ARSCNView
/ARSKView
。我将捕获的 ARFrames
序列放入 CVPixelBuffer
中,然后用于 VNRecognizedObjectObservation
.
我不需要会话的任何跟踪数据。 CVPixelBuffer
我只需要 currentFrame.capturedImage
。我需要以 30 fps 的速度捕获 ARFrames。 60 fps 是过高的帧速率。
preferredFramesPerSecond
instance property is absolutely useless in my case, because it controls frame rate for rendering an ARSCNView
/ARSKView
. I have no ARViews. And it doesn't affect session's frame rate.
所以,我决定使用 run()
和 pause()
方法来降低会话的帧速率。
问题
我想知道如何在指定的时间段内自动run
和pause
一个ARSession? run
和 pause
方法的持续时间必须是 16 ms
(或 0.016 秒)。我想这可能是通过 DispatchQueue
实现的。但是不知道怎么实现。
怎么做?
这是一个伪代码:
session.run(configuration)
/* run lasts 16 ms */
session.pause()
/* pause lasts 16 ms */
session.run(session.configuration!)
/* etc... */
P.S。 我的应用程序中既不能使用 CocoaPod 也不能使用 Carthage。
Update:是关于ARSession的currentFrame.capturedImage
如何获取和使用的
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)
}
}
如果您想要将帧率从 60 降低到 30,您应该使用 SCNView
的 preferredFramesPerSecond
属性。我假设您使用的是 ARSCNView
,它是 SCNView
.
的子类
如果我没理解错的话,你可以通过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 是否会阻止您的进程,如果是,我会编辑我的答案。
我认为 run()
和 pause()
策略不是可行的方法,因为 DispatchQueue API 不是为实时准确性而设计的。这意味着不能保证每次暂停都是 16ms。最重要的是,重新启动会话可能不会立即发生,并且可能会增加更多延迟。
此外,您共享的代码最多只能捕获一张图像,并且由于 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)
}
}
我正在开发具有手势识别功能的 ARKit
/Vision
iOS 应用程序。我的应用程序有一个简单的 UI,其中包含单个 UIView
。根本没有 ARSCNView
/ARSKView
。我将捕获的 ARFrames
序列放入 CVPixelBuffer
中,然后用于 VNRecognizedObjectObservation
.
我不需要会话的任何跟踪数据。 CVPixelBuffer
我只需要 currentFrame.capturedImage
。我需要以 30 fps 的速度捕获 ARFrames。 60 fps 是过高的帧速率。
preferredFramesPerSecond
instance property is absolutely useless in my case, because it controls frame rate for rendering anARSCNView
/ARSKView
. I have no ARViews. And it doesn't affect session's frame rate.
所以,我决定使用 run()
和 pause()
方法来降低会话的帧速率。
问题
我想知道如何在指定的时间段内自动run
和pause
一个ARSession? run
和 pause
方法的持续时间必须是 16 ms
(或 0.016 秒)。我想这可能是通过 DispatchQueue
实现的。但是不知道怎么实现。
怎么做?
这是一个伪代码:
session.run(configuration)
/* run lasts 16 ms */
session.pause()
/* pause lasts 16 ms */
session.run(session.configuration!)
/* etc... */
P.S。 我的应用程序中既不能使用 CocoaPod 也不能使用 Carthage。
Update:是关于ARSession的currentFrame.capturedImage
如何获取和使用的
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)
}
}
如果您想要将帧率从 60 降低到 30,您应该使用 SCNView
的 preferredFramesPerSecond
属性。我假设您使用的是 ARSCNView
,它是 SCNView
.
如果我没理解错的话,你可以通过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 是否会阻止您的进程,如果是,我会编辑我的答案。
我认为 run()
和 pause()
策略不是可行的方法,因为 DispatchQueue API 不是为实时准确性而设计的。这意味着不能保证每次暂停都是 16ms。最重要的是,重新启动会话可能不会立即发生,并且可能会增加更多延迟。
此外,您共享的代码最多只能捕获一张图像,并且由于 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)
}
}