Processing an Image Using Built-in Filters

Apply effects such as sepia tint, highlight strengthening, and scaling to images.


You can add effects to images by applying Core Image filters to CIImage objects. Figure 1 shows three filters chained together to achieve a cumulative effect:

  1. Apply the sepia filter to tint an image with a reddish-brown hue.

  2. Add the bloom filter to accentuate highlights.

  3. Use the Lanczos scale filter to scale an image down.

Figure 1

Filtering a waterwheel image using built-in Core Image filters

Photo of a waterwheel filtered using sepia tone, bloom, and Lanczos scale filters

Create a Context

CIImage processing occurs in a CIContext object. Creating a CIContext is expensive, so create one during your initial setup and reuse it throughout your app.

Listing 1

Creating a context in Core Image

let context = CIContext()

Load an Image to Process

The next step is to load an image to process. This example loads an image from the project bundle.

Listing 2

Loading an image into CIImage

let imageURL = URL(fileURLWithPath: "\(Bundle.main.bundlePath)/YourImageName.png")
let originalCIImage = CIImage(contentsOf: imageURL)!self.imageView.image = UIImage(ciImage:originalCIImage)

The CIImage object is not itself a displayable image, but rather image data. To display it, you must convert it to another type, such as UIImage.

Apply Built-In Core Image Filters

A CIFilter represents a single operation or recipe for a particular effect. To process a CIImage object, pass it through CIFilter objects. You can subclass CIFilter or draw from the existing library of built-in filters.

Tint Reddish-Brown with the Sepia Filter

Although you can chain filters without separating them into functions, the following example shows how to configure a single CIFilter, the CISepiaTone filter.

Listing 3

Applying sepia tint as a CIFilter

func sepiaFilter(_ input: CIImage, intensity: Double) -> CIImage?
    let sepiaFilter = CIFilter(name:"CISepiaTone")
    sepiaFilter?.setValue(input, forKey: kCIInputImageKey)
    sepiaFilter?.setValue(intensity, forKey: kCIInputIntensityKey)
    return sepiaFilter?.outputImage

To pass the image through the filter, call the sepia filter function.

Listing 4

Calling the sepia filter function

let sepiaCIImage = sepiaFilter(originalCIImage, intensity:0.9)

You can check the intermediate result at any point in the filter chain by converting from CIImage to the user interface-displayable UIImage and assigning the UIImage to a UIImageView in the view hierarchy.

Listing 5

Displaying intermediate outputs as UIImage

self.imageView.image = UIImage(ciImage:sepiaCIImage!)

Strengthen Highlights with the Bloom Filter

The bloom filter accentuates the highlights of an image. You can apply it as part of a chain without factoring it into a separate function, but this example encapsulates its functionality in a separate function.

Listing 6

Applying the bloom filter as a CIFilter

func bloomFilter(_ input:CIImage, intensity: Double, radius: Double) -> CIImage?
    let bloomFilter = CIFilter(name:"CIBloom")
    bloomFilter?.setValue(input, forKey: kCIInputImageKey)
    bloomFilter?.setValue(intensity, forKey: kCIInputIntensityKey)
    bloomFilter?.setValue(radius, forKey: kCIInputRadiusKey)
    return bloomFilter?.outputImage

Like the sepia filter, the intensity of the bloom filter’s effect ranges between 0 and 1, with 1 being the most intense effect. The bloom filter has an additional inputRadius parameter to determine how much the glowing regions will expand. Experiment with a range to values to fine tune the effect, or assign the input parameter to a control like a UISlider to allow your users to tweak its values.

To display the output, convert the CIImage to a UIImage.

Listing 7

Calling the bloom filter

let bloomCIImage = bloomFilter(sepiaCIImage, intensity:1, radius:10)
_imageView.image = UIImage(ciImage:bloomCIImage)

Scale Image Size with the Lanczos Scale Filter

Apply the CILanczosScaleTransform to obtain a high-quality downsampling of the image, preserving the original image’s aspect ratio through the CILanczosScaleTransform filter’s input parameter aspectRatio. For built-in Core Image filters, calculate the aspect ratio as the image’s width over height, as in Listing 8.

Listing 8

Computing aspect ratio as height over width

CGFloat imageWidth = originalUIImage.size.width
CGFloat imageHeight = originalUIImage.size.height
let aspectRatio = Double(originalImage.size.width) / Double(originalImage.size.height)
let scaledCIImage = scaleFilter(bloomCIImage, aspectRatio:aspectRatio, scale:0.5)

Like other built-in filters, the CILanczosScale filter also outputs its result as a CIImage.

Listing 9

Applying the Lanczos scale transform as a CIFilter

func scaleFilter(_ input:CIImage, aspectRatio : Double, scale : Double) -> CIImage
    let scaleFilter = CIFilter(name:"CILanczosScaleTransform")!
    scaleFilter.setValue(input, forKey: kCIInputImageKey)
    scaleFilter.setValue(scale, forKey: kCIInputScaleKey)
    scaleFilter.setValue(aspectRatio, forKey: kCIInputAspectRatioKey)
    return scaleFilter.outputImage!

Listing 10

Showing the final result in a UIImageView

self.imageView.image = UIImage(ciImage:scaledCIImage)

In addition to trying out the built-in filters for a fixed effect, you can combine filters in certain Filter Recipes to accomplish tasks such as Applying a Chroma Key Effect, Selectively Focusing on an Image, Customizing Image Transitions, and Simulating Scratchy Analog Film.

See Also

First Steps

class CIImage

A representation of an image to be processed or produced by Core Image filters.

class CIFilter

An image processor that produces an image by manipulating one or more input images or by generating new image data.

class CIContext

An evaluation context for rendering image processing results and performing image analysis.

Basic Data Types

Colors, vectors, and other types used in applying or creating image filters.