Apply effects such as sepia tint, highlight strengthening, and scaling to images.
Framework
- Core Image
Overview
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:
Apply the sepia filter to tint an image with a reddish-brown hue.
Add the bloom filter to accentuate highlights.
Use the Lanczos scale filter to scale an image down.
Filtering a waterwheel image using built-in Core Image 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.
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.
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.
Note
The built-in filters are not separate class types with visible properties. You must know their names and input parameters in advance; see Core Image Filter Reference for a list of filters and their effects. Some of the more common input parameter types have associated keys, such as k
. If you cannot infer the associated key constant, you can simply use the string literal found in the filter reference.
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 CISepia
filter.
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.
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 UIImage
in the view hierarchy.
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.
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 input
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.
Note
The CIGloom
filter performs the opposite effect.
To display the output, convert the CIImage
to a UIImage
.
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 CILanczos
to obtain a high-quality downsampling of the image, preserving the original image’s aspect ratio through the CILanczos
filter’s input parameter aspect
. For built-in Core Image filters, calculate the aspect ratio as the image’s width over height, as in 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 CILanczos
filter also outputs its result as a CIImage
.
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!
}
Important
To optimize computation, Core Image does not actually render any intermediate CIImage
result until you force the CIImage
to display its content onscreen, as you might do using UIImage
.
Showing the final result in a UIImage
self.imageView.image = UIImage(ciImage:scaledCIImage)
Note
Under the hood, Core Image optimizes filtering by reordering the three chained filters and concatenating them into a single image processing kernel, saving computation and rendering cycles.
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.