Sample Code

Specifying Histograms with vImage

Calculate the histogram of one image and apply it to a second image.



Histogram specification is a technique that allows you to calculate the histogram of a reference image and apply it to an input image. This sample walks you through the steps to implement histogram specification in vImage:

  1. Creating vImage buffers that represent the reference image and input image.

  2. Calculating the histogram of the reference image.

  3. Specifying the histogram of the input image as the reference image’s histogram.

The example below shows an input image (top left) and a histogram reference image (bottom left), with the result on the right:

Photos showing original image, histogram source image, and histogram specified result.

Create the vImage Buffers

To learn about creating a Core Graphics image format that describes your input and reference images, see Creating a Core Graphics Image Format. In this example, format describes an 8-bit-per-channel ARGB image:

let format = vImage_CGImageFormat(
    bitsPerComponent: 8,
    bitsPerPixel: 32,
    colorSpace: CGColorSpaceCreateDeviceRGB(),
    bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.first.rawValue),
    renderingIntent: .defaultIntent)!

The following code shows how to create a vImage buffer initialized with the input image (the flower in the above image).

    let sourceCGImage = sourceImage.cgImage,
    var sourceBuffer = try? vImage_Buffer(cgImage: sourceCGImage,
                                          format: format) else {
                                            return nil

defer {

The following code shows how to create a vImage buffer initialized with the histogram reference image (the rainbow in the above image).

    let histogramSourceCGImage = histogramSourceImage.cgImage,
    var histogramSourceBuffer = try? vImage_Buffer(cgImage: histogramSourceCGImage,
                                                   format: format) else {
                                                    return nil

defer {

Calculate the Reference Image Histogram

The histogram data is stored in four arrays—one for each channel—where the value of each element is the number of pixels in the reference image with that color value. In an 8-bit-per-channel image, each color channel can hold 256 different values, so each array is defined with a count of 256.

let histogramBins = (0...3).map { _ in
    return [vImagePixelCount](repeating: 0, count: 256)

To populate the histogram arrays with the calculated histogram data, prepare an array of UnsafeMutablePointer<vImagePixelCount> from the arrays, and pass it to vImageHistogramCalculation_ARGB8888.

var error = kvImageNoError

var mutableHistogram: [UnsafeMutablePointer<vImagePixelCount>?] = {
    return UnsafeMutablePointer<vImagePixelCount>(mutating: $0)

error = vImageHistogramCalculation_ARGB8888(&histogramSourceBuffer,

guard error == kvImageNoError else {
    print("Error calculating histogram")
    return nil

After vImageHistogramCalculation_ARGB8888 returns, the four arrays are now populated with the histogram data from the image that histogramSourceBuffer points to.

Specify the Input Image Histogram

The vImageHistogramSpecification_ARGB8888 function accepts a different parameter to receive the histogram data: an array of UnsafePointer<vImagePixelCount>. The following code prepares the four arrays for use in the specification function.

var immutableHistogram: [UnsafePointer<vImagePixelCount>?] = {
    return UnsafePointer<vImagePixelCount>($0)

Because vImageHistogramSpecification_ARGB8888 can work in place, you can pass the source buffer as both the source and destination:

error = vImageHistogramSpecification_ARGB8888(&sourceBuffer,

guard error == kvImageNoError else {
    print("Error specifying histogram")
    return nil

After vImageHistogramSpecification_ARGB8888 returns, sourceBuffer contains the original input image with the histogram specified by the reference image.

See Also

vImage Operations

Adjusting the Brightness and Contrast of an Image

Use a gamma function to apply a linear or exponential curve.

Adjusting Saturation and Applying Tone Mapping

Convert an RGB image to discrete luminance and chrominance channels, and apply color and contrast treatments.

Blurring an Image

Filter an image by convolving it with custom and high-speed kernels.

Adding a Bokeh Effect

Simulate a bokeh effect by applying dilation.

Converting Color Images to Grayscale

Convert a color image to grayscale using matrix multiplication.

Standardizing Arbitrary Image Formats for Processing

Convert assets with disparate color spaces and bit depths to a standard working format for applying vImage operations.

Reducing Artifacts in Resampled Images

Avoid ringing effects introduced by the default Lanczos algorithm when scaling an image by using a custom resampling filter.

Finding the Sharpest Image in a Sequence of Captured Images

Share image data between vDSP and vImage to compute the sharpest image from a bracketed photo sequence.

vImage Operations

Apply image manipulation operations to vImage buffers.