Sunday, January 31, 2010

Visualizing RGB

Update: See the follow-up post here.

Aldo Cortesi posted a link today to, a site dedicated to images visualizing the RGB colour space - in particular, 4096x4096 images that use each RGB value exactly once. Inspired by his hilbert curve visualization and the urge to spend a day programming, I present to you: the all-RGB Mandelbrot set.

Sort by colour...

My idea was this: instead of trying to visualize the colour space directly, why not use a base image for the "shape", and then map the RGB spectrum onto it? I thought that if I could find an image with an even spread of colours, this would let me make each pixel unique yet keep the overall look untouched.
To perform this mapping, I chose to define an ordering based on the 3 dimensional Hilbert curve. Cortesi explains it far better than I can, but the basic idea is this: the Hilbert curve can be used to find an ordering of all 16.8 million colours so that if you were to stretch them out on a line, every colour would be there and they would flow smoothly from one to another. Like this, except a lot smoother and a lot longer.
With this ordering in hand it is easy to find the index into this line for each pixel in the source image, sort the pixels, and then assign them the colour they line up with.

Choosing an image

When I started this morning, I had the idea that the output images would look reasonably close to the source images. I was half right; the images certainly have all the same features as before, but the colouring is all wrong. In hindsight the reason is obvious - unless the original had a perfectly even spectrum of colours, the mapping would be stretched in some places and shifted in others, and in general not line up nicely.

While interesting, this wasn't exactly what I was going for. Hmm...what image could I use where it wouldn't matter if the colours were all shifted? The first thing that came to mind was a visualization like the Mandelbrot set, where the colours are arbitrarily chosen anyways. A quick Google search found me this:

Which, when transformed, came out as this:


I created all the visualizations here using Python 2.6 and the Python Imaging Library. It isn't the most efficient code (it takes half an hour to render a 4096x4096 image), but the quick development time easily makes up for it. If anyone is interested in playing with it, I have placed the code on github. Or if you just want to see what something would look like, feel free to send it to me and I'd be happy to run it for you.


  1. Thanks for your submission. It has been published on allRGB. Cheers!


  2. I'd like to see the code, or (probably better) a more detailed description of the algorithm you created - I'm trying to write a photo-based entry in PHP.

  3. Ok, I have posted the code on GitHub:

    For a more detailed algorithm:
    1. Calculate the Hilbert index of each pixel, and place into an array. EG an array of (index, x, y) tuples.
    2. Randomize array, to avoid patterns in pixels with the same index.
    3. Sort the array by Hilbert index. Now you have a list of (x, y) coordinates with non-decreasing hilbert index, exactly 2^24 long.
    4. Colour the pixels in order. So the first pixel gets the first colour on the Hilbert line, the second pixel gets the second, etc.

    I have some ideas for variations of this algorithm if you'd like to discuss them, or if you have any particular questions I'd be happy to answer those as well.

  4. Eric,

    I'm interested in using your Python script to AllRGBify some images, would you care to give me a quick explanation of how your script is meant to function? That is, it looks like I have to modify the code to tell it what image to transform, is that right? From the command line do I pass any information to the script to change its operating parameters? I'm a programmer myself (though not of Python) and I glanced over the code and as far as I can tell there are functions, but nothing is calling the main one to start the whole process?


  5. You are correct, there was no main method to speak of - I always code in an interactive console, so I just call methods from there. I have updated the code on GitHub to add one, however. You should now be able to call

    python --image="test image.png" --bpc=6

    (using python 2.6 with the PIL installed).

    Image is the image to transform (the output file will be "[name]_allrgbify.png" in the same directory), and bpc is the number of bits per colour channel desired. So --bpc=2 will give you an 8x8 output, 4 will give you 64x64, 6 will give you 512x512, and 8 will give you the full 4096x4096. I generally use 6 for testing, to get a quick preview of what the output image is going to look like.

    Hope that helps.