Несколько обновлений CALayer для единого вывода Vision / CoreML

Я пытаюсь использовать Vision и CoreML для передачи стилей отслеживаемых объектов как можно ближе к реальному времени. Я использую AVKit для захвата видео и AVCaptureVideoDataOutputSampleBufferDelegate для получения каждого кадра.

На высоком уровне мой пайплайн:

1) обнаруживать лица

2) обновите слои предварительного просмотра, чтобы рисовать ограничивающие рамки в нужном месте экрана.

3) обрезать исходное изображение до обнаруженных лиц

4) прогоняйте изображения лиц через модель coreML и получайте новые изображения в качестве вывода

5) заполните слои предварительного просмотра (где бы они ни находились) новыми изображениями

Я надеялся разместить ограничивающие прямоугольники, как только они будут вычислены (в основном потоке), а затем заполнить их, как только будет сделан вывод. Однако я обнаружил, что при добавлении вывода coreML в конвейер (в AVCaptureOutputQueue или CoreMLQueue) ограничивающие прямоугольники не обновляют позиции до завершения вывода. Возможно, мне что-то не хватает в том, как обрабатываются очереди при закрытии. Ниже приведены (надеюсь) соответствующие части кода.

Я изменяю код из https://developer.apple.com/documentation/vision/tracking_the_user_s_face_in_real_time.

public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer,
    from connection: AVCaptureConnection) {
    // omitting stuff that gets pixelBuffers etc formatted for use with Vision
    // and sets up tracking requests

    // Perform landmark detection on tracked faces
    for trackingRequest in newTrackingRequests {

        let faceLandmarksRequest = VNDetectFaceLandmarksRequest(completionHandler: { (request, error) in

            guard let landmarksRequest = request as? VNDetectFaceLandmarksRequest,
                let results = landmarksRequest.results as? [VNFaceObservation] else {
                    return
            }

            // Perform all UI updates (drawing) on the main queue,
            //not the background queue on which this handler is being called.

            DispatchQueue.main.async {
                self.drawFaceObservations(results) //<<- places bounding box on the preview layer
            }

            CoreMLQueue.async{ //Queue for coreML uses

                //get region of picture to crop for CoreML
                let boundingBox = results[0].boundingBox 

                //crop the input frame to the detected object
                let image: CVPixelBuffer = self.cropFrame(pixelBuffer: pixelBuffer, region: boundingBox)

                //infer on region
                let styleImage: CGImage = self.performCoreMLInference(on: image)

                //on the main thread, place styleImage into the bounding box(CAShapeLayer)
                DispatchQueue.main.async{
                    self.boundingBoxOverlayLayer?.contents = styleImage
                }
            }
        })

        do {
            try requestHandler.perform(faceLandmarksRequest)
        } catch let error as NSError {
            NSLog("Failed Request: %@", error)
        }
    }
}

Помимо проблемы с очередью / синхронизацией, я думал, что одной из причин замедления может быть обрезка буфера пикселей до интересующей области. У меня здесь нет идей, любая помощь будет оценена


person tyrotyrotyro    schedule 17.07.2018    source источник


Ответы (1)


Я использую конвейер https://github.com/maxvol/RxAVFoundation и https://github.com/maxvol/RxVision, чтобы устранить проблемы с синхронизацией.

Базовый пример -

let textRequest: RxVNDetectTextRectanglesRequest<CVPixelBuffer> = VNDetectTextRectanglesRequest.rx.request(reportCharacterBoxes: true)
var session = AVCaptureSession.rx.session()
var requests = [RxVNRequest<CVPixelBuffer>]()

self.requests = [self.textRequest]
self
  .textRequest
  .observable
  .observeOn(Scheduler.main)
  .subscribe { [unowned self] (event) in
      switch event {
      case .next(let completion):
              self.detectTextHandler(value: completion.value, request: completion.request, error: completion.error)
          default:
          break
      }
  }
  .disposed(by: disposeBag)

self.session
  .flatMapLatest { [unowned self] (session) -> Observable<CaptureOutput> in
      let imageLayer = session.previewLayer
      imageLayer.frame = self.imageView.bounds
      self.imageView.layer.addSublayer(imageLayer)
      return session.captureOutput
  }
  .subscribe { [unowned self] (event) in
      switch event {
      case .next(let captureOutput):
          guard let pixelBuffer = CMSampleBufferGetImageBuffer(captureOutput.sampleBuffer) else {
              return
          }
          var requestOptions: [VNImageOption: Any] = [:]
          if let camData = CMGetAttachment(captureOutput.sampleBuffer, key: kCMSampleBufferAttachmentKey_CameraIntrinsicMatrix, attachmentModeOut: nil) {
              requestOptions = [.cameraIntrinsics: camData]
          }
          let imageRequestHandler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, orientation: .up, options: requestOptions)
          do {
              try imageRequestHandler.rx.perform(self.requests, with: pixelBuffer)
          } catch {
              os_log("error: %@", "\(error)")
          }
          break
      case .error(let error):
          os_log("error: %@", "\(error)")
          break
      case .completed:
          // never happens
          break
      }
  }
  .disposed(by: disposeBag)
person Maxim Volgin    schedule 02.10.2018