1

我正在开发一个应用程序,该应用程序从相机捕获帧并混合由另一个线程更新的图像。我的问题是每 500 毫秒调用一次的图像创建线程消耗了影响其他线程的峰值计算能力。

这里有一些日志以供澄清:

captureOutput(): used time:  0.027741014957428  , frame rate:  36.0477077545513
captureOutput(): used time:  0.0285720229148865  , frame rate:  34.9992719444091
captureOutput(): used time:  0.0310209989547729  , frame rate:  32.2362281581567
captureOutput(): used time:  0.0268059968948364  , frame rate:  37.3050852733863
captureOutput(): used time:  0.0263729691505432  , frame rate:  37.9176115624965
motionCallback(): starting drawing: 
captureOutput(): used time:  0.0376390218734741  , frame rate:  26.5681718127947
motionCallback(): elapesed time:  0.0835540294647217
captureOutput(): used time:  0.0581380128860474  , frame rate:  17.2004502795793
captureOutput(): used time:  0.0364410281181335  , frame rate:  27.4415967836645
captureOutput(): used time:  0.0278580188751221  , frame rate:  35.8963070734734
captureOutput(): used time:  0.0283130407333374  , frame rate:  35.3194137435949
captureOutput(): used time:  0.0271909832954407  , frame rate:  36.7768972947616
captureOutput(): used time:  0.0268760323524475  , frame rate:  37.2078730552999

如您所见,captureOutput 的帧速率超过 30 fps。一旦motionCallback 创建图像,帧速率就会下降然后再次上升。请注意,motionCallback() 仅在每 15 个捕获帧调用一次,因此平均计算能力应该很容易就足够了。

captureOutput() 在这样创建的队列上运行:

required init?(coder aDecoder: NSCoder) {
  super.init(coder: aDecoder)
  self.m_captureSessionQueue = DispatchQueue(label: "CaptureSessionOutputQueue", attributes: DispatchQueueAttributes.serial)
}

后来我像这样设置 AVCapture:

videoDataOutput.setSampleBufferDelegate(self, queue: self.m_captureSessionQueue)

motionCallback() 设置如下:

let interval = 0.4
if m_manager.isDeviceMotionAvailable {
  m_manager.showsDeviceMovementDisplay = true
  m_manager.deviceMotionUpdateInterval = interval
  // XArbitraryCorrectedZVertical
  m_manager.startDeviceMotionUpdates(using: CMAttitudeReferenceFrame.xArbitraryCorrectedZVertical , to: OperationQueue.main, withHandler: motionCallback)
  print("viewDidLoad(): CM initialized !")
}

然后:

func motionCallback(_ motion: CMDeviceMotion?, error: NSError?) -> Void {
  guard let mot = motion else {return}
  print("motionCallback(): starting drawing: ")

  let start = NSDate() // <<<<<<<<<< Start time
  self.m_instrview?.update(attitude: mot.attitude)
  self.m_overlayImage = CIImage(image: self.m_instrview!.generateImage()!)

  let end = NSDate()  // <<<<<<<<<<   end time
  let timeInterval: Double = end.timeIntervalSince(start as Date)
  print("motionCallback(): elapesed time: ", timeInterval)
}

和 generateImage() 像这样:

func generateImage() -> UIImage {
  UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.main().scale )
  drawHierarchy(in: self.bounds, afterScreenUpdates: true)
  let image = UIGraphicsGetImageFromCurrentImageContext()
  UIGraphicsEndImageContext()
  return image!
}

这个想法是每次 CoreMotion 系统让我更新时,我都会生成一个新图像。

我的问题是如何平均计算 motionCallback() 的计算时间,这样它就不会消耗导致帧速率下降的峰值 CPU 功率?所以我会接受它运行 10 倍的时间,但在此期间只消耗 1/10 的 cpu。

有什么想法可以控制吗?

谢谢克里斯

4

1 回答 1

1

创建一个优先级低于执行帧捕获的异步队列(使用 dispatch_queue_create)。在这个较低优先级的队列上运行你的 motionCallback。

您的 motionCapture 方法在主队列上运行,该队列在主线程上运行其任务。不要那样做。

这一行是错误的:

m_manager.startDeviceMotionUpdates(
  using: CMAttitudeReferenceFrame.xArbitraryCorrectedZVertical , 
  to: OperationQueue.main, 
  withHandler: motionCallback)

更改 to: 参数。

您需要创建一个以低于您创建的 CaptureSessionOutputQueue 的优先级运行的新串行队列,并将其传递到上面的 to: 参数中。

我刚刚开始学习 Swift 3,所以我还没有用于创建调度队列的新代码的语法。

于 2016-07-28T20:49:47.333 回答