我正在开发一个具有Vision框架功能的ARKit应用(处理CoreML模型)。
loopCoreMLUpdate()
函数会形成一个循环,导致非常高的能源影响(CPU=70%,GPU=66%)。
如何处理此任务并将能源影响降低到低水平?
对于这个循环问题,有什么解决方案可以帮助我降低CPU/GPU的工作负载?
这是我的代码:
import UIKitimport SpriteKitimport ARKitimport Visionclass ViewController: UIViewController, ARSKViewDelegate { @IBOutlet weak var sceneView: ARSKView! let dispatchQueueML = DispatchQueue(label: "AI") var visionRequests = [VNRequest]() // ......................................... // ......................................... override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let configuration = AROrientationTrackingConfiguration() sceneView.session.run(configuration) loopCoreMLUpdate() } func loopCoreMLUpdate() { dispatchQueueML.async { self.loopCoreMLUpdate() // 自循环导致非常高的影响 self.updateCoreML() } } func updateCoreML() { let piBuffer: CVPixelBuffer? = (sceneView.session.currentFrame?.capturedImage) if piBuffer == nil { return } let ciImage = CIImage(cvPixelBuffer: piBuffer!) let imageRequestHandler = VNImageRequestHandler(ciImage: ciImage, options: [:]) do { try imageRequestHandler.perform(self.visionRequests) } catch { print(error) } } // ......................................... // .........................................}
回答:
是的,你标记的那一行确实会是一个很大的问题。你在这里不是在循环,而是在前一个任务完成之前尽可能快地生成新的异步任务。无论如何,你试图捕获CVPixelBuffers的速度比它们创建的速度还要快,这是一种巨大的浪费。
如果你想捕获帧,不要创建一个紧凑的循环来采样它们。你应该设置自己为ARSessionDelegate,并实现session(_:didUpdate:)
。系统会在有新帧可用时通知你。(当然,你可以创建自己的渲染循环,但你在这里没有这样做,除非你真的需要自己的渲染管线,否则不应该这样做。)
请记住,你会非常快速地接收到大量的帧。30fps或60fps是很常见的,但也可能高达120fps。你不能使用所有的时间片(其他事情也需要处理器时间)。关键是,你通常无法跟上帧率,要么需要缓冲以便稍后处理,要么丢弃帧,或者两者兼而有之。这是实时处理中非常正常的一部分。
对于这种分类系统,你可能需要选择实际的帧率,可能是低至10-20fps,并跳过帧以维持该速率。分类数十个几乎相同的帧可能没有帮助。
不过,请确保你已经阅读了实时捕获中的对象识别。感觉你正在尝试做这件事,并且有很好的示例代码可用。