Search code examples
iosavfoundationcifilterciimage

iOS AVFoundation: Best way to apply CIFilter to photos from AVCapturePhoto for saving


In my AVCapturePhotoCaptureDelegate inside the method public func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) I need to save the photo with a filter effect, looks like I need an CIImage for CIFilter to work.

I can do this:

let ciImage = CIImage(UIImage(photo.fileDataRepresentation())
// ...apply filter
let uiImage = UIImage(ciImage: filter.outputImage!)
let data = uiImage.jpegData(compressionQuality: 1.0)
// ...save data using PHAssetCreationRequest

However, it involves a few image and data conversions, I wonder if there's a better way of doing this?

Thanks!


Solution

  • Yes, you can avoid UIImage conversions here.

    First, create a CIContext somewhere and re-use it every time you filter an image since it's expensive to create one. This is the object that performs the actual image filtering for you (this also happens under the hood when you call jpegData on an UIImage that was initialized with a CIImage). The default parameters (let context = CIContext()) should work well.

    Then in your callback, you can do the following:

    let ciImage = CIImage(data: photo.fileDataRepresentation())
    // apply filter...
    let colorSpace = CGColorSpace(name: CGColorSpace.displayP3)! // or .sRGB, but you camera can most likely shoot P3
    let outputData = self.ciContext.jpegRepresentation(of: filter.outputImage!, colorSpace: colorSpace, 
                                                       options: [kCGImageDestinationLossyCompressionQuality: 0.9])
    // save data...
    

    You can also use heifRepresentation if you want to save as HEIF. And you can obviously also modify the compression quality. I just added it to the example because the default (1.0, i.e. uncompressed) results in large files.