<!--
{
  "documentType" : "article",
  "framework" : "Accelerate",
  "identifier" : "/documentation/Accelerate/optimizing-image-processing-performance",
  "metadataVersion" : "0.1.0",
  "role" : "article",
  "title" : "Optimizing image-processing performance"
}
-->

# Optimizing image-processing performance

Improve your app’s performance by converting image buffer formats from interleaved to planar.

## Discussion

The vImage library operates on image data with two memory layouts:

**Interleaved** stores each pixel’s color data consecutively in a single buffer. For example, the data that describes a 4-channel image (red, green, blue, and alpha) would be stored as RGBARGBARGBA…

**Planar** stores each color channel in separate buffers. For example, a 4-channel image would be stored as four individual buffers containing red, green, blue, and alpha data.

![A diagram showing how the color information for each pixel in an image is stored in interleaved and planar buffers.](images/com.apple.accelerate/media-3023512@2x.png)

Because many vImage functions operate on a single color channel at a time — by converting an interleaved buffer to planar buffers — you can often improve your app’s performance by doing this conversion manually. However, most vImage functions are available in both the interleaved and planar variants, so before you do the conversion, try both to see which works better in your context.

In some cases, you may not want to apply a vImage operation to all four channels of an image. For example, you may know beforehand that the alpha channel is irrelevant in the images that you’re dealing with, or perhaps all of your images are grayscale and you need to operate on only one channel. Using planar formats makes it possible to isolate and work with only the channels you need.

### Review interleaved performance

Typically, your source imagery is in interleaved format, and your default option will be to use the interleaved variant of a vImage function. For example, the following code scales a Core Graphics image to one tenth of its original size. Note that the 4-channel, 8-bit-per-channel interleaved pixel buffer [`scale(destination:)`](/documentation/Accelerate/vImage/PixelBuffer/scale(destination:)-5euvc) function calls [`vImageScale_ARGB8888(_:_:_:_:)`](/documentation/Accelerate/vImageScale_ARGB8888(_:_:_:_:)).

```swift
var cgImageFormat = vImage_CGImageFormat(
    bitsPerComponent: 8,
    bitsPerPixel: 8 * 4,
    colorSpace: CGColorSpaceCreateDeviceRGB(),
    bitmapInfo: CGBitmapInfo(rawValue:
                                CGImageAlphaInfo.noneSkipLast.rawValue))!

let sourceBuffer = try vImage.PixelBuffer(cgImage: sourceImage,
                                          cgImageFormat: &cgImageFormat,
                                          pixelFormat: vImage.Interleaved8x4.self)

let destinationBuffer = vImage.PixelBuffer(size: .init(width: sourceBuffer.width / 10,
                                                       height: sourceBuffer.height / 10),
                                           pixelFormat: vImage.Interleaved8x4.self)

let time = ContinuousClock().measure {
    sourceBuffer.scale(destination: destinationBuffer)
}
```

You can use <doc://com.apple.documentation/documentation/Swift/ContinuousClock> to measure the execution time.

### Convert an interleaved source buffer to planar buffers

The pixel buffer [`init(cgImage:cgImageFormat:pixelFormat:)`](/documentation/Accelerate/vImage/PixelBuffer/init(cgImage:cgImageFormat:pixelFormat:)) initializer and the vImage buffer [`vImageBuffer_InitWithCGImage(_:_:_:_:_:)`](/documentation/Accelerate/vImageBuffer_InitWithCGImage(_:_:_:_:_:)) function both populate a buffer based on the properties of a [`vImage_CGImageFormat`](/documentation/Accelerate/vImage_CGImageFormat) structure.

For example, the following code creates an interleaved 3-channel, 8-bit-per-channel [`vImage.PixelBuffer`](/documentation/Accelerate/vImage/PixelBuffer) structure from the source Core Graphics image. The code calls [`deinterleave(destination:)`](/documentation/Accelerate/vImage/PixelBuffer/deinterleave(destination:)-hrhz) to deinterleave the image data and populate the individual red, green, and blue planar pixel buffers.

