Image Processing is the perfect playground for Functional Programming

One of the joys of Kotlin is its support for functional programming. This is typically introduced using examples in which a collection of objects is traversed and certain elements are modified in some way. For example, a List of Employees might be created, and then a function is applied that gives each of them who has been in the job for more than five years a 10 percent pay rise.

This is a good application of functional programming in a business setting and will resonate with experienced programmers because they will have written code to do a similar task in another language. The conciseness and expressiveness of Kotlin will really impress them.

For new programmers, however, we can do something a bit more exciting. Learning is most effective when it is play, so we want a problem domain that encourages experimentation.

In Chapter 15 of, Programming for Beginners, functional programming is introduced in the context of simple image processing. The problems are not too hard, the readers can actually see the results of their code, and there are lots of different ways in which the code can be modified to produce interesting visual effects.

Of course image processing is a huge topic, and the Java library classes that support it would be overwhelming for new programmers. Therefore, we start by introducing a really simple class for modelling images:

class Picture(val pixels: Array<Array<Color>>) {
fun height(): Int {
return pixels.size
}

fun width(): Int {
return pixels[0].size
}

fun pixelByRowColumn(row: Int, column: Int): Color {
return pixels[row][column]
}
}

We also add some basic JavaFX code for loading and displaying images and use this to show a holiday snap:

Then the fun begins. First we add a function for turning the picture red:

fun makeRed(): Picture {
val pixelsRed = Array<Array<Color>>(height()) {
row ->
Array<Color>(width()) {
column ->
val pixel = pixelByRowColumn(row, column)
Color(pixel.red, 0, 0)
}
}
return Picture(pixelsRed)
}

The effect of this is rather startling (this section of the chapter is called “Blood Sunset”):

The reader is then given the simple task of copying and modifying makeRedto produce functions for changing the picture to blue and to green.

These three functions of course have the same structure, and the urge to refactor them is pretty strong. To do this, we take a step back and introduce the concept of a function that takes a Color as input and produces a Color as its output. That is, a function Color -> Color.

The first stage in refactoring makeRed is to break it into two parts: a function for transforming Colors, and then code for applying this on a per-pixel basis:

fun makeRed() : Picture {
val makePixelRed = {it : Color -> Color(it.red, 0, 0)}
val pixelsRed = Array<Array<Color>>(height()) {
row ->
Array<Color>(width()) {
column ->
val pixel = pixelByRowColumn(row, column)
makePixelRed(pixel)
}
}
return Picture(pixelsRed)
}

Once this is done, we extract the second part as a separate function:

fun transform(pixelTransformation: (Color) -> (Color)): Picture {
val transformed = Array<Array<Color>>(height()) {
row ->
Array<Color>(width()) {
column ->
val pixel = pixelByRowColumn(row, column)
pixelTransformation(pixel)
}
}
return Picture(transformed)
}

This allows makeRedto be refactored to:

fun makeRed() : Picture {
val makePixelRed = {it : Color -> Color(it.red, 0, 0)}
return transform(makePixelRed)
}

The functional notation might be a little puzzling to the reader, but they can get some practice with it straight away by refactoring their makeBlue and makeGreeen functions.

At this point, there are lots of algorithms that the reader can play with, and they can literally see the results of their work. For example, there is an exercise to convert an image to grey-scale and another to permute the red, green and blue channels. This gives very strange results:

A more challenging exercise is to apply a black-white thresholding function:

Another nice effect is obtained by taking an image of a Waratah and converting those pixels that do not contain a fair bit of red to grey:

There are further examples in the book, based on famous art works. The Skating Parson dons a purple suit and Lady Agnew of Lochnaw gives us a lesson in colour reduction.

Image manipulation is a perfect setting for learning functional programming. To see for yourself, download the code and images for the chapter from GitHub.