CVPixelBuffer – 如何以每秒 60 帧的速度捕获每第三帧?

CVPixelBuffer – How to capture every THIRD frame out of 60 fps?

我只需要处理每秒 60 帧中的 20 帧 (CVPixelBuffer)。

如何在 ARKit 会话中捕获每三个 ARFrame?我需要大约 20 fps 的速度进行捕捉(我知道可能会出现掉帧)。

这是代码片段:

func updateCoreML() {

    let pixelBuffer: CVPixelBuffer? = (sceneView.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)
    }
}

我所知道的最简单的方法是使用 RxARKit 并将 .throttle() 运算符应用于 session.rx.didUpdateFrame。我认为跳过这么多事件是不明智的,因为帧速率不能保证始终是 60fps,所以最好使用 .throttle() 最多每隔这么多毫秒获取一帧,无论如何实际帧率是。您可以将它的结果插入 RxVision,这将确保 CoreML.

使用该帧
import RxSwift
import RxARKit
import RxVision


let disposeBag = DisposeBag()
let mlRequest: RxVNCoreMLRequest<CVPixelBuffer> = VNCoreMLRequest.rx.request(model: model, imageCropAndScaleOption: .scaleFit)

mlRequest
    .observable
    .subscribe { [unowned self] (event) in
        switch event {
            case .next(let completion):       
                let cvPixelBuffer = completion.value
                if let result = completion.request.results?[0] as? VNClassificationObservation {
                    os_log("results: %@", type: .debug, result.identifier)
                }
            default:
                break
        }
    }
    .disposed(by: disposeBag)

session
    .rx
    .didUpdateFrame
    .throttle(1/20)
    .subscribe { event in
        switch event {
        case .next(let didUpdateFrame):
        let frame: ARFrame = didUpdateFrame.frame        
        let imageRequestHandler = VNImageRequestHandler(cvPixelBuffer: frame.capturedImage, orientation: .up, options: requestOptions)
        do {
            try imageRequestHandler.rx.perform([mlRequest], with: frame.capturedImage) } catch {
            print(error)
        }            
        break
        default:
            break
        }
    }
    .disposed(by: disposeBag)

您可以设置自定义帧速率,这样您将只能获得 20 帧,或者您可以接收所有帧,但通过采用标志变量并递增它来仅使用每三个帧。

  func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
       totalFrameCount += 1
       if totalFrameCount % 3 != 0{ return }
}

或者您可以通过扩展 AVCaptureDevice

来设置自定义帧率
 lockForConfiguration()
 activeVideoMinFrameDuration = CMTimeMake(1, 20)
 activeVideoMaxFrameDuration = CMTimeMake(1, 20)
 unlockForConfiguration()