---
title: "How to Create Image Filters for iOS"
description: "Write a custom `CIFilter` in Metal and Swift, usable in any CoreImage pipeline."
url: "https://img.ly/blog/how-to-create-image-filters-in-ios/"
type: "blog"
date: "2021-08-24"
author: "Walter"
tags: ["App Development","iOS App Development","iOS","Photo Editing","Tutorial","Development","Mobile App Development","Photo Filter","How-To","Push2Medium"]
---

> This is the markdown version of [How to Create Image Filters for iOS](https://img.ly/blog/how-to-create-image-filters-in-ios/). For all pages in one file, see [llms-full.txt](https://img.ly/llms-full.txt). For an index of all available pages, see [llms.txt](https://img.ly/llms.txt).

---

In this tutorial, you will learn how to write a custom `CIFilter` in Metal and Swift. You can use this filter in any CoreImage pipeline.

Apple offers over 200 image filters in the CoreImage framework, but sometimes you need a little extra to make your images perfect. Apple provides two ways to create customize image filters. You can chain `CIFilter`s together in a `CIFilter` subclass or wrap a `CIKernel` in a `CIFilter` subclass. Either method creates a filter that CoreImage can execute on the GPU. That makes the filter fast. CoreImage filters can filter live video without impacting the frame rate. Before iOS 11 the only way to write a custom `CIKernel` was to pass a string containing the code to the GPU at runtime using the [OpenGL Shading Language](https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language). This had two drawbacks: it was difficult to find errors in the string of commands and the kernel was not compiled until runtime. This tutorial will show you how to add a custom filter with a Metal-based `CIKernel` to any Swift project in Xcode. The code samples in this tutorial use Xcode 12.5 and Swift 5.

Clone [this repository](https://github.com/waltertyree/core-image-metal) for a sample project and example code that supports this tutorial. The repo also contains some other custom filters, demonstrating other aspects of Metal filters.

## Adding Metal Support to an Xcode Project

The first step is to add a Metal source code file to your project.

![new-file](https://blog.img.ly/2021/08/new-file.png)

Find the "Metal File" template, select it and click `Next`. Name the file and save it. At compile time, Xcode combines all of the `.metal` files in your project into a single `.metallib` file. So, organize your Metal code into lots of files or just one file as you prefer. The last step before you start writing code is to add compiler flags for `CIKernel` objects.

In the Build Settings, find the `other Metal Compiler Flags` and add an entry of `-fcikernel`.

![build-settings-1](https://blog.img.ly/2021/08/build-settings-1.png)

Then find the `Other Metal Linker Flags` and add an entry of `-cikernel`.

![other-metal-linker-flag](https://blog.img.ly/2021/08/other-metal-linker-flag.png)

_Important note:_ any project without `.metal` files hides the Metal Compiler and Linker sections from build Settings.

## Setting Up a Metal File for CoreImage Kernels

Metal is a general-purpose technology for writing code that will execute on the GPU. The compiler flags tell Metal that you will be writing code to work with `CoreImage`. With a Metal source file in your project and the compiler flags set, you're finally ready to write some code! Open the Metal file you created above. It should be empty except for

```
#include <metal_stdlib>
using namespace metal;
```

Below these lines, import the CoreImage headers by adding:

```
#include <CoreImage/CoreImage.h>
```

and add a stub for your filter code with:

```
extern "C" {
  namespace coreimage {
    // KERNEL GOES HERE
  }
}
```

Prefix your kernel code with `extern "C"` and the CoreImage namespace. You write kernel code in the [Metal Shader Language](https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf), which is a variation of C. If you only know Swift, you should be able to rely on Xcode's code-completion and symbol lookup to help you as you're learning to write Metal code. In addition to the general language reference, Apple provides a [Metal Shading Language for Core Image Kernels](https://developer.apple.com/metal/MetalCIKLReference6.pdf) document.

The example below creates a `CIColorKernel`. This kernel is optimized for changing the color value of each pixel in an image. It will change each pixel to a grayscale version of itself. A filter applying his kernel will send the color value of each pixel of the image to the kernel.

```
float4 grayscaleFilterKernel(sample_t s) { //1
  float gray = (s.r + s.g + s.b) / 3;      //2
  return float4(gray, gray, gray, s.a);    //3
}
```

Here is how this kernel works:

1.  The filter provides the color of the current pixel to the kernel as a `sample_t` type. The kernel will return the new color for that pixel as a `float4` type. The `sample_t` type is equivalent to a `float4` type and is only named differently because of historical convention. A `float4` type contains four float values. In the case of color, they are the red, green, blue and alpha values for the pixel. Each float has a value between zero and 1.
2.  Calculate a grayscale version of the pixel by averaging the three color channels.
3.  Create a new `float4` with the averaged value for the three color channels and the original alpha value for the fourth.

CoreImage also provides a `CIWarpKernel` class that is optimized for changing the position of each pixel and a `CIBlendKernel` for blending two images. For more complex tasks, CoreImage also provides a general `CIKernel`. Apple suggests that chaining together optimized kernels in a pipeline is usually more efficient than trying to write a larger, general kernel. A color-optimized kernel only has access to the color of the current pixel. A transform-optimized kernel can only affect the position of the current pixel. A general kernel can impact color and position as well as read other values from the source image.

## Wrapping a \`CIKernel\` with a \`CIFilter\`

Now you'll make a `CIFilter` subclass as the interface between the Metal code and your Swift code. Every `CIFilter` has an `outputImage` variable that returns a `CIImage`. Generally, input variables for a filter begin with `input`. The grayscale filter you are making has a single input and a single output. Create a new Swift file in your project and title it "GrayscaleFilter.swift". Then be sure to import CoreImage by adding

```
import CoreImage
```

to the top of the file. Then create the filter as a subclass of `CIFilter`.

```
class GrayscaleFilter: CIFilter {
```

Next, create a `kernel` variable as either `static` or `lazy`, so that it only gets created one time, regardless of how often it's called.

```
static var kernel: CIColorKernel = { () -> CIColorKernel in
    let url = Bundle.main.url(forResource: "default",
                            withExtension: "metallib")! //1
    let data = try! Data(contentsOf: url)
    return try! CIColorKernel(functionName: "grayscaleFilterKernel",
                      fromMetalLibraryData: data) //2
}()
```

1.  Look in the bundle for the compiled metal code. The metal code will have a filename of "default".
2.  The `.metalib` may contain many kernels so use the one called "grayscaleFilterKernel"

Now add a variable to hold the input image.

```
var inputImage: CIImage?
```

Finally override the `outputImage` variable.

```
override var outputImage: CIImage? {
    guard let inputImage = inputImage else { return .none }
    return GrayscaleFilter.kernel.apply(extent: inputImage.extent,
                                   roiCallback: { (index, rect) -> CGRect in
                                                  return rect
                                  }, arguments: [inputImage])
}
```

The `apply` function of the kernel takes an `extent` argument. For a `CIImage` the `extent` property is the width and height of the image. The function has an `roiCallback` that allows you to specify what part of the image the kernel uses for each calculation. For a color filter, you generally just pass back the `rect`. Finally, pass in any `arguments` as an array. Notice that there is not any type checking here. It is up to you to pass in the correct types in the correct order.

## Using the Filter

With the Metal code wrapped in a `CIFilter` you can now apply the filter images the same as using one of the built-in filters. Create an instance of the filter, then assign the `inputImage` variable a `CIImage` and display the `outputImage`. Your code might look something like this:

```
let filter = GrayscaleFilter()
filter.inputImage = image

displayView.image = UIImage(ciImage: (filter.outputImage ?? image) ?? CIImage())
```

and it can render a grayscale version of an image, like in this example.

![filter-example](https://blog.img.ly/2021/08/filter-example.jpg)

## Going Further

You can write custom filters and chain them to the built-in filters to invent new effects. Many of the math formulas for effects are available on the Internet. Most are easy to translate into the Metal Shader Language to make them perfect for your application. Writing an entire image or video editing application to showcase your filters is a much larger task. Using an SDK like the [IMG.LY](https://img.ly/index.md?utm_source=imgly&utm_medium=blog&utm_campaign=howtos) `PhotoEditorSDK` or `VideoEditorSDK` can help you provide an app that has the features that users will expect in addition to your cool filters.

To add your filter to the `PhotoEditorSDK` you first wrap your `CIFilter` with an `Effect`. For a basic filter, you only need to override the `newEffectFilter` variable.

```
import PhotoEditorSDK

class GrayscaleEffect: Effect {
  override var newEffectFilter: CIFilter? {
      GrayscaleFilter()
  }
}
```

Then add the filter to the `Effect.all` array when configuring the SDK, using something like

```
Effect.all = [NoEffect(), GrayscaleEffect(identifier: "grayscaleFilter", displayName: "Best Gray")]
```

Now your filter appears the same as the other 60+ filters that ship with the SDK.

![](https://blog.img.ly/2021/08/create-filter-for-iOS-2.jpg)

It applies to live camera previews as well as still images.

## Wrapping Up

In this tutorial, you learned how to configure Xcode for custom Metal code. You also learned how to create a color filter kernel in Metal and wrap it in a `CIFilter`. By combining your filters with Apple's filters in pipelines, you can create unique effects for your next app. Further, using an SDK such as `PhotoEditorSDK` or `VideoEditorSDK` allows you to showcase your filters in a full-featured image or video editing application. The PhotoEditor SDK for iOS [documentation](https://img.ly/docs/pesdk/ios/introduction/overview/) will give you deeper look into all other image adjustments including, ofcourse, the image filtering we discussed above.

**Thanks for reading! We hope that you found this tutorial helpful. Feel free to reach out to us on [Twitter](https://twitter.com/imgly) with any questions, comments, or suggestions.**

---

## More Resources

- **[IMG.LY Website](https://img.ly/index.md)** - Creative editing SDKs for photo, video, and design
- **[Documentation](https://img.ly/docs/cesdk/)** - CE.SDK developer documentation
- **[Contact Sales](https://img.ly/forms/contact-sales.md)** - Get a custom quote. A public JSON API accepts the request directly, no account or key needed. Ask your user for consent and their details first.
