Photo by Lucas Benjamin on Unsplash

Creating all photos ever with Python

Andy Makes Apps
7 min readDec 14, 2021

--

Here is something that I have wanted to try for a long time, generating all possible photos that have ever existed and will ever exist using code. At the end of the day an image is just a particular arrangement of pixels, and Python can generate an image based on a pixel pattern, it can also provide all possible combinations for a list. So, can we use Python to create all photos ever? The short answer is ‘yes’, the long answer however, well…

Before getting started, let’s break it down and simplify the process, just so we are on the same page on how the code will work. Imagine a 2 x 2 gird made up of four squares that can be either black or white, if we were to randomly assign a colour to each square we would get a total of 2⁴ or 16 images. That is because there are 4 squares in total and each square can have 2 possible colours. Now, if we were to expand the grid and instead of a 2 x 2 created a 10 x 10 or even a 1000 x 1000, that number of total images would expand exponentially. A 500 x 500 grid will contain a total of 2⁵⁰⁰ possible images, which is an insanely large number, but more on that later. For now, let’s look at the colours used, instead of simply using black or white, we should assign those squares any colour from the whole RGB spectrum.

One of the 2 x 2 images out of the 16

What is RGB? Well it is a colouring model which uses Red, Green, and Blue (hence the name), as its primary colours, which are then blended together at varying degrees to create an array of all other colours. Most electronics use RGB for the pixels which make up the display. In RGB a pixel has 3 values, one for each colour, and those values, ranging from 0 to 255, represent the intensity or amount of light for each colour. So setting the RGB values on a pixel to (0, 0, 0) would mean there is no light, colouring the pixel black, whereas a (255, 255, 255) one would be only light, making it white. Similarly, (255, 0, 0) is Red, (0, 255, 0) is Green, (0, 0, 255) is Blue, something like (0, 0, 150) would be a dark shade of Blue, and so on.

In case you are wondering, why is it Red, Green, and Blue, when the primary colours are Red, Yellow, and Blue, the answer is quite simple. As you’ve seen setting the intensity of each colour to the maximum value gives us white, but if you have ever tried mixing all colours together when painting you almost certainly didn’t get white but a mushy dark grey or something like that. That is because when dealing with light the colours are additive, meaning adding them all up gives us white, whilst paints are subtractive, adding them all together results in black. Another fun fact, the primary colours are actually Cyan, Magenta and Yellow; Red, Yellow, and Blue, are mere approximations of those colours.
The additive property of RGB

Now that we have our colours defined we can work out how the code will function and just for the fun of it do some maths to show why running the code is impossible. Since in RGB colours values start with 0 and end at 255, that means we have a total of 256 intensity values for one colour, and since we have 3 colours in total, that is 256³ total colours or 16,777,216, quite the number from the initial 2 colours we started with. In other words, in RGB there is a total of almost 17 million possible colours. Going back to our 500 x 500 grid, to create an image we go to each square one by one and randomly pick one of the colours from the RGB spectrum. The result is quite a beautiful mess.

A 500 x 500 randomly created image

Thus we have generated our image, and we can repeat this process for as long as we want until we get to an actual image which means something, but the probability of that occurring is astronomically low. Instead of generating images randomly, let’s see how many possible images we could generate on this grid. So each square can take any value out of 16,777,216 possible ones, and since it is a 500 x 500 grid, there is a total of 250,000 squares. Making the total number of images 16777216²⁵⁰⁰⁰⁰, I wish I could tell you how much that is, but as far as I know there isn’t a calculator out there which could compute that.

If you are like me and you can’t even wrap your head around how large that number actually is, let’s approach this problem in more simple terms. Instead of taking all colours let’s only take the ‘primary’ colours, Red, Yellow, and Blue. So that’s 3 possible options, and for simplicity’s sake instead of a 500 x 500 grid, let’s take a 3 x 3 grid, so 9 squares in total. The pattern should be familiar by now — the total number of images we would get is 3⁹ or 19,683. If we then were to create a new image every 5 seconds, it would take 98,415 seconds to create all images, or about a day. In other words, it would take you one whole day of just drawing a new image every 5 seconds for only 3 colours on a 3 x 3 grid. Don’t know about you, but I can think of much better ways to spend a whole day. Anyway, looking back at the original total number of images, it’s safe to say it would take more than a lifetime. Hence why, generating all those images, although possible, would actually be impossible. And that’s just taking time into account, if we were to look at storage, that’s a whole different story, with just the same outcome.

Now, about building all this in Python…

The whole script used can be found on my GitHub (linked at the bottom), but I will be breaking it down into the main parts here. First off, the class created for this takes two arguments, the length (one side of the grid), and the colouring type (since computation time is key, I have decided to use multiple colouring types like Black and White, Greyscale or Full).

The first task is to generate colour arrays, which contains all possible RGB colours for the different colouring types, to save on time when the code runs next time these arrays are then saved to a dictionary which is saved to a json file. A ‘for-loop’ can easily create the colours for most of the colouring types, however the itertools module needs to be used to create the full array as that will allow us to create all possible combinations.

Creating an image is quite straightforward, the matplotlib module allows us to create a graph which can simply be a 2D array where each cell is the RGB value. The array can be created using numpy, and the easiest way to do this is to make a one dimensional array then break it down by the length and ‘stack’ them on top of each other to create the 2D array.

The final function in the whole process is one which creates a random image by generating those one dimensional arrays and picking one random value for each element from the specified colouring type.

That’s pretty much it. It’s quite a simple script, and at the moment I cannot see any practical applications to it, besides the fact that it was a fun little project to work on. That being said I have a few future plans for the script. In the full code there is another function which creates all possible images based on user inputs. The idea is to have a website created which every few seconds will display an image before discarding it, that way every time someone visits the site that would be the first and only time they are able to view that image. Another function I intend to add to the code is one which allows users to provide an image and define some pixels within it, then the script will only colour those specific pixels. And finally, a more exciting addition would be adding some Machine Learning which quickly skims through randomly generated arrays and only creates images which actually resemble something.

The best way to end this article I think is with one of my favorite math stories. Since I am a big fan of chess and this whole script involved using lots of exponentials, I couldn’t help but be reminded of that one tale when the inventor of chess, after beating his ruler at the game, made a simple request:

Your Majesty I ask you for just one thing. Take your chessboard and place on the first square one grain of wheat. On the first day I will take this grain home to feed my family. On the second day place on the second square 2 grains for me to take home. On the third day cover the third square with four grains for me to take. Each day double the number of grains you give me until you have placed wheat on every square of the chessboard. Then my reward will be complete.

The King replied:

This sounds like a small price to pay for your invention of such a fascinating game, I will see that your request is granted immediately

and hopefully by now we all know how that request turned out…

--

--

Andy Makes Apps

Hey there! I am Andy and I make apps, 60% of the time, they work every time