Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2026 Photos & Camera

WWDC26 · 16 min · Photos & Camera

Enhance RAW image processing with Core Image

Harness the power of version 9 of the Core Image RAW processing APIs to dramatically improve image quality in your apps, with improved sharpness and more defined color, while using the Apple Neural Engine for optimal performance. Take advantage of the CIRAWFilter API to let your users edit RAW photos by changing exposure, noise reduction, sharpness, contrast and more. And explore new CIImageProcessor APIs that optimize performance by giving you precise control over tile sizing and buffer management.

Watch at developer.apple.com ↗

Transcript all transcripts

Chapters

  • 0:00 — Introduction
  • 0:52 — How Core Image supports RAW
  • 2:48 — The evolution of RAW support
  • 3:33 — RAW 9 overview
  • 3:56 — RAW 9 quality improvements
  • 5:50 — Enable and edit RAW 9 with CIRAWFilter API
  • 8:33 — RAW 9 performance overview
  • 9:19 — Interactive editing
  • 10:52 — Exporting to other formats
  • 11:50 — New CIImageProcessor features

Code shown on screen · 3 snippets

Contact for exports swift · at 11:08 ↗
let exportCtx = CIContext(options : [
  .cacheIntermediate : false,
  .memoryLimit : 512 ])
CIImageProcessor with explicit output tile sizes swift · at 12:23 ↗
import CoreImage

class MyProcessor: CIImageProcessorKernel {
    override class func roi(forInput input: Int32,
                            arguments: [String : Any]?,
                            outputRect: CGRect) -> CGRect { return outputRect }
    
    override class func process(with inputs: [CIImageProcessorInput]?,
                                arguments: [String : Any]?,
                                output: CIImageProcessorOutput) throws {
        guard let input = inputs?.first,
              let iBuffer = input.pixelBuffer,
              let oBuffer = output.pixelBuffer else { return }
        
        let iRegion = input.region
        let oRegion = output.region // controlled by Core Image
        
        // MyCopyBuffer(iBuffer,iRegion, oBuffer,oRegion)
    }
}

let extent = inImg.extent
let tileSize = 512.0 // whatever tile size you want
var tiles: [CIVector] = []
for y in stride(from: extent.minY, to: extent.maxY, by: tileSize) {
    for x in stride(from: extent.minX, to: extent.maxX, by: tileSize) {
        let tile = CGRect(x: x, y: y,
                          width: min(tileSize, extent.maxX - x),
                          height: min(tileSize, extent.maxY - y))
        tiles.append(CIVector(cgRect: tile))
    }
}

let result = try MyProcessor.apply(withTiledExtent: tiles, inputs: [inImg], arguments: [:])
CIImageProcessor using temporary PixelBuffer swift · at 14:24 ↗
import CoreImage

class MyProcessor: CIImageProcessorKernel {
    override class func process(with inputs: [CIImageProcessorInput]?,
                                arguments: [String: Any]?,
                                output: CIImageProcessorOutput) throws {
        guard let input = inputs?.first,
              let srcPixelBuffer = input.pixelBuffer,
              let dstPixelBuffer = output.pixelBuffer else { return }
        
        // Get a scratch buffer from Core Image's cache
        guard let scratch = output.temporaryPixelBuffer(identifier : "myScratch",
                   format: kCVPixelFormatType_64RGBAHalf,
                   width: Int(output.region.width),
                   height: Int(output.region.height),
                   pixelBufferAttributes: nil) else { return }
        
        // Step 1: copy input CVPixelBuffer → scratch
        // Step 2: process pixels in scratch
        // Step 3: copy scratch → output CVPixelBuffer
    }
}

Resources