```swift
var cgImageFormat = vImage_CGImageFormat(
    bitsPerComponent: 8,
    bitsPerPixel: 8 * 3,
    colorSpace: CGColorSpaceCreateDeviceRGB(),
    bitmapInfo: CGBitmapInfo(rawValue:
                                CGImageAlphaInfo.none.rawValue))!

let sourceBuffer = try vImage.PixelBuffer(cgImage: sourceImage,
                                          cgImageFormat: &cgImageFormat,
                                          pixelFormat: vImage.Interleaved8x3.self)
let sourceRedBuffer = vImage.PixelBuffer(size: sourceBuffer.size,
                                         pixelFormat: vImage.Planar8.self)
let sourceGreenBuffer = vImage.PixelBuffer(size: sourceBuffer.size,
                                           pixelFormat: vImage.Planar8.self)
let sourceBlueBuffer = vImage.PixelBuffer(size: sourceBuffer.size,
                                          pixelFormat: vImage.Planar8.self)

sourceBuffer.deinterleave(planarDestinationBuffers: [sourceRedBuffer,
                                                     sourceGreenBuffer,
                                                     sourceBlueBuffer])
```

### Initialize the destination buffers

Create an interleaved 3-channel, 8-bit-per-channel destination buffer and three planar destination buffers:

```swift
let destinationBuffer = vImage.PixelBuffer(size: .init(width: sourceBuffer.width / 10,
                                                       height: sourceBuffer.height / 10),
                                           pixelFormat: vImage.Interleaved8x3.self)

let destinationRedBuffer = vImage.PixelBuffer(size: destinationBuffer.size,
                                              pixelFormat: vImage.Planar8.self)
let destinationGreenBuffer = vImage.PixelBuffer(size: destinationBuffer.size,
                                                pixelFormat: vImage.Planar8.self)
let destinationBlueBuffer = vImage.PixelBuffer(size: destinationBuffer.size,
                                               pixelFormat: vImage.Planar8.self)
```

### Apply the scale operation to the planar buffers

Use the `withTaskGroup(of:returning:body:)` function to start a new scope that contains the three planar scale operations. Note that the 8-bit planar [`scale(destination:)`](/documentation/Accelerate/vImage/PixelBuffer/scale(destination:)-5euvc) function calls [`vImageScale_Planar8(_:_:_:_:)`](/documentation/Accelerate/vImageScale_Planar8(_:_:_:_:)).

In the code below, the [`interleave(destination:)`](/documentation/Accelerate/vImage/PixelBuffer/interleave(destination:)-46cgi) function interleaves the three planar buffers and populates the interleaved destination buffer with the scaled image:

```swift
let time = await ContinuousClock().measure {
    
    await withTaskGroup(of: Void.self) { group in
        
        group.addTask(priority: .userInitiated) {
            sourceRedBuffer.scale(destination: destinationRedBuffer)
        }
        
        group.addTask(priority: .userInitiated) {
            sourceGreenBuffer.scale(destination: destinationGreenBuffer)
        }
        
        group.addTask(priority: .userInitiated) {
            sourceBlueBuffer.scale(destination: destinationBlueBuffer)
        }
    }
    
    destinationBuffer.interleave(planarSourceBuffers: [destinationRedBuffer,
                                                       destinationGreenBuffer,
                                                       destinationBlueBuffer])
}
```

The following code calls [`makeCGImage(cgImageFormat:)`](/documentation/Accelerate/vImage/PixelBuffer/makeCGImage(cgImageFormat:)) to create a Core Graphics image from the result of the scale operation:

```swift
let scaledImage = destinationBuffer.makeCGImage(cgImageFormat: cgImageFormat)
```

---

Copyright &copy; 2026 Apple Inc. All rights reserved. | [Terms of Use](https://www.apple.com/legal/internet-services/terms/site.html) | [Privacy Policy](https://www.apple.com/privacy/privacy-policy)