Come applicare un Vignette CIFilter a un feed live della videocamera in iOS?



avfoundation core-image (1)

Mentre provo ad applicare un semplice filtro vignetta al feed della fotocamera grezzo di un iPhone6, con l'aiuto di Metal e Core Image, vedo un sacco di ritardo tra i fotogrammi elaborati e renderizzati in un MTKView

L'approccio che ho seguito è (MetalViewController.swift):

  1. Ottieni output della videocamera raw utilizzando AVCaptureVideoDataOutputSampleBufferDelegate
  2. Converti CMSampleBuffer > CVPixelBuffer > CGImage
  3. Crea una MTLTexture con questo CGImage .

Punto n. 2 e 3 sono all'interno del metodo denominato: fillMTLTextureToStoreTheImageData

  1. Applicare un CIFilter al CIImage recuperato da MTLTexture in MTKViewDelegate
    func draw(in view: MTKView) {

        if let currentDrawable = view.currentDrawable {
            let commandBuffer = self.commandQueue.makeCommandBuffer()

            if let myTexture = self.sourceTexture{

                let inputImage = CIImage(mtlTexture: myTexture, options: nil)

                self.vignetteEffect.setValue(inputImage, forKey: kCIInputImageKey)

                self.coreImageContext.render(self.vignetteEffect.outputImage!, to: currentDrawable.texture, commandBuffer: commandBuffer, bounds: inputImage!.extent, colorSpace: self.colorSpace)

                commandBuffer?.present(currentDrawable)

                commandBuffer?.commit()
            }
        }
    }

Le prestazioni non sono affatto ciò che Apple ha menzionato in questo documento: https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/CoreImaging/ci_tasks/ci_tasks.html#//apple_ref/doc/uid/TP30001185-CH3-TPXREF101

Mi sto perdendo qualcosa?


Il tuo passaggio 2 è troppo lento per supportare il rendering in tempo reale ... e sembra che manchi un paio di passaggi. Per il tuo scopo, in genere dovresti:

Impostare:

  1. crea un pool di CVPixelBuffer - usando CVPixelBufferPoolCreate
  2. crea un pool di trame metalliche usando CVMetalTextureCacheCreate

Per ogni fotogramma:

  1. converti CMSampleBuffer > CVPixelBuffer > CIImage
  2. Passa quel CIImage attraverso la tua pipeline di filtri
  3. CVPixelBuffer il rendering dell'immagine di output in un CVPixelBuffer dal pool creato nel passaggio 1
  4. usa CVMetalTextureCacheCreateTextureFromImage per creare una trama metallica con il tuo CVPixelBuffer filtrato

Se impostato correttamente, tutti questi passaggi assicureranno che i dati dell'immagine rimangano sulla GPU, anziché spostarsi dalla GPU alla CPU e tornare alla GPU per la visualizzazione.

La buona notizia è che tutto ciò è dimostrato nel codice di esempio AVCamPhotoFilter di Apple https://developer.apple.com/library/archive/samplecode/AVCamPhotoFilter/Introduction/Intro.html#//apple_ref/doc/uid/TP40017556 . In particolare, vedi la classe RosyCIRenderer e la sua superclasse FilterRenderer .





metalkit