From chaining and blending to digital compositing Core Image CIFilter

nicole
5 min readSep 5, 2022

--

Core Image is a powerful iOS framework that makes hardware-accelerated image manipulation easy. Oftentimes, as developers, we use it to add graphical effects to images in our app. The typical process involves choosing a right CIFilter, setting parameters, and applying the filter to an image.

Sometimes, the process may involve chaining several filters to get the desired result. For example, we can use a CIColorControls filter to first adjust the saturation of an image followed by applying a Vignette filter to add a dark fading border around the edges.

Background for a wrapper Library

Core Image CIFilter by itself is already a full fledged library that we are able to use independently. Furthermore, we often find ourselves referring to the open-source Filterpedia project by Simon Gladman for implementation details. With these, we should already have everything that we need. So, why bother with another library?

There may be times when we need to go beyond just chaining a few CIFilter. We may need to chain filters, blend the output with another image, and then apply more filters to achieve the desired result. Or in other words, we may need to apply some kind of Node Graph (Digital Compositing) to get the effect we wanted. As developers, we often find ourselves in this programmatical chaining and blending process repeatedly.

Beyond this, we may want to twirl the output, and then further apply an Addition composition/blend with another image. We often need to test this out in programming code.

For example, a CIEdgeWork filter produces a stylized black-and-white rendition of an image that looks similar to a woodblock cutout. The output of this filter, however, requires a background image to visualize. This requires a composite filter, CISourceAtopCompositing, to place the output of CIEdgeWork over a constant color background (CIConstantColor).

Open Source Nodef wrapper library for Digital Compositing

The Nodef library is a very simple wrapper for applying a node graph (of CIFilter) on an image. The library is the same library used in the open-source Nodef app that that reimagines node-based compositing on a mobile device with an innovative Node Pipeline. The app enables us to perform many of the compositing behavior we desire without changing a single line of code.

Besides providing a library for node-based compositing, the library also provides default values for each of the different filters and implements the ‘Codable’ protocol for saving and loading a node graph in JSON. Using the JSON file, we can be creatively compositing on a mobile device, saving the composite as a file and then loading it on our desktop computer with an application or command line tool.

Happy Compositing on Mobile!

Original Image

Add a CIColorMonoChrome Filter

func createOneFilter(_ inputImage: UIImage) -> UIImage {let filters = FiltersX() 
filters.add(filterHolder: filters.getFilterWithHolder("Color Monochrome"))
return filters.applyFilters(image: inputImage)
}

Chain Filters

CISepiaTone takes the original image as the input image and CIZoomBlur takes the output of CISepiaTone as the input image.

func chainFilters(_ inputImage: UIImage) -> UIImage { let filters = FiltersX() 
filters.add(filterHolder: filters.getFilterWithHolder("Sepia Tone")) filters.add(filterHolder: filters.getFilterWithHolder("Zoom Blur")) return filters.applyFilters(image: inputImage)
}

Blend Filters

First apply a CIDotScreen filter.

func blendFilters(_ inputImage: UIImage) -> UIImage { let filters = FiltersX() filters.add(filterHolder: filters.getFilterWithHolder("Dot Screen")) 
filters.add(filterHolder: filters.getFilterWithHolder("Subtract Blend Mode"))
return filters.applyFilters(image: inputImage) }

CISubtractBlendMode takes the output of CIDotScreen as the inputImage and the original image as the backgroundImage.

Create a CICheckboardGenerator filter

CICheckboardGenerator requires no input Image.

func generatorFilters(_ inputImage: UIImage) -> UIImage { let filters = FiltersX() filters.size=CGSize(width:inputImage.size.width, height:inputImage.size.height)

filters.add(filterHolder: filters.getFilterWithHolder("Checkerboard Generator"))
return filters.applyFilters(image: inputImage) }

Changing the width of CICheckboardGenerator.

func filterProperties(_ inputImage: UIImage) -> UIImage { let filters = FiltersX() filters.size=CGSize(width:inputImage.size.width, height:inputImage.size.height) let fxHolder=filters.getFilterWithHolder("Checkerboard Generator") (fxHolder.filter as! CheckerboardGeneratorFX).width = 500 filters.add(filterHolder: fxHolder) return filters.applyFilters(image: inputImage) }

Digital Compositing Filters

Apply a CIMultiplyBlendMode on the CIColorMonochrome version of the original image with a CICheckboardGenerator.

func compositingFilters(_ inputImage: UIImage) -> UIImage { let filters = FiltersX() filters.size=CGSize(width:inputImage.size.width, height:inputImage.size.height) filters.add(filterHolder: filters.getFilterWithHolder("Color Monochrome")) //Node 1 
filters.add(filterHolder: filters.getFilterWithHolder("Checkerboard Generator")) //Node 2
let fxHolder=filters.getFilterWithHolder("Multiply Blend Mode") (fxHolder.filter as! MultiplyBlendModeFX).inputImageAlias = "2" (fxHolder.filter as! MultiplyBlendModeFX).backgroundImageAlias = "1" filters.add(filterHolder: fxHolder) return filters.applyFilters(image: inputImage) }

Digital Compositing with a Node Graph

First, chain the original image with the following filters.

Original Image -> CILineScreen -> CIColorMonochrome

Next, generate a checkboard and then apply a triangle tile.

CheckboardGenerator -> CITriangleTile

Finally, multiply blend the output of the color monochrome with the output of the triangle tile.

CIMultiplyBlendMode on CIColorMonochrome and CITriangleTile

The Swift code for the node graph above.

func nodeGraphFilters(_ inputImage: UIImage) -> UIImage { let filters = FiltersX() filters.size=CGSize(width:inputImage.size.width, height:inputImage.size.height) filters.add(filterHolder: filters.getFilterWithHolder("Line Screen")) //Node 1 
filters.add(filterHolder: filters.getFilterWithHolder("Color Monochrome")) //Node 2
filters.add(filterHolder: filters.getFilterWithHolder("Checkerboard Generator")) //Node 3
filters.add(filterHolder: filters.getFilterWithHolder("Triangle Tile")) //Node 4
let fxHolder=filters.getFilterWithHolder("Multiply Blend Mode") (fxHolder.filter as! MultiplyBlendModeFX).inputImageAlias = "4" (fxHolder.filter as! MultiplyBlendModeFX).backgroundImageAlias = "2" filters.add(filterHolder: fxHolder) return filters.applyFilters(image: inputImage) }

Compiling the Source

  • Download the Source Code
  • Launch XCode and load Nodef.xcodeproj Build and run on iPhone Simulator or Device
  • or download the Nodef app on the App Store

Originally published at https://github.com.

--

--

nicole

WebAssembly, .NET MAUI, Blazor, Swift, VFX, Metal, and Rust