Building Images with one pixel for every RGB colour
What is an allRGB image?
I first encountered the concept when I stumbled across allrgb.com.
The objective of allRGB is simple: To create images with one pixel for every RGB color (16,777,216); not one color missing, and not one color twice.
As I assume would be the case for most developers, after the idea was presented to me I started to contemplate how I might go about transforming an image such that it meets that criteria but remains recognizable.
Find the code On my GitHub.
The thing that caught my interest about the subject was that I almost immediately came up with a technique that I figured might work, but honestly I had no idea until I put together a proof of concept.
The idea is simple enough that I can just show you an MVP:
import numpy as np from PIL import Image # Create a 4096x4096 version of the input image, then squash it down to a single row im = Image.open("examples/mandrill.tiff").convert("RGB") resized = im.resize((4096, 4096)) src = np.reshape(np.array(resized), (4096*4096, 3)) # Generate a row containing every RGB colour (Our result array) red, green, blue = np.arange(256, dtype='u1'), np.arange(256, dtype='u1'), np.arange(256, dtype='u1') res = np.array(np.meshgrid(red, green, blue)).T.reshape(-1, 3) # Split the two images into their red, green, and blue components res_r, res_g, res_b = res.astype(np.uint32).transpose() src_r, src_g, src_b = src.astype(np.uint32).transpose() # Combine all three values into 1 to use in sorting later combined_res = (res_r << 16) | (res_g << 8) | res_b combined_src = (src_r << 16) | (src_g << 8) | src_b # Sort the result pixels (By red, then green, then blue due to how they were combined) res_sorted = res[np.argsort(combined_res)] # Generate a mapping of unsorted source image -> sorted source image mapping = np.argsort(combined_src) # Reverse that mapping so that it is sorted souce image > source image reverse_mapping = np.argsort(mapping) # reverse the sorting on the sorted result image as if it was the sorted source image final_result = res_sorted[reverse_mapping] # Turn the image back into 4096x4096 and save it image = Image.fromarray(np.reshape(final_result, (4096, 4096, 3)), mode='RGB') image.save('examples/mandrill-sorted.webp')
- This will come up later, but the important part is that the step to generate a “critera” version of the result can be switch out with other methods (For example switching the order of presidence for red, green, and blue) to produce different end results.
The general idea is as follows:
- Generate an image containing every RGB value exactly once, and sort it by some criteria (In the example above they are sorted by red, then green, then blue values). We will call this the result image.
- Generated a mapping to sort the source image by the same criteria. This mapping is then used to generate the reverse mapping; That is how to take the sorted version and turn it back into the orignal source image.
- This mapping is then instead used to “unsort” the result image. The output of that is our final image.
With the concept at least showing that it could work, I started experimenting with different strategies by which the pixels should be sorted.
It turns out that red, then green, then blue is rarely a good strategy.
As part of writing this project I also wrote a verification function that confirms if an image is a valid allRGB image or not.