Overview

In this post I will describe a method of generating images using a Markov Chain built from a training image. We train a markov chain to store pixel colours as the node values and the count of neighbouring pixel colours becomes the connection weight to neighbour nodes. To generate an image, we randomly walk through the chain and paint a pixel in the output image. The result is images that have a similar colour pallette to the original, but none of the coherence. They still look nice though.

Description

At a high level, this algorithm works in the following manner:

Read in a training `image` into an array of pixels Create a mapping `colourMap` from pixel colour to a pixel counter colour. For each `pixel` in `image`: For each `neighbour` of `pixel`: `mapping[pixel.colour][neighbour.colour] += 1` Create a blank `newImage` Choose a random `startingPosition` within the bounds of `newImage` Choose a random `startingState` from `mapping` Create a `stack` with `[startingPosition]` `image[startingPosition] = startingState` while `stack` is not empty: `pixel = stack.pop()` For each `neighbour` of `pixel`: if `neighbour.isColoured`: continue `neighbour.isColoured = true` Push `neighbour` to `stack` `neighbour.colour = ` randomly chosen colour from `mapping[pixel.colour].keys()`. Probability distribution is `mapping[pixel.colour].counts() / sum(mapping[pixel.colour].counts())` Display `newImage`

Outputs

Here’s some example outputs:

An image generated from my black and white portrait:

Code

To run this you’ll need python and the following packages installed:

pip install pillow numpy pyprind pygame

Feel free to mess around with bucket_size and four_neighbour . In my testing I found they didn’t give much difference in ouput.

The final code is here: