Images with all colors

  • Similar to the images on allrgb.com, make images where each pixel is a unique color (no color is used twice and no color is missing).

    Give a program that generates such an image, along with a screenshot or file of the output (upload as PNG).

    • Create the image purely algorithmically.
    • Image must be 256×128 (or grid that can be screenshot and saved at 256×128)
    • Use all 15-bit colors*
    • No external input allowed (also no web queries, URLs or databases)
    • No embedded images allowed (source code which is an image is fine, e.g. Piet)
    • Dithering is allowed
    • This is not a short code contest, although it might win you votes.
    • If you're really up for a challenge, do 512×512, 2048×1024 or 4096×4096 (in increments of 3 bits).

    Scoring is by vote. Vote for the most beautiful images made by the most elegant code and/or interesting algorithm.

    Two-step algorithms, where you first generate a nice image and then fit all pixels to one of the available colors, are of course allowed, but won't win you elegance points.

    * 15-bit colors are the 32768 colors that can be made by mixing 32 reds, 32 greens, and 32 blues, all in equidistant steps and equal ranges. Example: in 24 bits images (8 bit per channel), the range per channel is 0..255 (or 0..224), so divide it up into 32 equally spaced shades.

    To be very clear, the array of image pixels should be a permutation, because all possible images have the same colors, just at different pixels locations. I'll give a trivial permutation here, which isn't beautiful at all:

    Java 7

    import java.awt.image.BufferedImage;
    import java.io.BufferedOutputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import javax.imageio.ImageIO;
    
    public class FifteenBitColors {
        public static void main(String[] args) {
            BufferedImage img = new BufferedImage(256, 128, BufferedImage.TYPE_INT_RGB);
    
            // Generate algorithmically.
            for (int i = 0; i < 32768; i++) {
                int x = i & 255;
                int y = i / 256;
                int r = i << 3 & 0xF8;
                int g = i >> 2 & 0xF8;
                int b = i >> 7 & 0xF8;
                img.setRGB(x, y, (r << 8 | g) << 8 | b);
            }
    
            // Save.
            try (OutputStream out = new BufferedOutputStream(new FileOutputStream("RGB15.png"))) {
                ImageIO.write(img, "png", out);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    enter image description here

    Winner

    Because the 7 days are over, I'm declaring a winner

    However, by no means, think this is over. I, and all readers, always welcome more awesome designs. Don't stop creating.

    Winner: fejesjoco with 231 votes

    When you say "Dithering is allowed", what do you mean? Is this an exception to the rule "each pixel is a unique color"? If not, what are you allowing which was otherwise forbidden?

    It means you can place colors in a pattern, so when viewed with the eye, they blend into a different color. For example, see the image "clearly all RGB" on the allRGB page, and many others there.

    small tip for verifying the output: sort the pixel array and check that all values are only 1 in difference as integer

    I actually find your trivial permutation example to be quite pleasing to the eye.

    @Zom-B Man, I freakin' love this post. Thanks!

    Beautiful results/answers!

    Not a valid answer, because it uses a source image, but I enjoyed working on a version of Las grupas de Sorolla.

    I might actually work on making this an iOS app. This actually looks really cool.

    You cannot make images with all possible colors because sRGB can only represent approx. 30% of all possible colors. Also the definition of "equidistant" implicitly depends on the characterisitcs of CRT because sRGB was made to emulate CRT-monitors.

    Why I saw `creating` as `cheating`?

  • fejesjoco

    fejesjoco Correct answer

    7 years ago

    C#

    I put a random pixel in the middle, and then start putting random pixels in a neighborhood that most resembles them. Two modes are supported: with minimum selection, only one neighboring pixel is considered at a time; with average selection, all (1..8) are averaged. Minimum selection is somewhat noisy, average selection is of course more blurred, but both look like paintings actually. After some editing, here is the current, somewhat optimized version (it even uses parallel processing!):

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Diagnostics;
    using System.IO;
    
    class Program
    {
        // algorithm settings, feel free to mess with it
        const bool AVERAGE = false;
        const int NUMCOLORS = 32;
        const int WIDTH = 256;
        const int HEIGHT = 128;
        const int STARTX = 128;
        const int STARTY = 64;
    
        // represent a coordinate
        struct XY
        {
            public int x, y;
            public XY(int x, int y)
            {
                this.x = x;
                this.y = y;
            }
            public override int GetHashCode()
            {
                return x ^ y;
            }
            public override bool Equals(object obj)
            {
                var that = (XY)obj;
                return this.x == that.x && this.y == that.y;
            }
        }
    
        // gets the difference between two colors
        static int coldiff(Color c1, Color c2)
        {
            var r = c1.R - c2.R;
            var g = c1.G - c2.G;
            var b = c1.B - c2.B;
            return r * r + g * g + b * b;
        }
    
        // gets the neighbors (3..8) of the given coordinate
        static List<XY> getneighbors(XY xy)
        {
            var ret = new List<XY>(8);
            for (var dy = -1; dy <= 1; dy++)
            {
                if (xy.y + dy == -1 || xy.y + dy == HEIGHT)
                    continue;
                for (var dx = -1; dx <= 1; dx++)
                {
                    if (xy.x + dx == -1 || xy.x + dx == WIDTH)
                        continue;
                    ret.Add(new XY(xy.x + dx, xy.y + dy));
                }
            }
            return ret;
        }
    
        // calculates how well a color fits at the given coordinates
        static int calcdiff(Color[,] pixels, XY xy, Color c)
        {
            // get the diffs for each neighbor separately
            var diffs = new List<int>(8);
            foreach (var nxy in getneighbors(xy))
            {
                var nc = pixels[nxy.y, nxy.x];
                if (!nc.IsEmpty)
                    diffs.Add(coldiff(nc, c));
            }
    
            // average or minimum selection
            if (AVERAGE)
                return (int)diffs.Average();
            else
                return diffs.Min();
        }
    
        static void Main(string[] args)
        {
            // create every color once and randomize the order
            var colors = new List<Color>();
            for (var r = 0; r < NUMCOLORS; r++)
                for (var g = 0; g < NUMCOLORS; g++)
                    for (var b = 0; b < NUMCOLORS; b++)
                        colors.Add(Color.FromArgb(r * 255 / (NUMCOLORS - 1), g * 255 / (NUMCOLORS - 1), b * 255 / (NUMCOLORS - 1)));
            var rnd = new Random();
            colors.Sort(new Comparison<Color>((c1, c2) => rnd.Next(3) - 1));
    
            // temporary place where we work (faster than all that many GetPixel calls)
            var pixels = new Color[HEIGHT, WIDTH];
            Trace.Assert(pixels.Length == colors.Count);
    
            // constantly changing list of available coordinates (empty pixels which have non-empty neighbors)
            var available = new HashSet<XY>();
    
            // calculate the checkpoints in advance
            var checkpoints = Enumerable.Range(1, 10).ToDictionary(i => i * colors.Count / 10 - 1, i => i - 1);
    
            // loop through all colors that we want to place
            for (var i = 0; i < colors.Count; i++)
            {
                if (i % 256 == 0)
                    Console.WriteLine("{0:P}, queue size {1}", (double)i / WIDTH / HEIGHT, available.Count);
    
                XY bestxy;
                if (available.Count == 0)
                {
                    // use the starting point
                    bestxy = new XY(STARTX, STARTY);
                }
                else
                {
                    // find the best place from the list of available coordinates
                    // uses parallel processing, this is the most expensive step
                    bestxy = available.AsParallel().OrderBy(xy => calcdiff(pixels, xy, colors[i])).First();
                }
    
                // put the pixel where it belongs
                Trace.Assert(pixels[bestxy.y, bestxy.x].IsEmpty);
                pixels[bestxy.y, bestxy.x] = colors[i];
    
                // adjust the available list
                available.Remove(bestxy);
                foreach (var nxy in getneighbors(bestxy))
                    if (pixels[nxy.y, nxy.x].IsEmpty)
                        available.Add(nxy);
    
                // save a checkpoint
                int chkidx;
                if (checkpoints.TryGetValue(i, out chkidx))
                {
                    var img = new Bitmap(WIDTH, HEIGHT, PixelFormat.Format24bppRgb);
                    for (var y = 0; y < HEIGHT; y++)
                    {
                        for (var x = 0; x < WIDTH; x++)
                        {
                            img.SetPixel(x, y, pixels[y, x]);
                        }
                    }
                    img.Save("result" + chkidx + ".png");
                }
            }
    
            Trace.Assert(available.Count == 0);
        }
    }
    

    256x128 pixels, starting in the middle, minimum selection:

    256x128 pixels, starting in the top left corner, minimum selection:

    256x128 pixels, starting in the middle, average selection:

    Here are two 10-frame animgifs that show how minimum and average selection works (kudos to the gif format for being able to display it with 256 colors only):

    The mimimum selection mode grows with a small wavefront, like a blob, filling all pixels as it goes. In the average mode, however, when two different colored branches start growing next to each other, there will be a small black gap because nothing will be close enough to two different colors. Because of those gaps, the wavefront will be an order of magnitude larger, therefore the algorithm will be so much slower. But it's nice because it looks like a growing coral. If I would drop the average mode, it could be made a bit faster because each new color is compared to each existing pixel about 2-3 times. I see no other ways to optimize it, I think it's good enough as it is.

    And the big attraction, here's an 512x512 pixels rendering, middle start, minimum selection:

    I just can't stop playing with this! In the above code, the colors are sorted randomly. If we don't sort at all, or sort by hue ((c1, c2) => c1.GetHue().CompareTo(c2.GetHue())), we get these, respectively (both middle start and minimum selection):

    Another combination, where the coral form is kept until the end: hue ordered with average selection, with a 30-frame animgif:

    UPDATE: IT IS READY!!!

    You wanted hi-res, I wanted hi-res, you were impatient, I barely slept. Now I'm excited to announce that it's finally ready, production quality. And I am releasing it with a big bang, an awesome 1080p YouTube video! Click here for the video, let's make it viral to promote the geek style. I'm also posting stuff on my blog at http://joco.name/, there will be a technical post about all the interesting details, the optimizations, how I made the video, etc. And finally, I am sharing the source code under GPL. It's become huge so a proper hosting is the best place for this, I will not edit the above part of my answer anymore. Be sure to compile in release mode! The program scales well to many CPU cores. A 4Kx4K render requires about 2-3 GB RAM.

    I can now render huge images in 5-10 hours. I already have some 4Kx4K renders, I will post them later. The program has advanced a lot, there have been countless optimizations. I also made it user friendly so that anyone can easily use it, it has a nice command line. The program is also deterministically random, which means, you can use a random seed and it will generate the same image every time.

    Here are some big renders.

    My favorite 512:


    (source: joco.name)

    The 2048's which appear in my video:


    (source: joco.name)


    (source: joco.name)


    (source: joco.name)


    (source: joco.name)

    The first 4096 renders (TODO: they are being uploaded, and my website cannot handle the big traffic, so they are temporarily relocated):


    (source: joco.name)


    (source: joco.name)


    (source: joco.name)


    (source: joco.name)

    Now this is cool!

    Very nice :-D Now make some bigger ones!

    @squeamishossifrage: it is O(N^2) with a big constant multiplier, it takes over a minute with 32K colors, so it would need optimization before I do that. How about I do that after 10 votes :).

    @fejesjoco Start now; you'll get 10 votes; this is a really nice one! I have the same complexity issues with mine; 256x128 takes about 5 seconds but 4096x4096 takes over an hour, but optimizing is difficult because it's easier to play with stuff in unoptimized code.

    You're a true artist! :)

    OK I think I maxed it out, optimized, 512x512, animgifs, what else could I add? The 6-bit image looks just like the 5-bit one, so I'm not really interested in bigger sizes, I don't think it's worth the wait. But of course anyone can try :).

    Keep a list with all the endpoints, just like a backtracking fill algorithm

    I smell a winner! The animations are beautiful and the 512x512 really took it up a notch.

    How much for a print?

    ^ I want one too!

    I'm working on huge renders and an 1080p video. Gonna take hours or days. I hope someone will be able to create a print from a big render. Or even a t-shirt: code on one side, image on the other. Can anyone arrange that?

    whats the code for the last one? wondering about that

    @masterX244: It's the same code. Specific parameters plus a different comparator in the initial pixel sorting (the comparator will be parameterized in the next version).

    @fejesjoco what parameters? maybe you could post the parameters below for usage

    experimenting with some parameters atm to find one suitable for panorama print on 8kx2k size

    I'm rewriting the whole thing to be faster because big renders take too much time. I'm also adding more parameterization options.

    goin to abuse mah webserver as renderer for the panorama big one...

    I'm seriously considering this as a live wallpaper for my phone (at an appropriate framerate). Any objections if I throw up a github/download link if I do? Attribution would obviously be present.

    @Geobits I don't mind but I suggest to wait for the final version. Instead of linking to my SE profile, please check out my contact info on my SE profile and link to those.

    Ooooh, final version. I like the sound of that. Will do.

    rendering atm; 0,05 % on my lowend VPS and 1 % at my comp for a 8192x2048 in average mode with the hue sorting

    @fejesjoco You could submit it to threadless; they make their shirts with spot process printing so the colors should be fine, although I don't know what their size and resolution limits are. Still, they'll handle all the printing, promo, and sales. You have to get community support to get threadless to actually accept the design though. You could also just search around for printers, e.g. this guy. Dunno where you live, but maybe you can find somebody local, although threadless is nice because it handles distribution.

    @JasonC 4% atm on my render; cpu is nicely col at around 40 °C

    It gets slower as it's progressing, it may take many days. I sped it up a lot already, I barely slept. I started running some renders for the night, some of them quit with out of memory exceptions, the others have barely progressed. I'm working hard on making it even faster, please hang tight.

    yeah; i know that it may take days; but its no issue for me to let the comp run 24/7 for a while :P at least tis a nice stability test for the system

    A simple 512x512 render took minutes with the above code, took 1m20s at midnight, and after the 3rd-4th total rewrite, it's now about 20s. Let me work on it a bit more, and let's hope I can make 4Kx4K renders in a couple of hours at most.

    could you post the current versions, too?

    another issue: program doesnt support larger amounts of checkpoints (tiied 50 and above: just getting a error) @fejesjoco

    grrr; error... meant 500 checkpoints

    Needs more parallelism! I want to see those cores *burn*! :D http://i.stack.imgur.com/WmKD7.png

    @fejesjoco even that halfway optimized one would help to get that 4k one rendered cause anything is better than 2 weeks of rendertime :P

    @masterX244 and everyone: Please be patient. A halfway optimized version will simply not get a 4K render. The algorithm is brutally exponential. The 4K image can take 1000x-10000x-100000x more time than the 512K image. I expect the queue size to go up to 1 million, and it is executed millions of times. One trick can mean the difference between hours and days. I am progressing very well and have many more ideas, it shouldn't take much longer. Please wait 1 or 2 days and you will get a 4K render a lot sooner than if you start now with the unfinished version.

    Hmm... was this inspired by diffusion-limited aggregation?

    It is readyyyyyyyyyyyyyyyyyy! See my last edit!

    @Oberon I didn't know about it before. It does look like a similar concept.

    2kx1k in the one mode after hue-sorting ran thru in approx 10 minutes: `All done! It took this long: 00:10:28.3399627` `artgen 128 2048 1024 1024 512 100 9263 11111111 hue-36 0 one` was the commandline used

    btw @fejesjoco still having the seeds used for the video?

    @masterX244: I think 12345, but not sure :). Maybe it should remain a mistery :). I also added a note: you should compile in release mode, it also speeds things up a lot. I have three 4K renders ready, the fourth may be ready today or tomorrow. I will post what I have soon.

    yeah; immediately used release mode :) time to warm up my cpu, too :P

    The program now scales very well to multiple CPU's. I'm running the last 4K render on 8 cores now.

    rgb_2048_2.png is absolutely amazing!

    I added the 4Kx4K renders. Some are still uploading, but the links will point to them when they arrive. So now this project is finished. Maybe I'll dream some new ideas in the coming days.

    finally got a big render done, too.... rendered on 8192x2048 (needing wide ones for personal use) http://files.nplusc.de/public.php?service=files&;t=be95eee699d950f61cf7287b9f68e960

    All your 16M images have exactly 16,703,545 colors for some reason. That's 73671 short of a win.

    can ya tell which ones got nulled? @Zom-B

    I think I've found why your code is so slow (Order O(n^4)), and I think I can fix it, if only I could port it to Java. I'm stuck at C# api things like OrderBy, ToDictionary and AsParallel

    he already optimized :) read the latest edits after the 4k images @Zom-B :) 8kx2k took approx 6 hours with another old render runnign in paralell and other stuff running and eating cpu cycles. Currently writing a small compression utility to keep the imtermediate frames wthout hogging too much disk space up: already got a idea (using one image to track at which frame what pixel appeared)

    Nice video! The music kinda cracks me up; it's so YouTube!

    btw algorithm has one small issue which could give some more speedup if fixed (there are holes which you see as red spots in the hue-sort generated images after finishing -> those holes eat up space in the loop array and somehow get missed until the end when they are forcefilled) and thanks for keeping my CPU warm :)

    @Zom-B: NOOOOOOOOO you're destroying my life!!! Seriously? How did you count? The program does count all colors after the render and I get 16777216, no repetitions!!! And now I checked again again and always get 16777216. I even checked with GIMP's colorcube analysis, same result. And why do you instantly accuse me of losing?

    @masterX244: I know about the holes and, they are a side effect. There are just not enough similar colors to fill each branch. I doubt it can be changed. Or if you change it, it will be a different algorithm, different image. I do like it this way :).

    @Zom-B: I'm open to suggestions about speeding up. I compare each new to pixel to all pixels placed already, it has its complexity. I'm doing that with as few instructions as possible. It may be changed more drastically, I could use a colorcube for searching (except in the average sqaure case, which is the slowest btw) or port it to C... I don't think it's worth any more of my or anyone's time, it's just good enough. If I spend more time with this, that will be on new algorithms.

    Sorry for doubting you. It's Firefox that fscked up your images. I always copy/pasted images into paint shop pro and it always worked, except with your 4k images. Save As->Open in PSP and I count all colors with none missing.

    Ok ZomB... It's just that I lived my life for this thing in the last 3 days, and you scared the hell out of me and I got very nervous. I'm alright now. I think I will submit a Firefox bugreport for that.

    @masterX244 can I ask what you will do with these images? My wife wants to hang one on our wall :)

    @fejesjoco some on my wall, too but others used as paper to fold nice looking boxes :) btw CPU still on full load :P and wide format fitted better on my wall, thats why i make those

    @fejesjoco currently trying to port the simple version of your algo to java as part for my respone to another question but somehow the code says GAH! and doesnt work

    Give him a medal :) awesome and neat.

    @fejesjoco : seems that the big renders went 404 on your site

    Yes, my site is struggling with the huge traffic, the video is going viral. I uploaded the big images to allrgb.com and my google drive (links can be found on my blog). I will re-upload them later when the traffic gets lower.

    OK guys I fixed the links to point to my Google drive. I also added my fourth 4Kx4K render. It's similar to the 2nd, but look more closely!

    Amazing. Well done. + 1

    This really calls for a GPU implementation... It could work significantly faster :)

    I got some tips here and elsewhere to submit my design to Threadless. So I did and I am waiting for approval. As it turns out, it's not as easy as submitting a rendered PNG, you have to make a complete design, survive several rounds of approval, and then get community votes. Of course I will send you a link when it's time to vote, but I doubt I have any chance next to those real pro designers there. Can anybody find a designer who can actually do something with these images? And not just mine of course, there are many awesome answers here.

    The program is now featured on newscientist.com! I just want to thank you all guys, especially @Zom-B, for starting this thing and voting and giving ideas. It's also your success, codegolf's success, all geeks success :)

    by the way: somehow those weird black "canyons" are generated only after the change; posting a comparison of a 8kx2k render in old and new algorithm in the next few days when it finished rendering

    How awesome. Congratulations again, @fejesjoco

    The allRGB website is now having capacity problems, ROFL

    Some of you mentioned you would like a print. You can now get it here, I will add some more later (promo link with free shipping): http://society6.com/fejesjoco?promo=4552f3

    @MarkJeronimus They got CodeGolfed! It's great to see so many submissions from this thread on there!

    @fejesjoco Got me an extra large one. Man you're really working the fame here; nice job! :D I'll keep an eye out for t-shirt printers too, but short-run high-quality photorealistic prints are kind of a specialty job (either the shop needs special equipment, or a screen printer has to be skilled with process prints).

    You are in news !! http://www.newscientist.com/article/dn25167-computer-paints-rainbow-smoke-with-17-million-colours.html#.UxjKznYgp2N. I created an account in this site just to tell you this :) I had seen this answer in this site couple of days ago and surprized to see it in news !!

    by the way finished a 8kx2k render with the program out of the post aka the unoptimized version; took me 6 days to finish.... (wanted to see the differences added by the optimisations :)

    You also made Gizmodo! I don't think I've ever seen the results of an of the CodeGolf challenges get this kinda of feeback. Really really really well done.

    @fejesjoco Print arrived today; it's gorgeous -- society6 did a beautiful, high-quality job. Do you get decent kick-backs for sales with them?

    In case anyone's still reading, here's an app for Android that I'm working on: https://play.google.com/store/apps/details?id=name.joco.rainbowsmoke.demo

    Whoa, this is great!

    I have no words, it's just art.... Well done!

    The output from this looks scarily similar to an image-generation program I've been working on for a while: https://github.com/g-rocket/Starburst/ (well, some of them do). For example, see this: https://dl.dropboxusercontent.com/u/29197095/example.png (generated with properties set to [-.2,-.2,-.2,0,0,2] and seed properties set to [3,1,0,3]).

    These look like beautiful rainbow coral reefs.

    For anyone who is having trouble deciding on a `NUMCOLORS` to use when they change the dimensions.. use `NUMCOLORS = (int)Math.Ceiling(Math.Pow(HEIGHT* WIDTH, (1.0 / 3.0)));`

    After seeing this, I wrote a script to generate random images. They happen to look very similar to these.

    About to make this into an iOS app so people can generate their own images. :D

    Umm the huge render links are dead.

    This is beautiful.

    The last 4 links point to a Google Drive location which no longer exists.

    @fejesjoco it would appear that the source code hosted on google code is not available? I am getting an `401: Anonymous users does not have storage.objects.get access to object google-code-archive/v2/code.google.com/joco-tools/project.json.` or `The project joco-tools was not found.` error every time i attempt to access it :( would it be possible to share the source code via GitHub or otherwise?

    What happened to the alternative code where the previews on the website got posted after the initial version?

    @TaylorScott https://nplusc.de/rgbgen.zip Had the source still floating around in a corner of my harddisks

    20K CPU-hours in on a Ryzen processor on rendering one of those corals. 50% according to output but my estimate is 30%. Rendering it with 10K steps

    I immediately thought this was a pretty neat idea when I saw it a couple years ago, but was kind of shocked at how slow it is. I have since implemented it in Python (renders in an hour or two), Java (renders in about 2-3 minutes) and most recently C++ (renders in about 20-25 seconds), all single threaded (w/ fully shuffled colors, the fastest). Most variants use CIELAB colorspace to look extra good; strict color orderings (non-shuffled colors, and similar) produce extremely wild outputs, sometimes deterministically. The variety is endless; I could be coaxed into posting examples and a repo.

    @Mumbleskates make repo please!

    @Mumbleskates thanks

    @Mumbleskates Just FYI, my latest version runs in a couple of seconds. That's how it can also run in the Android app.

    @fejesjoco Nice. I'm assuming the approach is similar, if that's the case; what size is it rendering? The only reason the one I made takes so long (30-40 minutes for some rendering modes, full 24 bit) is because it seeks exact best answers. Introducing stochastic inaccuracy could greatly speed it up, but I've found that for some of the coolest patterns this actually makes the end result less interesting.

    The joco.name and source code links appear to be dead. Can these be updated?

    rainbowsmoke.hu still works

License under CC-BY-SA with attribution


Content dated before 6/26/2020 9:53 AM