Images with all colors

433

367

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

Mark Jeronimus

Posted 2014-02-25T21:16:53.557

Reputation: 6 451

I might actually work on making this an iOS app. This actually looks really cool. – DDPWNAGE – 2015-07-18T03:03:30.807

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. – Sdz – 2017-07-03T08:26:34.053

Why I saw creating as cheating? – l4m2 – 2018-03-21T17:59:53.553

8When 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? – Peter Taylor – 2014-02-25T22:02:07.100

1It 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. – Mark Jeronimus – 2014-02-26T06:42:22.010

small tip for verifying the output: sort the pixel array and check that all values are only 1 in difference as integer – masterX244 – 2014-02-26T21:32:29.100

8I actually find your trivial permutation example to be quite pleasing to the eye. – Jason C – 2014-02-27T00:48:00.803

2@Zom-B Man, I freakin' love this post. Thanks! – Jason C – 2014-02-27T17:26:10.567

7Beautiful results/answers! – EthanB – 2014-02-28T03:37:07.000

1

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

– Peter Taylor – 2014-03-28T10:00:50.750

Answers

535

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)

fejesjoco

Posted 2014-02-25T21:16:53.557

Reputation: 5 174

After seeing this, I wrote a script to generate random images. They happen to look very similar to these. – KSFT – 2015-01-01T16:05:16.533

2Sorry, the file you have requested does not exist. :( ... the horrors of off-site hosting. – Martin Ender – 2015-02-17T20:47:32.680

About to make this into an iOS app so people can generate their own images. :D – DDPWNAGE – 2015-07-18T06:29:30.837

5Umm the huge render links are dead. – starbeamrainbowlabs – 2016-03-17T19:48:52.883

This is beautiful. – Pete Arden – 2016-10-27T10:10:32.043

3The last 4 links point to a Google Drive location which no longer exists. – Mast – 2016-11-02T10:26:01.483

@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? – Taylor Scott – 2017-06-21T16:44:25.417

What happened to the alternative code where the previews on the website got posted after the initial version? – masterX244 – 2018-02-26T18:00:47.500

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

– masterX244 – 2018-03-03T12:49:22.037

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 – masterX244 – 2018-07-14T18:03:34.137

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 – 2019-02-21T08:09:39.887

@Mumbleskates make repo please! – Benjamin Urquhart – 2019-03-18T19:00:12.657

1

@BenjaminUrquhart https://github.com/mumbleskates/color-deposit-cc

– Mumbleskates – 2019-03-19T18:00:29.340

@Mumbleskates thanks – Benjamin Urquhart – 2019-03-19T18:23:40.673

@Mumbleskates Just FYI, my latest version runs in a couple of seconds. That's how it can also run in the Android app. – fejesjoco – 2019-03-19T22:24:28.750

@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. – Mumbleskates – 2019-03-19T23:16:22.433

25Now this is cool! – Jaa-c – 2014-02-27T15:41:08.507

5Very nice :-D Now make some bigger ones! – r3mainer – 2014-02-27T16:03:12.010

@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 – 2014-02-27T16:06:42.363

@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. – Jason C – 2014-02-27T17:14:29.470

20You're a true artist! :) – A.L – 2014-02-27T17:55:59.247

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 :). – fejesjoco – 2014-02-27T21:00:49.940

Keep a list with all the endpoints, just like a backtracking fill algorithm – Mark Jeronimus – 2014-02-27T22:24:12.163

1I smell a winner! The animations are beautiful and the 512x512 really took it up a notch. – Jason C – 2014-02-27T23:03:28.000

10How much for a print? – primo – 2014-02-28T06:31:40.407

^ I want one too! – Jason C – 2014-02-28T07:30:35.853

16I'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? – fejesjoco – 2014-02-28T08:41:57.060

whats the code for the last one? wondering about that – masterX244 – 2014-02-28T16:01:43.737

@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 – 2014-02-28T16:14:05.953

@fejesjoco what parameters? maybe you could post the parameters below for usage – masterX244 – 2014-02-28T16:16:29.137

experimenting with some parameters atm to find one suitable for panorama print on 8kx2k size – masterX244 – 2014-02-28T19:28:42.457

I'm rewriting the whole thing to be faster because big renders take too much time. I'm also adding more parameterization options. – fejesjoco – 2014-02-28T19:30:59.650

goin to abuse mah webserver as renderer for the panorama big one... – masterX244 – 2014-02-28T20:12:19.470

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 – 2014-02-28T20:36:16.517

@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. – fejesjoco – 2014-02-28T20:50:33.623

Ooooh, final version. I like the sound of that. Will do. – Geobits – 2014-02-28T21:03:18.377

rendering atm; 0,05 % on my lowend VPS and 1 % at my comp for a 8192x2048 in average mode with the hue sorting – masterX244 – 2014-02-28T23:51:33.087

@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.

– Jason C – 2014-03-01T00:08:14.887

@JasonC 4% atm on my render; cpu is nicely col at around 40 °C – masterX244 – 2014-03-01T09:46:06.110

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. – fejesjoco – 2014-03-01T10:45:20.920

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 – masterX244 – 2014-03-01T11:14:08.683

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. – fejesjoco – 2014-03-01T12:38:49.407

could you post the current versions, too? – masterX244 – 2014-03-01T12:44:20.197

another issue: program doesnt support larger amounts of checkpoints (tiied 50 and above: just getting a error) @fejesjoco – masterX244 – 2014-03-01T13:12:31.767

grrr; error... meant 500 checkpoints – masterX244 – 2014-03-01T13:42:35.017

1

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

– Bob – 2014-03-01T15:11:33.630

@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 – 2014-03-01T16:56:23.063

2@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. – fejesjoco – 2014-03-01T17:56:13.687

Hmm... was this inspired by diffusion-limited aggregation? – Oberon – 2014-03-02T00:14:48.827

It is readyyyyyyyyyyyyyyyyyy! See my last edit! – fejesjoco – 2014-03-02T13:52:02.117

@Oberon I didn't know about it before. It does look like a similar concept. – fejesjoco – 2014-03-02T13:52:34.977

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 – masterX244 – 2014-03-02T14:38:08.747

The joco.name and source code links appear to be dead. Can these be updated? – Patrick Roberts – 2019-11-04T02:20:19.843

btw @fejesjoco still having the seeds used for the video? – masterX244 – 2014-03-02T14:44:21.303

@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. – fejesjoco – 2014-03-02T14:45:42.557

yeah; immediately used release mode :) time to warm up my cpu, too :P – masterX244 – 2014-03-02T14:51:51.050

The program now scales very well to multiple CPU's. I'm running the last 4K render on 8 cores now. – fejesjoco – 2014-03-02T14:53:35.523

rgb_2048_2.png is absolutely amazing! – Mark Jeronimus – 2014-03-02T19:18:01.867

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. – fejesjoco – 2014-03-02T21:18:10.000

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

– masterX244 – 2014-03-02T22:17:48.963

All your 16M images have exactly 16,703,545 colors for some reason. That's 73671 short of a win. – Mark Jeronimus – 2014-03-02T22:45:23.780

can ya tell which ones got nulled? @Zom-B – masterX244 – 2014-03-02T22:53:29.477

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 – Mark Jeronimus – 2014-03-02T23:38:52.750

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) – masterX244 – 2014-03-02T23:41:34.290

Nice video! The music kinda cracks me up; it's so YouTube! – Jason C – 2014-03-03T00:19:35.833

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 :) – masterX244 – 2014-03-03T00:33:26.867

@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? – fejesjoco – 2014-03-03T06:01:29.793

@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 :). – fejesjoco – 2014-03-03T06:03:06.227

@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. – fejesjoco – 2014-03-03T06:08:43.803

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. – Mark Jeronimus – 2014-03-03T06:28:52.967

1Ok 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. – fejesjoco – 2014-03-03T06:57:32.530

2@masterX244 can I ask what you will do with these images? My wife wants to hang one on our wall :) – fejesjoco – 2014-03-03T06:58:06.223

1

Please t-shirt with http://joco.name/wp-content/uploads/2014/03/rgb_2048_2.png !

– Antonio Ragagnin – 2014-03-03T08:16:03.977

@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 – masterX244 – 2014-03-03T09:58:07.970

@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 – masterX244 – 2014-03-03T17:38:58.720

rainbowsmoke.hu still works – fejesjoco – 2019-12-03T15:51:10.560

Give him a medal :) awesome and neat. – Rajesh CP – 2014-03-04T08:17:35.020

1@fejesjoco : seems that the big renders went 404 on your site – masterX244 – 2014-03-04T09:00:29.190

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. – fejesjoco – 2014-03-04T12:06:30.487

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! – fejesjoco – 2014-03-04T16:15:32.883

Amazing. Well done. + 1 – Sam Leach – 2014-03-04T16:51:43.863

2This really calls for a GPU implementation... It could work significantly faster :) – Jaa-c – 2014-03-04T21:13:38.457

1I 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. – fejesjoco – 2014-03-04T21:15:56.557

1The 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 :) – fejesjoco – 2014-03-05T19:52:04.073

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 – masterX244 – 2014-03-05T20:12:39.190

How awesome. Congratulations again, @fejesjoco – Mark Jeronimus – 2014-03-05T21:34:13.540

2The allRGB website is now having capacity problems, ROFL – Mark Jeronimus – 2014-03-05T21:36:19.343

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

– fejesjoco – 2014-03-05T22:42:39.213

1@MarkJeronimus They got CodeGolfed! It's great to see so many submissions from this thread on there! – Jason C – 2014-03-06T14:51:48.957

1@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). – Jason C – 2014-03-06T15:07:09.840

5

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 !!

– PermanentGuest – 2014-03-06T19:31:26.633

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 :) – masterX244 – 2014-03-07T11:48:05.030

4You 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. – lochok – 2014-03-07T23:26:25.183

@fejesjoco Print arrived today; it's gorgeous -- society6 did a beautiful, high-quality job. Do you get decent kick-backs for sales with them? – Jason C – 2014-03-13T15:17:38.107

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

– fejesjoco – 2014-03-22T19:14:28.807

Whoa, this is great! – Null Reference – 2014-03-27T02:50:21.810

I have no words, it's just art.... Well done! – Superdrac – 2014-06-06T18:04:48.060

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]).

– Gavin S. Yancey – 2014-10-13T21:59:30.823

I put my own twist on your algorithm here: http://whoiskylefinn.com/colors.html#paint=3,dirs=0xaa,picky=901,seed=0.01

– kylefinn – 2014-11-25T05:36:35.500

These look like beautiful rainbow coral reefs. – Joe Z. – 2014-12-08T23:14:32.847

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))); – BenVlodgi – 2014-12-11T19:28:44.260

250

Processing

Update! 4096x4096 images!

I've merged my second post into this one by combining the two programs together.

A full collection of selected images can be found here, on Dropbox. (Note: DropBox can't generate previews for the 4096x4096 images; just click them then click "Download").

If you only look at one look at this one (tileable)! Here it is scaled down (and many more below), original 2048x1024:

enter image description here

This program works by walking paths from randomly selected points in the color cube, then drawing them into randomly selected paths in the image. There are a lot of possibilities. Configurable options are:

  • Maximum length of color cube path.
  • Maximum step to take through color cube (larger values cause larger variance but minimize the number of small paths towards the end when things get tight).
  • Tiling the image.
  • There are currently two image path modes:
    • Mode 1 (the mode of this original post): Finds a block of unused pixels in the image and renders to that block. Blocks can be either randomly located, or ordered from left to right.
    • Mode 2 (the mode of my second post that I merged into this one): Picks a random start point in the image and walks along a path through unused pixels; can walk around used pixels. Options for this mode:
      • Set of directions to walk in (orthogonal, diagonal, or both).
      • Whether or not to change the direction (currently clockwise but code is flexible) after each step, or to only change direction upon encountering an occupied pixel..
      • Option to shuffle order of direction changes (instead of clockwise).

It works for all sizes up to 4096x4096.

The complete Processing sketch can be found here: Tracer.zip

I've pasted all the files in the same code block below just to save space (even all in one file, it is still a valid sketch). If you want to use one of the presets, change the index in the gPreset assignment. If you run this in Processing you can press r while it is running to generate a new image.

  • Update 1: Optimized code to track first unused color/pixel and not search over known-used pixels; reduced 2048x1024 generation time from 10-30 minutes down to about 15 seconds, and 4096x4096 from 1-3 hours to about 1 minute. Drop box source and source below updated.
  • Update 2: Fixed bug that was preventing 4096x4096 images from being generated.
final int BITS = 5; // Set to 5, 6, 7, or 8!

// Preset (String name, int colorBits, int maxCubePath, int maxCubeStep, int imageMode, int imageOpts)
final Preset[] PRESETS = new Preset[] {
  // 0
  new Preset("flowers",      BITS, 8*32*32, 2, ImageRect.MODE2, ImageRect.ALL_CW | ImageRect.CHANGE_DIRS),
  new Preset("diamonds",     BITS, 2*32*32, 2, ImageRect.MODE2, ImageRect.ORTHO_CW | ImageRect.CHANGE_DIRS),
  new Preset("diamondtile",  BITS, 2*32*32, 2, ImageRect.MODE2, ImageRect.ORTHO_CW | ImageRect.CHANGE_DIRS | ImageRect.WRAP),
  new Preset("shards",       BITS, 2*32*32, 2, ImageRect.MODE2, ImageRect.ALL_CW | ImageRect.CHANGE_DIRS | ImageRect.SHUFFLE_DIRS),
  new Preset("bigdiamonds",  BITS,  100000, 6, ImageRect.MODE2, ImageRect.ORTHO_CW | ImageRect.CHANGE_DIRS),
  // 5
  new Preset("bigtile",      BITS,  100000, 6, ImageRect.MODE2, ImageRect.ORTHO_CW | ImageRect.CHANGE_DIRS | ImageRect.WRAP),
  new Preset("boxes",        BITS,   32*32, 2, ImageRect.MODE2, ImageRect.ORTHO_CW),
  new Preset("giftwrap",     BITS,   32*32, 2, ImageRect.MODE2, ImageRect.ORTHO_CW | ImageRect.WRAP),
  new Preset("diagover",     BITS,   32*32, 2, ImageRect.MODE2, ImageRect.DIAG_CW),
  new Preset("boxfade",      BITS,   32*32, 2, ImageRect.MODE2, ImageRect.DIAG_CW | ImageRect.CHANGE_DIRS),
  // 10
  new Preset("randlimit",    BITS,     512, 2, ImageRect.MODE1, ImageRect.RANDOM_BLOCKS),
  new Preset("ordlimit",     BITS,      64, 2, ImageRect.MODE1, 0),
  new Preset("randtile",     BITS,    2048, 3, ImageRect.MODE1, ImageRect.RANDOM_BLOCKS | ImageRect.WRAP),
  new Preset("randnolimit",  BITS, 1000000, 1, ImageRect.MODE1, ImageRect.RANDOM_BLOCKS),
  new Preset("ordnolimit",   BITS, 1000000, 1, ImageRect.MODE1, 0)
};


PGraphics gFrameBuffer;
Preset gPreset = PRESETS[2];

void generate () {
  ColorCube cube = gPreset.createCube();
  ImageRect image = gPreset.createImage();
  gFrameBuffer = createGraphics(gPreset.getWidth(), gPreset.getHeight(), JAVA2D);
  gFrameBuffer.noSmooth();
  gFrameBuffer.beginDraw();
  while (!cube.isExhausted())
    image.drawPath(cube.nextPath(), gFrameBuffer);
  gFrameBuffer.endDraw();
  if (gPreset.getName() != null)
    gFrameBuffer.save(gPreset.getName() + "_" + gPreset.getCubeSize() + ".png");
  //image.verifyExhausted();
  //cube.verifyExhausted();
}

void setup () {
  size(gPreset.getDisplayWidth(), gPreset.getDisplayHeight());
  noSmooth();
  generate();
}

void keyPressed () {
  if (key == 'r' || key == 'R')
    generate();
}

boolean autogen = false;
int autop = 0;
int autob = 5;

void draw () {
  if (autogen) {
    gPreset = new Preset(PRESETS[autop], autob);
    generate();
    if ((++ autop) >= PRESETS.length) {
      autop = 0;
      if ((++ autob) > 8)
        autogen = false;
    }
  }
  if (gPreset.isWrapped()) {
    int hw = width/2;
    int hh = height/2;
    image(gFrameBuffer, 0, 0, hw, hh);
    image(gFrameBuffer, hw, 0, hw, hh);
    image(gFrameBuffer, 0, hh, hw, hh);
    image(gFrameBuffer, hw, hh, hw, hh);
  } else {
    image(gFrameBuffer, 0, 0, width, height);
  }
}

static class ColorStep {
  final int r, g, b;
  ColorStep (int rr, int gg, int bb) { r=rr; g=gg; b=bb; }
}

class ColorCube {

  final boolean[] used;
  final int size; 
  final int maxPathLength;
  final ArrayList<ColorStep> allowedSteps = new ArrayList<ColorStep>();

  int remaining;
  int pathr = -1, pathg, pathb;
  int firstUnused = 0;

  ColorCube (int size, int maxPathLength, int maxStep) {
    this.used = new boolean[size*size*size];
    this.remaining = size * size * size;
    this.size = size;
    this.maxPathLength = maxPathLength;
    for (int r = -maxStep; r <= maxStep; ++ r)
      for (int g = -maxStep; g <= maxStep; ++ g)
        for (int b = -maxStep; b <= maxStep; ++ b)
          if (r != 0 && g != 0 && b != 0)
            allowedSteps.add(new ColorStep(r, g, b));
  }

  boolean isExhausted () {
    println(remaining);
    return remaining <= 0;
  }

  boolean isUsed (int r, int g, int b) {
    if (r < 0 || r >= size || g < 0 || g >= size || b < 0 || b >= size)
      return true;
    else
      return used[(r*size+g)*size+b];
  }

  void setUsed (int r, int g, int b) {
    used[(r*size+g)*size+b] = true;
  }

  int nextColor () {

    if (pathr == -1) { // Need to start a new path.

      // Limit to 50 attempts at random picks; things get tight near end.
      for (int n = 0; n < 50 && pathr == -1; ++ n) {
        int r = (int)random(size);
        int g = (int)random(size);
        int b = (int)random(size);
        if (!isUsed(r, g, b)) {
          pathr = r;
          pathg = g;
          pathb = b;
        }
      }
      // If we didn't find one randomly, just search for one.
      if (pathr == -1) {
        final int sizesq = size*size;
        final int sizemask = size - 1;
        for (int rgb = firstUnused; rgb < size*size*size; ++ rgb) {
          pathr = (rgb/sizesq)&sizemask;//(rgb >> 10) & 31;
          pathg = (rgb/size)&sizemask;//(rgb >> 5) & 31;
          pathb = rgb&sizemask;//rgb & 31;
          if (!used[rgb]) {
            firstUnused = rgb;
            break;
          }
        }
      }

      assert(pathr != -1);

    } else { // Continue moving on existing path.

      // Find valid next path steps.
      ArrayList<ColorStep> possibleSteps = new ArrayList<ColorStep>();
      for (ColorStep step:allowedSteps)
        if (!isUsed(pathr+step.r, pathg+step.g, pathb+step.b))
          possibleSteps.add(step);

      // If there are none end this path.
      if (possibleSteps.isEmpty()) {
        pathr = -1;
        return -1;
      }

      // Otherwise pick a random step and move there.
      ColorStep s = possibleSteps.get((int)random(possibleSteps.size()));
      pathr += s.r;
      pathg += s.g;
      pathb += s.b;

    }

    setUsed(pathr, pathg, pathb);  
    return 0x00FFFFFF & color(pathr * (256/size), pathg * (256/size), pathb * (256/size));

  } 

  ArrayList<Integer> nextPath () {

    ArrayList<Integer> path = new ArrayList<Integer>(); 
    int rgb;

    while ((rgb = nextColor()) != -1) {
      path.add(0xFF000000 | rgb);
      if (path.size() >= maxPathLength) {
        pathr = -1;
        break;
      }
    }

    remaining -= path.size();

    //assert(!path.isEmpty());
    if (path.isEmpty()) {
      println("ERROR: empty path.");
      verifyExhausted();
    }
    return path;

  }

  void verifyExhausted () {
    final int sizesq = size*size;
    final int sizemask = size - 1;
    for (int rgb = 0; rgb < size*size*size; ++ rgb) {
      if (!used[rgb]) {
        int r = (rgb/sizesq)&sizemask;
        int g = (rgb/size)&sizemask;
        int b = rgb&sizemask;
        println("UNUSED COLOR: " + r + " " + g + " " + b);
      }
    }
    if (remaining != 0)
      println("REMAINING COLOR COUNT IS OFF: " + remaining);
  }

}


static class ImageStep {
  final int x;
  final int y;
  ImageStep (int xx, int yy) { x=xx; y=yy; }
}

static int nmod (int a, int b) {
  return (a % b + b) % b;
}

class ImageRect {

  // for mode 1:
  //   one of ORTHO_CW, DIAG_CW, ALL_CW
  //   or'd with flags CHANGE_DIRS
  static final int ORTHO_CW = 0;
  static final int DIAG_CW = 1;
  static final int ALL_CW = 2;
  static final int DIR_MASK = 0x03;
  static final int CHANGE_DIRS = (1<<5);
  static final int SHUFFLE_DIRS = (1<<6);

  // for mode 2:
  static final int RANDOM_BLOCKS = (1<<0);

  // for both modes:
  static final int WRAP = (1<<16);

  static final int MODE1 = 0;
  static final int MODE2 = 1;

  final boolean[] used;
  final int width;
  final int height;
  final boolean changeDir;
  final int drawMode;
  final boolean randomBlocks;
  final boolean wrap;
  final ArrayList<ImageStep> allowedSteps = new ArrayList<ImageStep>();

  // X/Y are tracked instead of index to preserve original unoptimized mode 1 behavior
  // which does column-major searches instead of row-major.
  int firstUnusedX = 0;
  int firstUnusedY = 0;

  ImageRect (int width, int height, int drawMode, int drawOpts) {
    boolean myRandomBlocks = false, myChangeDir = false;
    this.used = new boolean[width*height];
    this.width = width;
    this.height = height;
    this.drawMode = drawMode;
    this.wrap = (drawOpts & WRAP) != 0;
    if (drawMode == MODE1) {
      myRandomBlocks = (drawOpts & RANDOM_BLOCKS) != 0;
    } else if (drawMode == MODE2) {
      myChangeDir = (drawOpts & CHANGE_DIRS) != 0;
      switch (drawOpts & DIR_MASK) {
      case ORTHO_CW:
        allowedSteps.add(new ImageStep(1, 0));
        allowedSteps.add(new ImageStep(0, -1));
        allowedSteps.add(new ImageStep(-1, 0));
        allowedSteps.add(new ImageStep(0, 1));
        break;
      case DIAG_CW:
        allowedSteps.add(new ImageStep(1, -1));
        allowedSteps.add(new ImageStep(-1, -1));
        allowedSteps.add(new ImageStep(-1, 1));
        allowedSteps.add(new ImageStep(1, 1));
        break;
      case ALL_CW:
        allowedSteps.add(new ImageStep(1, 0));
        allowedSteps.add(new ImageStep(1, -1));
        allowedSteps.add(new ImageStep(0, -1));
        allowedSteps.add(new ImageStep(-1, -1));
        allowedSteps.add(new ImageStep(-1, 0));
        allowedSteps.add(new ImageStep(-1, 1));
        allowedSteps.add(new ImageStep(0, 1));
        allowedSteps.add(new ImageStep(1, 1));
        break;
      }
      if ((drawOpts & SHUFFLE_DIRS) != 0)
        java.util.Collections.shuffle(allowedSteps);
    }
    this.randomBlocks = myRandomBlocks;
    this.changeDir = myChangeDir;
  }

  boolean isUsed (int x, int y) {
    if (wrap) {
      x = nmod(x, width);
      y = nmod(y, height);
    }
    if (x < 0 || x >= width || y < 0 || y >= height)
      return true;
    else
      return used[y*width+x];
  }

  boolean isUsed (int x, int y, ImageStep d) {
    return isUsed(x + d.x, y + d.y);
  }

  void setUsed (int x, int y) {
    if (wrap) {
      x = nmod(x, width);
      y = nmod(y, height);
    }
    used[y*width+x] = true;
  }

  boolean isBlockFree (int x, int y, int w, int h) {
    for (int yy = y; yy < y + h; ++ yy)
      for (int xx = x; xx < x + w; ++ xx)
        if (isUsed(xx, yy))
          return false;
    return true;
  }

  void drawPath (ArrayList<Integer> path, PGraphics buffer) {
    if (drawMode == MODE1)
      drawPath1(path, buffer);
    else if (drawMode == MODE2)
      drawPath2(path, buffer);
  }

  void drawPath1 (ArrayList<Integer> path, PGraphics buffer) {

    int w = (int)(sqrt(path.size()) + 0.5);
    if (w < 1) w = 1; else if (w > width) w = width;
    int h = (path.size() + w - 1) / w; 
    int x = -1, y = -1;

    int woff = wrap ? 0 : (1 - w);
    int hoff = wrap ? 0 : (1 - h);

    // Try up to 50 times to find a random location for block.
    if (randomBlocks) {
      for (int n = 0; n < 50 && x == -1; ++ n) {
        int xx = (int)random(width + woff);
        int yy = (int)random(height + hoff);
        if (isBlockFree(xx, yy, w, h)) {
          x = xx;
          y = yy;
        }
      }
    }

    // If random choice failed just search for one.
    int starty = firstUnusedY;
    for (int xx = firstUnusedX; xx < width + woff && x == -1; ++ xx) {
      for (int yy = starty; yy < height + hoff && x == -1; ++ yy) {
        if (isBlockFree(xx, yy, w, h)) {
          firstUnusedX = x = xx;
          firstUnusedY = y = yy;
        }  
      }
      starty = 0;
    }

    if (x != -1) {
      for (int xx = x, pathn = 0; xx < x + w && pathn < path.size(); ++ xx)
        for (int yy = y; yy < y + h && pathn < path.size(); ++ yy, ++ pathn) {
          buffer.set(nmod(xx, width), nmod(yy, height), path.get(pathn));
          setUsed(xx, yy);
        }
    } else {
      for (int yy = 0, pathn = 0; yy < height && pathn < path.size(); ++ yy)
        for (int xx = 0; xx < width && pathn < path.size(); ++ xx)
          if (!isUsed(xx, yy)) {
            buffer.set(nmod(xx, width), nmod(yy, height), path.get(pathn));
            setUsed(xx, yy);
            ++ pathn;
          }
    }

  }

  void drawPath2 (ArrayList<Integer> path, PGraphics buffer) {

    int pathn = 0;

    while (pathn < path.size()) {

      int x = -1, y = -1;

      // pick a random location in the image (try up to 100 times before falling back on search)

      for (int n = 0; n < 100 && x == -1; ++ n) {
        int xx = (int)random(width);
        int yy = (int)random(height);
        if (!isUsed(xx, yy)) {
          x = xx;
          y = yy;
        }
      }  

      // original:
      //for (int yy = 0; yy < height && x == -1; ++ yy)
      //  for (int xx = 0; xx < width && x == -1; ++ xx)
      //    if (!isUsed(xx, yy)) {
      //      x = xx;
      //      y = yy;
      //    }
      // optimized:
      if (x == -1) {
        for (int n = firstUnusedY * width + firstUnusedX; n < used.length; ++ n) {
          if (!used[n]) {
            firstUnusedX = x = (n % width);
            firstUnusedY = y = (n / width);
            break;
          }     
        }
      }

      // start drawing

      int dir = 0;

      while (pathn < path.size()) {

        buffer.set(nmod(x, width), nmod(y, height), path.get(pathn ++));
        setUsed(x, y);

        int diro;
        for (diro = 0; diro < allowedSteps.size(); ++ diro) {
          int diri = (dir + diro) % allowedSteps.size();
          ImageStep step = allowedSteps.get(diri);
          if (!isUsed(x, y, step)) {
            dir = diri;
            x += step.x;
            y += step.y;
            break;
          }
        }

        if (diro == allowedSteps.size())
          break;

        if (changeDir) 
          ++ dir;

      }    

    }

  }

  void verifyExhausted () {
    for (int n = 0; n < used.length; ++ n)
      if (!used[n])
        println("UNUSED IMAGE PIXEL: " + (n%width) + " " + (n/width));
  }

}


class Preset {

  final String name;
  final int cubeSize;
  final int maxCubePath;
  final int maxCubeStep;
  final int imageWidth;
  final int imageHeight;
  final int imageMode;
  final int imageOpts;
  final int displayScale;

  Preset (Preset p, int colorBits) {
    this(p.name, colorBits, p.maxCubePath, p.maxCubeStep, p.imageMode, p.imageOpts);
  }

  Preset (String name, int colorBits, int maxCubePath, int maxCubeStep, int imageMode, int imageOpts) {
    final int csize[] = new int[]{ 32, 64, 128, 256 };
    final int iwidth[] = new int[]{ 256, 512, 2048, 4096 };
    final int iheight[] = new int[]{ 128, 512, 1024, 4096 };
    final int dscale[] = new int[]{ 2, 1, 1, 1 };
    this.name = name; 
    this.cubeSize = csize[colorBits - 5];
    this.maxCubePath = maxCubePath;
    this.maxCubeStep = maxCubeStep;
    this.imageWidth = iwidth[colorBits - 5];
    this.imageHeight = iheight[colorBits - 5];
    this.imageMode = imageMode;
    this.imageOpts = imageOpts;
    this.displayScale = dscale[colorBits - 5];
  }

  ColorCube createCube () {
    return new ColorCube(cubeSize, maxCubePath, maxCubeStep);
  }

  ImageRect createImage () {
    return new ImageRect(imageWidth, imageHeight, imageMode, imageOpts);
  }

  int getWidth () {
    return imageWidth;
  }

  int getHeight () {
    return imageHeight;
  }

  int getDisplayWidth () {
    return imageWidth * displayScale * (isWrapped() ? 2 : 1);
  }

  int getDisplayHeight () {
    return imageHeight * displayScale * (isWrapped() ? 2 : 1);
  }

  String getName () {
    return name;
  }

  int getCubeSize () {
    return cubeSize;
  }

  boolean isWrapped () {
    return (imageOpts & ImageRect.WRAP) != 0;
  }

}

Here is a full set of 256x128 images that I like:

Mode 1:

My favorite from original set (max_path_length=512, path_step=2, random, displayed 2x, link 256x128):

enter image description here

Others (left two ordered, right two random, top two path length limited, bottom two unlimitted):

ordlimit randlimit ordnolimit randnolimit

This one can be tiled:

randtile

Mode 2:

diamonds flowers boxfade diagover bigdiamonds boxes2 shards

These ones can be tiled:

bigtile diamondtile giftwrap

512x512 selections:

Tileable diamonds, my favorite from mode 2; you can see in this one how the paths walk around existing objects:

enter image description here

Larger path step and max path length, tileable:

enter image description here

Random mode 1, tileable:

enter image description here

More selections:

enter image description here enter image description here enter image description here

All of the 512x512 renderings can be found in the dropbox folder (*_64.png).

2048x1024 and 4096x4096:

These are too large to embed and all the image hosts I found drop them down to 1600x1200. I'm currently rendering a set of 4096x4096 images so more will be available soon. Instead of including all the links here, just go check them out in the dropbox folder (*_128.png and *_256.png, note: the 4096x4096 ones are too big for the dropbox previewer, just click "download"). Here are some of my favorites, though:

2048x1024 big tileable diamonds (same one I linked to at start of this post)

2048x1024 diamonds (I love this one!), scaled down:

enter image description here

4096x4096 big tileable diamonds (Finally! Click 'download' in Dropbox link; it's too large for their previewer), scaled way down:

4096x4096 big tileable diamonds

4096x4096 random mode 1: enter image description here

4096x4096 another cool one

Update: The 2048x1024 preset image set is finished and in the dropbox. The 4096x4096 set should be done within the hour.

There's tons of good ones, I'm having a really hard time picking which ones to post, so please check out the folder link!

Jason C

Posted 2014-02-25T21:16:53.557

Reputation: 6 253

The first image is my favorite; it looks like things flying through the air with a ground and sky horizon behind it. Well, that and about 10 hits of acid. – Jason C – 2014-02-27T08:47:36.277

6It reminds me of close-up views of some minerals. – Morwenn – 2014-02-27T09:22:45.823

Awesome! That's the spirit I've been missing until now. – Mark Jeronimus – 2014-02-27T13:28:38.780

I've merged my second post into this one. – Jason C – 2014-02-28T02:22:35.440

3

Not part of the contest, but I thought this was kinda cool; I applied a big gaussian blur and auto contrast enhance to one of the random mode 1 pics in photoshop and it made kind of a nice desktop background-y sort of thing.

– Jason C – 2014-02-28T04:40:43.847

2whoa, these are cool pictures! – sevenseacat – 2014-02-28T06:51:58.873

1

^ http://i.stack.imgur.com/oHOvJ.png -> Tokyo, sideways

– Mark Jeronimus – 2014-02-28T07:50:02.183

@Zom-B Funny I was just thinking about rotating that one. – Jason C – 2014-02-28T08:12:21.667

2Reminds me of Gustav Klimt textures. – Kim – 2014-02-28T15:29:31.847

I'm having a really hard time generating 4096x4096 textures; for some reason ColorCube.nextPath() is returning an empty path for the very last pixel, but only in 4096x4096 mode. This shouldn't happen because the cube and the image have exactly the same cell count. I suspect a subtle integer overflow somewhere. I will update here when I can actually render the large size. The 2048x1024 set is complete, though. – Jason C – 2014-02-28T19:53:33.897

1@JasonC oucherz; stupid bug.... – masterX244 – 2014-02-28T20:11:15.153

Update: An easy optimization in the code reduced 2048x1024 generation times from 10-30 minutes down to about 15 seconds. I should be able to solve the large image bug now too. I've updated the source on Drop Box as well as above. Enjoy! – Jason C – 2014-02-28T20:31:48.833

Update: Bug fixed and it works with 4096x4096 images now; will post images soon. @masterX244: I forgot Processing's color(r,g,b) function returns 0xAARRGGBB with alpha set to 255. I used -1 to signal the end of a color path but for the color (255, 255, 255), the result was the same as -1 (it only failed for 4096x4096 because that's the only one with 255,255,255 in the palette). Kinda under the radar. It just so happened that more often than not, 255,255,255 was the last color picked. I solved it by masking out the alpha component before returning the rgb value. – Jason C – 2014-02-28T23:19:57.303

@Zom-B Finally got some 4096x4096 images up. Here's a tileable diamond one.

– Jason C – 2014-02-28T23:38:16.183

2

Did you know you can hotlink images in Dropbox? Just copy the download URL, remove the dl=1 and the token_hash=<something> part and make a link to your image like this: [![Alt text of my small preview image](https://i.stack.imgur.com/smallpreview.png)](https://dl.dropbox.com/linktoyourfullsiz‌​eimage.png). Another tip: you can compress your images (I get good results with TruePNG (Download)). I was able to save 28.1% of the file size on this image.

– user2428118 – 2014-03-03T14:40:10.503

@user2428118 Oh sweet! Thanks! I had tried hotlinking just with the "link" button but it didn't work so I figured it wasn't possible. I'll see if I can compress the images, too. ZIP and RAR weren't able to do much with them, so I didn't try anything else, I'll try TruePNG. – Jason C – 2014-03-03T16:13:55.643

1The Klimt-Gogh algorithm. – nevvermind – 2014-03-04T09:44:49.033

Some of them look like those magnified processor die photos. – fejesjoco – 2014-02-27T19:15:50.647

Try treating the canvas as a toroidal topology (borderless, tiling) – Mark Jeronimus – 2014-02-27T22:32:42.997

The 2048x1024 diamonds one is now my laptop's background image – Carter Pape – 2014-02-27T22:42:33.583

@CarterPape Yes!! Generating something that somebody else uses as a background is kinda the best compliment ever. Fist pump! :D Hopefully I'll have some 4096x4096 ones up soon; I just spent about 2 hours waiting for one to render only to have an assertion failure on the very last pixel. :( – Jason C – 2014-02-27T23:02:22.013

@Zom-B Done and done; check out the 4th image in this post. I have rewritten the code to be much cleaner and to combine both this post and my other post into the same program; so I am going to work on merging this into my other post then deleting this. Although, to be honest, fejesjoco is going to kick our butts no matter what I do, lol. – Jason C – 2014-02-28T00:33:42.967

221

Python w/ PIL

This is based on a Newtonian Fractal, specifically for z → z5 - 1. Because there are five roots, and thus five convergence points, the available color space is split into five regions, based on Hue. The individual points are sorted first by number of iterations required to reach their convergence point, and then by distance to that point, with earlier values being assigned a more luminous color.

Update: 4096x4096 big renders, hosted on allrgb.com.

Original (33.7 MB)

A close-up of the very center (actual size):

A different vantage point using these values:

xstart = 0
ystart = 0

xd = 1 / dim[0]
yd = 1 / dim[1]

Original (32.2 MB)

And another using these:

xstart = 0.5
ystart = 0.5

xd = 0.001 / dim[0]
yd = 0.001 / dim[1]

Original (27.2 MB)


Animation

By request, I've compiled a zoom animation.

Focal Point: (0.50051, -0.50051)
Zoom factor: 21/5

The focal point is a slightly odd value, because I didn't want to zoom in on a black dot. The zoom factor is chosen such that it doubles every 5 frames.

A 32x32 teaser:

A 256x256 version can be seen here:
http://www.pictureshack.org/images/66172_frac.gif (5.4MB)

There may be points that mathematically zoom in "onto themselves," which would allow for an infinite animation. If I can identify any, I'll add them here.


Source

from __future__ import division
from PIL import Image, ImageDraw
from cmath import phase
from sys import maxint

dim  = (4096, 4096)
bits = 8

def RGBtoHSV(R, G, B):
  R /= 255
  G /= 255
  B /= 255

  cmin = min(R, G, B)
  cmax = max(R, G, B)
  dmax = cmax - cmin

  V = cmax

  if dmax == 0:
    H = 0
    S = 0

  else:
    S = dmax/cmax

    dR = ((cmax - R)/6 + dmax/2)/dmax
    dG = ((cmax - G)/6 + dmax/2)/dmax
    dB = ((cmax - B)/6 + dmax/2)/dmax

    if   R == cmax: H = (dB - dG)%1
    elif G == cmax: H = (1/3 + dR - dB)%1
    elif B == cmax: H = (2/3 + dG - dR)%1

  return (H, S, V)

cmax = (1<<bits)-1
cfac = 255/cmax

img  = Image.new('RGB', dim)
draw = ImageDraw.Draw(img)

xstart = -2
ystart = -2

xd = 4 / dim[0]
yd = 4 / dim[1]

tol = 1e-12

a = [[], [], [], [], []]

for x in range(dim[0]):
  print x, "\r",
  for y in range(dim[1]):
    z = d = complex(xstart + x*xd, ystart + y*yd)
    c = 0
    l = 1
    while abs(l-z) > tol and abs(z) > tol:
      l = z
      z -= (z**5-1)/(5*z**4)
      c += 1
    if z == 0: c = maxint
    p = int(phase(z))

    a[p] += (c,abs(d-z), x, y),

for i in range(5):
  a[i].sort(reverse = False)

pnum = [len(a[i]) for i in range(5)]
ptot = dim[0]*dim[1]

bounds = []
lbound = 0
for i in range(4):
  nbound = lbound + pnum[i]/ptot
  bounds += nbound,
  lbound = nbound

t = [[], [], [], [], []]
for i in range(ptot-1, -1, -1):
  r = (i>>bits*2)*cfac
  g = (cmax&i>>bits)*cfac
  b = (cmax&i)*cfac
  (h, s, v) = RGBtoHSV(r, g, b)
  h = (h+0.1)%1
  if   h < bounds[0] and len(t[0]) < pnum[0]: p=0
  elif h < bounds[1] and len(t[1]) < pnum[1]: p=1
  elif h < bounds[2] and len(t[2]) < pnum[2]: p=2
  elif h < bounds[3] and len(t[3]) < pnum[3]: p=3
  else: p=4
  t[p] += (int(r), int(g), int(b)),

for i in range(5):
  t[i].sort(key = lambda c: c[0]*2126 + c[1]*7152 + c[2]*722, reverse = True)

r = [0, 0, 0, 0, 0]
for p in range(5):
  for c,d,x,y in a[p]:
    draw.point((x,y), t[p][r[p]])
    r[p] += 1

img.save("out.png")

primo

Posted 2014-02-25T21:16:53.557

Reputation: 30 891

2@primo I know I'm late, but I just wanted to say these images are spectacular. – Ashwin Gupta – 2016-01-20T15:21:16.773

@JasonC done... two and half years later. The render requires more than 4GB of RAM, which I didn't have at the time. – primo – 2016-10-13T15:48:25.853

1"Anything worth doing is worth overdoing" :D – Albert Renshaw – 2016-10-15T23:03:23.213

Your pictureshack.us link seems broken, can be fixed by linking to pictureshack.org.

– Jonathan Frech – 2018-02-24T00:24:24.157

@JonathanFrech thanks :) – primo – 2018-02-24T09:17:48.600

6Finally a fractal :) Love those. Also, that green at 144 degrees is my favorite color (as opposed to pure green at 120 degrees which is just boring). – Mark Jeronimus – 2014-03-01T11:15:55.867

I'm glad you like it :) Pure coincidence about the green, though (360 * 2/5). A version I've made for myself (without the allrgb restriction) has much nicer output: http://i.stack.imgur.com/4H3m1.png

– primo – 2014-03-01T11:53:03.413

2I dunno, I actually kind of like the AllRGB versions better; the need to use the full luminance space nicely emphasizes the gradients. – Ilmari Karonen – 2014-03-01T20:38:14.820

@Zom-B My answer also has a fractal: The Sierpinski Triangle. :-) (and it was posted earlier). – Justin – 2014-03-02T01:30:47.670

2+1 Finally some good fractals! The last one is my personal favorite. You should make a video zooming in! (@Quincunx: Saw yours too; it had my vote from day 1!) – Jason C – 2014-03-03T00:12:41.587

1@JasonC I've added an animation ;) – primo – 2014-03-03T08:43:49.927

@primo Nice!! These are so cool! I love how the hues change as it zooms. – Jason C – 2014-03-03T16:15:02.597

1

Come join the CodeGolf club on allrgb.com, lol. P.S. Nice avatar.

– Jason C – 2014-03-06T14:54:47.190

130

I got this idea from user fejesjoco's algorithm and wanted to play a bit, so I started to write my own algorithm from scratch.

I'm posting this because I feel that if I can make something better* than the best out of you guys, I don't think this challenge is finished yet. To compare, there are some stunning designs on allRGB that I consider way beyond the level reached here and I have no idea how they did it.

*) will still be decided by votes

This algorithm:

  1. Start with a (few) seed(s), with colors as close as possible to black.
  2. Keep a list of all pixels that are unvisited and 8-connected to a visited point.
  3. Select a random** point from that list
  4. Calculate the average color of all calculated pixels [Edit ...in a 9x9 square using a Gaussian kernel] 8-connected to it (this is the reason why it looks so smooth) If none are found, take black.
  5. in a 3x3x3 cube around this color, search for an unused color.
    • When multple colors are found, take the darkest one.
    • When multple equally dark colors are found, take a random one out of those.
    • When nothing is found, update the search range to 5x5x5, 7x7x7, etc. Repeat from 5.
  6. Plot pixel, update list and repeat from 3

I also experimented with different probabilities of choosing candidate points based on counting how many visited neighbors the selected pixel has, but it only slowed down the algorithm without making it prettier. The current algorithm doesn't use probabilities and chooses a random point from the list. This causes points with lots of neighbors to quickly fill up, making it just a growing solid ball with a fuzzy edge. This also prevents unavailability of neighboring colors if the crevices were to be filled up later in the process.

The image is toroidal.

Java

Download: com.digitalmodular library

package demos;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.Arrays;

import com.digitalmodular.utilities.RandomFunctions;
import com.digitalmodular.utilities.gui.ImageFunctions;
import com.digitalmodular.utilities.swing.window.PixelImage;
import com.digitalmodular.utilities.swing.window.PixelWindow;

/**
 * @author jeronimus
 */
// Date 2014-02-28
public class AllColorDiffusion extends PixelWindow implements Runnable {
    private static final int    CHANNEL_BITS    = 7;

    public static void main(String[] args) {
        int bits = CHANNEL_BITS * 3;
        int heightBits = bits / 2;
        int widthBits = bits - heightBits;

        new AllColorDiffusion(CHANNEL_BITS, 1 << widthBits, 1 << heightBits);
    }

    private final int           width;
    private final int           height;
    private final int           channelBits;
    private final int           channelSize;

    private PixelImage          img;
    private javax.swing.Timer   timer;

    private boolean[]           colorCube;
    private long[]              foundColors;
    private boolean[]           queued;
    private int[]               queue;
    private int                 queuePointer    = 0;
    private int                 remaining;

    public AllColorDiffusion(int channelBits, int width, int height) {
        super(1024, 1024 * height / width);

        RandomFunctions.RND.setSeed(0);

        this.width = width;
        this.height = height;
        this.channelBits = channelBits;
        channelSize = 1 << channelBits;
    }

    @Override
    public void initialized() {
        img = new PixelImage(width, height);

        colorCube = new boolean[channelSize * channelSize * channelSize];
        foundColors = new long[channelSize * channelSize * channelSize];
        queued = new boolean[width * height];
        queue = new int[width * height];
        for (int i = 0; i < queue.length; i++)
            queue[i] = i;

        new Thread(this).start();
    }

    @Override
    public void resized() {}

    @Override
    public void run() {
        timer = new javax.swing.Timer(500, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                draw();
            }
        });

        while (true) {
            img.clear(0);
            init();
            render();
        }

        // System.exit(0);
    }

    private void init() {
        RandomFunctions.RND.setSeed(0);

        Arrays.fill(colorCube, false);
        Arrays.fill(queued, false);
        remaining = width * height;

        // Initial seeds (need to be the darkest colors, because of the darkest
        // neighbor color search algorithm.)
        setPixel(width / 2 + height / 2 * width, 0);
        remaining--;
    }

    private void render() {
        timer.start();

        for (; remaining > 0; remaining--) {
            int point = findPoint();
            int color = findColor(point);
            setPixel(point, color);
        }

        timer.stop();
        draw();

        try {
            ImageFunctions.savePNG(System.currentTimeMillis() + ".png", img.image);
        }
        catch (IOException e1) {
            e1.printStackTrace();
        }
    }

    void draw() {
        g.drawImage(img.image, 0, 0, getWidth(), getHeight(), 0, 0, width, height, null);
        repaintNow();
    }

    private int findPoint() {
        while (true) {
            // Time to reshuffle?
            if (queuePointer == 0) {
                for (int i = queue.length - 1; i > 0; i--) {
                    int j = RandomFunctions.RND.nextInt(i);
                    int temp = queue[i];
                    queue[i] = queue[j];
                    queue[j] = temp;
                    queuePointer = queue.length;
                }
            }

            if (queued[queue[--queuePointer]])
                return queue[queuePointer];
        }
    }

    private int findColor(int point) {
        int x = point & width - 1;
        int y = point / width;

        // Calculate the reference color as the average of all 8-connected
        // colors.
        int r = 0;
        int g = 0;
        int b = 0;
        int n = 0;
        for (int j = -1; j <= 1; j++) {
            for (int i = -1; i <= 1; i++) {
                point = (x + i & width - 1) + width * (y + j & height - 1);
                if (img.pixels[point] != 0) {
                    int pixel = img.pixels[point];

                    r += pixel >> 24 - channelBits & channelSize - 1;
                    g += pixel >> 16 - channelBits & channelSize - 1;
                    b += pixel >> 8 - channelBits & channelSize - 1;
                    n++;
                }
            }
        }
        r /= n;
        g /= n;
        b /= n;

        // Find a color that is preferably darker but not too far from the
        // original. This algorithm might fail to take some darker colors at the
        // start, and when the image is almost done the size will become really
        // huge because only bright reference pixels are being searched for.
        // This happens with a probability of 50% with 6 channelBits, and more
        // with higher channelBits values.
        //
        // Try incrementally larger distances from reference color.
        for (int size = 2; size <= channelSize; size *= 2) {
            n = 0;

            // Find all colors in a neighborhood from the reference color (-1 if
            // already taken).
            for (int ri = r - size; ri <= r + size; ri++) {
                if (ri < 0 || ri >= channelSize)
                    continue;
                int plane = ri * channelSize * channelSize;
                int dr = Math.abs(ri - r);
                for (int gi = g - size; gi <= g + size; gi++) {
                    if (gi < 0 || gi >= channelSize)
                        continue;
                    int slice = plane + gi * channelSize;
                    int drg = Math.max(dr, Math.abs(gi - g));
                    int mrg = Math.min(ri, gi);
                    for (int bi = b - size; bi <= b + size; bi++) {
                        if (bi < 0 || bi >= channelSize)
                            continue;
                        if (Math.max(drg, Math.abs(bi - b)) > size)
                            continue;
                        if (!colorCube[slice + bi])
                            foundColors[n++] = Math.min(mrg, bi) << channelBits * 3 | slice + bi;
                    }
                }
            }

            if (n > 0) {
                // Sort by distance from origin.
                Arrays.sort(foundColors, 0, n);

                // Find a random color amongst all colors equally distant from
                // the origin.
                int lowest = (int)(foundColors[0] >> channelBits * 3);
                for (int i = 1; i < n; i++) {
                    if (foundColors[i] >> channelBits * 3 > lowest) {
                        n = i;
                        break;
                    }
                }

                int nextInt = RandomFunctions.RND.nextInt(n);
                return (int)(foundColors[nextInt] & (1 << channelBits * 3) - 1);
            }
        }

        return -1;
    }

    private void setPixel(int point, int color) {
        int b = color & channelSize - 1;
        int g = color >> channelBits & channelSize - 1;
        int r = color >> channelBits * 2 & channelSize - 1;
        img.pixels[point] = 0xFF000000 | ((r << 8 | g) << 8 | b) << 8 - channelBits;

        colorCube[color] = true;

        int x = point & width - 1;
        int y = point / width;
        queued[point] = false;
        for (int j = -1; j <= 1; j++) {
            for (int i = -1; i <= 1; i++) {
                point = (x + i & width - 1) + width * (y + j & height - 1);
                if (img.pixels[point] == 0) {
                    queued[point] = true;
                }
            }
        }
    }
}
  • 512×512
  • original 1 seed
  • 1 second

enter image description here

  • 2048×1024
  • slightly tiled to 1920×1080 desktop
  • 30 seconds
  • negative in photoshop

enter image description here

  • 2048×1024
  • 8 seeds
  • 27 seconds

enter image description here

  • 512×512
  • 40 random seeds
  • 6 seconds

enter image description here

  • 4096×4096
  • 1 seed
  • Streaks get significantly sharper (as in they look like they could chop a fish into sashimi)
  • Looked like it finished in 20 minutes, but ... failed to finish for some reason, so now I'm running 7 instances in parallel overnight.

[See below]

[Edit]
** I discovered that my method of choosing pixels was not totally random at all. I thought having a random permutation of the search space would be random and faster than real random (because a point will not be chosen twice by chance. However somehow, replacing it with real random, I consistently get more noise speckles in my image.

[version 2 code removed because I was over the 30,000 character limit]

enter image description here

  • Increased the initial search cube to 5x5x5

enter image description here

  • Even bigger, 9x9x9

enter image description here

  • Accident 1. Disabled the permutation so the search space is always linear.

enter image description here

  • Accident 2. Tried a new search technique using a fifo queue. Still have to analyze this but I thought it was worth sharing.

enter image description here

  • Always choosing within X unused pixels from the center
  • X ranges from 0 to 8192 in steps of 256

Image can't be uploaded: "Oops! Something Bad Happened! It’s not you, it’s us. This is our fault." Image is just too big for imgur. Trying elsewhere...

enter image description here

Experimenting with a scheduler package I found in the digitalmodular library to determine the order in which the pixels are handled (instead of diffusion).

package demos;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.Arrays;

import com.digitalmodular.utilities.RandomFunctions;
import com.digitalmodular.utilities.gui.ImageFunctions;
import com.digitalmodular.utilities.gui.schedulers.ScheduledPoint;
import com.digitalmodular.utilities.gui.schedulers.Scheduler;
import com.digitalmodular.utilities.gui.schedulers.XorScheduler;
import com.digitalmodular.utilities.swing.window.PixelImage;
import com.digitalmodular.utilities.swing.window.PixelWindow;

/**
 * @author jeronimus
 */
// Date 2014-02-28
public class AllColorDiffusion3 extends PixelWindow implements Runnable {
    private static final int    CHANNEL_BITS    = 7;

    public static void main(String[] args) {

        int bits = CHANNEL_BITS * 3;
        int heightBits = bits / 2;
        int widthBits = bits - heightBits;

        new AllColorDiffusion3(CHANNEL_BITS, 1 << widthBits, 1 << heightBits);
    }

    private final int           width;
    private final int           height;
    private final int           channelBits;
    private final int           channelSize;

    private PixelImage          img;
    private javax.swing.Timer   timer;
    private Scheduler           scheduler   = new XorScheduler();

    private boolean[]           colorCube;
    private long[]              foundColors;

    public AllColorDiffusion3(int channelBits, int width, int height) {
        super(1024, 1024 * height / width);

        this.width = width;
        this.height = height;
        this.channelBits = channelBits;
        channelSize = 1 << channelBits;
    }

    @Override
    public void initialized() {
        img = new PixelImage(width, height);

        colorCube = new boolean[channelSize * channelSize * channelSize];
        foundColors = new long[channelSize * channelSize * channelSize];

        new Thread(this).start();
    }

    @Override
    public void resized() {}

    @Override
    public void run() {
        timer = new javax.swing.Timer(500, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                draw();
            }
        });

        // for (double d = 0.2; d < 200; d *= 1.2)
        {
            img.clear(0);
            init(0);
            render();
        }

        // System.exit(0);
    }

    private void init(double param) {
        // RandomFunctions.RND.setSeed(0);

        Arrays.fill(colorCube, false);

        // scheduler = new SpiralScheduler(param);
        scheduler.init(width, height);
    }

    private void render() {
        timer.start();

        while (scheduler.getProgress() != 1) {
            int point = findPoint();
            int color = findColor(point);
            setPixel(point, color);
        }

        timer.stop();
        draw();

        try {
            ImageFunctions.savePNG(System.currentTimeMillis() + ".png", img.image);
        }
        catch (IOException e1) {
            e1.printStackTrace();
        }
    }

    void draw() {
        g.drawImage(img.image, 0, 0, getWidth(), getHeight(), 0, 0, width, height, null);
        repaintNow();
        setTitle(Double.toString(scheduler.getProgress()));
    }

    private int findPoint() {
        ScheduledPoint p = scheduler.poll();

        // try {
        // Thread.sleep(1);
        // }
        // catch (InterruptedException e) {
        // }

        return p.x + width * p.y;
    }

    private int findColor(int point) {
        // int z = 0;
        // for (int i = 0; i < colorCube.length; i++)
        // if (!colorCube[i])
        // System.out.println(i);

        int x = point & width - 1;
        int y = point / width;

        // Calculate the reference color as the average of all 8-connected
        // colors.
        int r = 0;
        int g = 0;
        int b = 0;
        int n = 0;
        for (int j = -3; j <= 3; j++) {
            for (int i = -3; i <= 3; i++) {
                point = (x + i & width - 1) + width * (y + j & height - 1);
                int f = (int)Math.round(10000 * Math.exp((i * i + j * j) * -0.4));
                if (img.pixels[point] != 0) {
                    int pixel = img.pixels[point];

                    r += (pixel >> 24 - channelBits & channelSize - 1) * f;
                    g += (pixel >> 16 - channelBits & channelSize - 1) * f;
                    b += (pixel >> 8 - channelBits & channelSize - 1) * f;
                    n += f;
                }
                // System.out.print(f + "\t");
            }
            // System.out.println();
        }
        if (n > 0) {
            r /= n;
            g /= n;
            b /= n;
        }

        // Find a color that is preferably darker but not too far from the
        // original. This algorithm might fail to take some darker colors at the
        // start, and when the image is almost done the size will become really
        // huge because only bright reference pixels are being searched for.
        // This happens with a probability of 50% with 6 channelBits, and more
        // with higher channelBits values.
        //
        // Try incrementally larger distances from reference color.
        for (int size = 2; size <= channelSize; size *= 2) {
            n = 0;

            // Find all colors in a neighborhood from the reference color (-1 if
            // already taken).
            for (int ri = r - size; ri <= r + size; ri++) {
                if (ri < 0 || ri >= channelSize)
                    continue;
                int plane = ri * channelSize * channelSize;
                int dr = Math.abs(ri - r);
                for (int gi = g - size; gi <= g + size; gi++) {
                    if (gi < 0 || gi >= channelSize)
                        continue;
                    int slice = plane + gi * channelSize;
                    int drg = Math.max(dr, Math.abs(gi - g));
                    // int mrg = Math.min(ri, gi);
                    long srg = ri * 299L + gi * 436L;
                    for (int bi = b - size; bi <= b + size; bi++) {
                        if (bi < 0 || bi >= channelSize)
                            continue;
                        if (Math.max(drg, Math.abs(bi - b)) > size)
                            continue;
                        if (!colorCube[slice + bi])
                            // foundColors[n++] = Math.min(mrg, bi) <<
                            // channelBits * 3 | slice + bi;
                            foundColors[n++] = srg + bi * 114L << channelBits * 3 | slice + bi;
                    }
                }
            }

            if (n > 0) {
                // Sort by distance from origin.
                Arrays.sort(foundColors, 0, n);

                // Find a random color amongst all colors equally distant from
                // the origin.
                int lowest = (int)(foundColors[0] >> channelBits * 3);
                for (int i = 1; i < n; i++) {
                    if (foundColors[i] >> channelBits * 3 > lowest) {
                        n = i;
                        break;
                    }
                }

                int nextInt = RandomFunctions.RND.nextInt(n);
                return (int)(foundColors[nextInt] & (1 << channelBits * 3) - 1);
            }
        }

        return -1;
    }

    private void setPixel(int point, int color) {
        int b = color & channelSize - 1;
        int g = color >> channelBits & channelSize - 1;
        int r = color >> channelBits * 2 & channelSize - 1;
        img.pixels[point] = 0xFF000000 | ((r << 8 | g) << 8 | b) << 8 - channelBits;

        colorCube[color] = true;
    }
}
  • Angular(8)

enter image description here

  • Angular(64)

enter image description here

  • CRT

enter image description here

  • Dither

enter image description here

  • Flower(5, X), where X ranges from 0.5 to 20 in steps of X=X×1.2

enter image description here

  • Mod

enter image description here

  • Pythagoras

enter image description here

  • Radial

enter image description here

  • Random

enter image description here

  • Scanline

enter image description here

  • Spiral(X), where X ranges from 0.1 to 200 in steps of X=X×1.2
  • You can see it ranges in between Radial to Angular(5)

enter image description here

  • Split

enter image description here

  • SquareSpiral

enter image description here

  • XOR

enter image description here

New eye-food

  • Effect of color selection by max(r, g, b)

enter image description here

  • Effect of color selection by min(r, g, b)
  • Notice that this one has exactly the same features/details as the one above, only with different colors! (same random seed)

enter image description here

  • Effect of color selection by max(r, min(g, b))

enter image description here

  • Effect of color selection by gray value 299*r + 436*g + 114*b

enter image description here

  • Effect of color selection by 1*r + 10*g + 100*b

enter image description here

  • Effect of color selection by 100*r + 10*g + 1*b

enter image description here

  • Happy accidents when 299*r + 436*g + 114*b overflowed in a 32-bit integer

enter image description here enter image description here enter image description here

  • Variant 3, with gray value and Radial scheduler

enter image description here

  • I forgot how I created this

enter image description here

  • The CRT Scheduler also had a happy integer overflow bug (updated the ZIP), this caused it to start half-way, with 512×512 images, instead of at the center. This is what it's supposed to look like:

enter image description here enter image description here

  • InverseSpiralScheduler(64) (new)

enter image description here

  • Another XOR

enter image description here

  • First successful 4096 render after the bugfix. I think this was version 3 on SpiralScheduler(1) or something

enter image description here (50MB!!)

  • Version 1 4096, but I accidentally left the color criteria on max()

enter image description here (50MB!!)

  • 4096, now with min()
  • Notice that this one has exactly the same features/details as the one above, only with different colors! (same random seed)
  • Time: forgot to record it but the file timestamp is 3 minutes after the image before

enter image description here (50MB!!)

Mark Jeronimus

Posted 2014-02-25T21:16:53.557

Reputation: 6 451

Cool. Your final image is similar to a second idea I've been tossing around, although I have a feeling mine won't look as good as that. BTW, there is a similar cool one at http://allrgb.com/diffusive.

– Jason C – 2014-03-01T03:00:06.017

It was meant as just a teaser, but I edited it in fear of being flagged, which apparently happened :) – Mark Jeronimus – 2014-03-01T11:02:39.463

2Even the accidents look nice :). The color cube seems like a very good idea, and your render speeds are amazing, compared to mine. Some designs on allrgb do have a good description, for example allrgb.com/dla. I wish I had more time to do more experiments, there are so many possibilities... – fejesjoco – 2014-03-02T18:56:34.610

I almost forgot, I just uploaded some of my big renders. I think one of them, the rainbow smoke/spilled ink thingy, is better than anything on allrgb :). I agree, the others are not so stunning, that's why I made a video to make something more out of them :). – fejesjoco – 2014-03-02T19:09:32.680

Added source code and the link to the Digisoft library, so you can actually compile my code – Mark Jeronimus – 2014-03-02T21:06:54.087

@Zom-B If the contest is over, you should select the most popular answer as the accepted winner. – Jason C – 2014-03-03T23:38:17.867

Bug update, dear followers. – Mark Jeronimus – 2014-03-04T08:10:27.393

This is definitely my favorite answer. I really like the variety in all of the images you posted. – Kevin – 2014-03-05T17:50:41.233

Can we find the program that generated any of the other images, on pastebin/github/etc.? Would be cool to read through that! (the >30,000 char one) – Joseph Adams – 2014-03-15T21:44:54.433

The 2nd program is just a simplified version of the first, with random instead of the permutation. The 30,000 refers to the StackExchange post limit. – Mark Jeronimus – 2014-03-16T19:03:22.853

72

C++ w/ Qt

I see you version:

enter image description here

using normal distribution for the colors:

enter image description here enter image description here

or first sorted by red / hue (with a smaller deviation):

enter image description here enter image description here

or some other distributions:

enter image description here enter image description here

Cauchy distribution (hsl / red):

enter image description here enter image description here

sorted cols by lightness (hsl):

enter image description here

updated source code - produces 6th image:

int main() {
    const int c = 256*128;
    std::vector<QRgb> data(c);
    QImage image(256, 128, QImage::Format_RGB32);

    std::default_random_engine gen;
    std::normal_distribution<float> dx(0, 2);
    std::normal_distribution<float> dy(0, 1);

    for(int i = 0; i < c; ++i) {
        data[i] = qRgb(i << 3 & 0xF8, i >> 2 & 0xF8, i >> 7 & 0xF8);
    }
    std::sort(data.begin(), data.end(), [] (QRgb a, QRgb b) -> bool {
        return QColor(a).hsvHue() < QColor(b).hsvHue();
    });

    int i = 0;
    while(true) {
        if(i % 10 == 0) { //no need on every iteration
            dx = std::normal_distribution<float>(0, 8 + 3 * i/1000.f);
            dy = std::normal_distribution<float>(0, 4 + 3 * i/1000.f);
        }
        int x = (int) dx(gen);
        int y = (int) dy(gen);
        if(x < 256 && x >= 0 && y >= 0 && y < 128) {
            if(!image.pixel(x, y)) {
                image.setPixel(x, y, data[i]);
                if(i % (c/100) == 1) {
                    std::cout << (int) (100.f*i/c) << "%\n";
                }
                if(++i == c) break;
            }
        }
    }
    image.save("tmp.png");
    return 0;
}

Jaa-c

Posted 2014-02-25T21:16:53.557

Reputation: 1 575

Nicely done. However, mightn't image.pixel(x, y) == 0 fail and overwrite the first placed pixel? – Mark Jeronimus – 2014-02-26T06:36:04.093

@Zom-B: it can, but then the last one will be black, so it's within the rules.. – Jaa-c – 2014-02-26T11:04:35.940

No rule problem though. I just thought you might have missed it. Might as well count from 1 then. I love your other ones! – Mark Jeronimus – 2014-02-26T21:48:07.030

@Zom-B: thanks, I might add a few more, I kinda like it :P – Jaa-c – 2014-02-26T22:09:17.403

The one with two circles and the one below it together kinda look like a monkey face. – Jason C – 2014-02-28T04:45:02.147

"I see you version" has 262102 colors for some reason. – Mark Jeronimus – 2014-02-28T23:11:13.987

64

In Java:

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedList;

import javax.imageio.ImageIO;

public class ImgColor {

    private static class Point {
        public int x, y;
        public color c;

        public Point(int x, int y, color c) {
            this.x = x;
            this.y = y;
            this.c = c;
        }
    }

    private static class color {
        char r, g, b;

        public color(int i, int j, int k) {
            r = (char) i;
            g = (char) j;
            b = (char) k;
        }
    }

    public static LinkedList<Point> listFromImg(String path) {
        LinkedList<Point> ret = new LinkedList<>();
        BufferedImage bi = null;
        try {
            bi = ImageIO.read(new File(path));
        } catch (IOException e) {
            e.printStackTrace();
        }
        for (int x = 0; x < 4096; x++) {
            for (int y = 0; y < 4096; y++) {
                Color c = new Color(bi.getRGB(x, y));
                ret.add(new Point(x, y, new color(c.getRed(), c.getGreen(), c.getBlue())));
            }
        }
        Collections.shuffle(ret);
        return ret;
    }

    public static LinkedList<color> allColors() {
        LinkedList<color> colors = new LinkedList<>();
        for (int r = 0; r < 256; r++) {
            for (int g = 0; g < 256; g++) {
                for (int b = 0; b < 256; b++) {
                    colors.add(new color(r, g, b));
                }
            }
        }
        Collections.shuffle(colors);
        return colors;
    }

    public static Double cDelta(color a, color b) {
        return Math.pow(a.r - b.r, 2) + Math.pow(a.g - b.g, 2) + Math.pow(a.b - b.b, 2);
    }

    public static void main(String[] args) {
        BufferedImage img = new BufferedImage(4096, 4096, BufferedImage.TYPE_INT_RGB);
        LinkedList<Point> orig = listFromImg(args[0]);
        LinkedList<color> toDo = allColors();

        Point p = null;
        while (orig.size() > 0 && (p = orig.pop()) != null) {
            color chosen = toDo.pop();
            for (int i = 0; i < Math.min(100, toDo.size()); i++) {
                color c = toDo.pop();
                if (cDelta(c, p.c) < cDelta(chosen, p.c)) {
                    toDo.add(chosen);
                    chosen = c;
                } else {
                    toDo.add(c);
                }
            }
            img.setRGB(p.x, p.y, new Color(chosen.r, chosen.g, chosen.b).getRGB());
        }
        try {
            ImageIO.write(img, "PNG", new File(args[1]));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

and an input image:

lemur

I generate something like this:

acidLemur

uncompressed version here: https://www.mediafire.com/?7g3fetvaqhoqgh8

It takes my computer roughly 30 minutes to do a 4096^2 image, which is a huge improvement over the 32 days my first implementation would have taken.

AdamSpurgin

Posted 2014-02-25T21:16:53.557

Reputation: 941

1ouch; 32 days didnt sounded funny..... the average algorithm in fejesjocos answer on 4k before optimize would have taken probably multiple months – masterX244 – 2014-03-08T21:59:20.040

5I love his punk eyebrows! – Level River St – 2014-03-09T12:56:48.120

45

Java with BubbleSort

(usually Bubblesort isnt liked that much but for this challenge it finally had a use :) generated a line with all elements in 4096 steps apart then shuffled it; the sorting went thru and each like got 1 added to their value while being sorted so as result you got the values sorted and all colors

Updated the Sourcecode to get those big stripes removed
(needed some bitwise magic :P)

class Pix
{
    public static void main(String[] devnull) throws Exception
    {
        int chbits=8;
        int colorsperchannel=1<<chbits;
        int xsize=4096,ysize=4096;
        System.out.println(colorsperchannel);
        int[] x = new int[xsize*ysize];//colorstream

        BufferedImage i = new BufferedImage(xsize,ysize, BufferedImage.TYPE_INT_RGB);
        List<Integer> temp = new ArrayList<>();
        for (int j = 0; j < 4096; j++)
        {
            temp.add(4096*j);
        }
        int[] temp2=new int[4096];

        Collections.shuffle(temp,new Random(9263));//intended :P looked for good one
        for (int j = 0; j < temp.size(); j++)
        {
            temp2[j]=(int)(temp.get(j));
        }
        x = spezbubblesort(temp2, 4096);
        int b=-1;
        int b2=-1;
        for (int j = 0; j < x.length; j++)
        {
            if(j%(4096*16)==0)b++;
            if(j%(4096)==0)b2++;
            int h=j/xsize;
            int w=j%xsize;
            i.setRGB(w, h, x[j]&0xFFF000|(b|(b2%16)<<8));
            x[j]=x[j]&0xFFF000|(b|(b2%16)<<8);
        }  

        //validator sorting and checking that all values only have 1 difference
        Arrays.sort(x);
        int diff=0;
        for (int j = 1; j < x.length; j++)
        {
            int ndiff=x[j]-x[j-1];
            if(ndiff!=diff)
            {
                System.out.println(ndiff);
            }
            diff=ndiff;

        }
        OutputStream out = new BufferedOutputStream(new FileOutputStream("RGB24.bmp"));
        ImageIO.write(i, "bmp", out);

    }
    public static int[] spezbubblesort(int[] vals,int lines)
    {
        int[] retval=new int[vals.length*lines];
        for (int i = 0; i < lines; i++)
        {
            retval[(i<<12)]=vals[0];
            for (int j = 1; j < vals.length; j++)
            {
                retval[(i<<12)+j]=vals[j];
                if(vals[j]<vals[j-1])
                {

                    int temp=vals[j-1];
                    vals[j-1]=vals[j];
                    vals[j]=temp;
                }
                vals[j-1]=vals[j-1]+1;
            }
            vals[lines-1]=vals[lines-1]+1;
        }
        return retval;
    }
}

Result:

Old version

class Pix
{
    public static void main(String[] devnull) throws Exception
    {
        int[] x = new int[4096*4096];//colorstream
        int idx=0;
        BufferedImage i = new BufferedImage(4096, 4096, BufferedImage.TYPE_INT_RGB);
        //GENCODE
        List<Integer> temp = new ArrayList<>();
        for (int j = 0; j < 4096; j++)
        {
            temp.add(4096*j);
        }
        int[] temp2=new int[4096];

        Collections.shuffle(temp,new Random(9263));//intended :P looked for good one
        for (int j = 0; j < temp.size(); j++)
        {
            temp2[j]=(int)(temp.get(j));
        }
        x = spezbubblesort(temp2, 4096);
        for (int j = 0; j < x.length; j++)
        {
            int h=j/4096;
            int w=j%4096;
            i.setRGB(w, h, x[j]);
        }
        //validator sorting and checking that all values only have 1 difference
        Arrays.sort(x);
        int diff=0;
        for (int j = 1; j < x.length; j++)
        {
            int ndiff=x[j]-x[j-1];
            if(ndiff!=diff)
            {
                System.out.println(ndiff);
            }
            diff=ndiff;

        }
        OutputStream out = new BufferedOutputStream(new FileOutputStream("RGB24.bmp"));
        ImageIO.write(i, "bmp", out);
    }
    public static int[] spezbubblesort(int[] vals,int lines)
    {
        int[] retval=new int[vals.length*lines];
        for (int i = 0; i < lines; i++)
        {
            retval[(i<<12)]=vals[0];
            for (int j = 1; j < vals.length; j++)
            {
                retval[(i<<12)+j]=vals[j];
                if(vals[j]<vals[j-1])
                {

                    int temp=vals[j-1];
                    vals[j-1]=vals[j];
                    vals[j]=temp;
                }
                vals[j-1]=vals[j-1]+1;
            }
            vals[lines-1]=vals[lines-1]+1;
        }
        return retval;
    }
}

output preview

masterX244

Posted 2014-02-25T21:16:53.557

Reputation: 3 942

There is already a QuickSort version on the allRGB page. – Mark Jeronimus – 2014-02-28T23:11:57.950

1@Zom-B Quicksort is a different algorithm than Bubblesort – masterX244 – 2014-02-28T23:43:29.837

43

C

Creates a vortex, for reasons I don't understand, with even and odd frames containing completely different vortices.

This a preview of the first 50 odd frames:

vortex preview

Sample image converted from PPM to demo complete color coverage:

sample image

Later on, when it's all blended into grey, you can still see it spinning: longer sequence.

Code as follows. To run, include the frame number, e.g.:

./vortex 35 > 35.ppm

I used this to get an animated GIF:

convert -delay 10 `ls *.ppm | sort -n | xargs` -loop 0 vortex.gif
#include <stdlib.h>
#include <stdio.h>

#define W 256
#define H 128

typedef struct {unsigned char r, g, b;} RGB;

int S1(const void *a, const void *b)
{
    const RGB *p = a, *q = b;
    int result = 0;

    if (!result)
        result = (p->b + p->g * 6 + p->r * 3) - (q->b + q->g * 6 + q->r * 3);

    return result;
}

int S2(const void *a, const void *b)
{
    const RGB *p = a, *q = b;
    int result = 0;

    if (!result)
        result = p->b * 6 - p->g;
    if (!result)
        result = p->r - q->r;
    if (!result)
        result = p->g - q->b * 6;

    return result;
}

int main(int argc, char *argv[])
{
    int i, j, n;
    RGB *rgb = malloc(sizeof(RGB) * W * H);
    RGB c[H];

    for (i = 0; i < W * H; i++)
    {
        rgb[i].b = (i & 0x1f) << 3;
        rgb[i].g = ((i >> 5) & 0x1f) << 3;
        rgb[i].r = ((i >> 10) & 0x1f) << 3;
    }

    qsort(rgb, H * W, sizeof(RGB), S1);

    for (n = 0; n < atoi(argv[1]); n++)
    {
        for (i = 0; i < W; i++)
        {
            for (j = 0; j < H; j++)
                c[j] = rgb[j * W + i];
            qsort(c, H, sizeof(RGB), S2);
            for (j = 0; j < H; j++)
                rgb[j * W + i] = c[j];
        }

        for (i = 0; i < W * H; i += W)
            qsort(rgb + i, W, sizeof(RGB), S2);
    }

    printf("P6 %d %d 255\n", W, H);
    fwrite(rgb, sizeof(RGB), W * H, stdout);

    free(rgb);

    return 0;
}

user15259

Posted 2014-02-25T21:16:53.557

Reputation:

53You know it's C when thing happen for "reasons I don't understand". – Nit – 2014-02-26T20:22:00.360

2Yeah, usually I know what to expect, but here I was just playing around to see what patterns I could get, and this non-terminating order-within-chaos sequence came up. – None – 2014-02-26T21:02:17.793

1I count only 256 unique colors in each of the animation frames (ofcourse because it's GIF) – Mark Jeronimus – 2014-02-26T21:53:23.820

Yes, the raw PPM files though have all the colors. Was going to attach one, but PPM images aren't accepted. – None – 2014-02-26T21:58:21.090

convert em to PNG (lossless so all colors will stay at their intended spot) or upload it to mediaafire or any other filehoster and post link – masterX244 – 2014-02-26T22:16:42.113

Bug? result = p->b * 6 - p->g; – Mark Jeronimus – 2014-02-26T22:20:25.323

8It vortexes because your comparison function doesn't follow the triangle inequality. For example, r>b, b>g, g>r. I can't even port it to Java because it's mergesort relies on this very property, so I get the exception "Comparison method violates its general contract!" – Mark Jeronimus – 2014-02-26T22:32:56.720

2I'll try p->b * 6 - q->g; but if it wrecks the vortex, won't fix it! – None – 2014-02-26T23:52:34.957

convert -deconstruct and good 95% of your gif size is gone – mniip – 2014-02-27T03:21:04.270

4+1 for reasons I don't understand. – Jason C – 2014-02-27T04:49:26.380

is there a similar tool like convert for windows? I'm using MS gif animator but its a crappy windows 95 gui – Mark Jeronimus – 2014-02-27T06:28:53.963

@Zom-B, the GIMP has an "optimise for GIF" feature. – Peter Taylor – 2014-02-27T09:25:58.180

@Zom-B - the line is essential for the vortex, correcting it or removing it - no vortex! – None – 2014-02-27T14:05:40.677

@mniip - no difference in size after -deconstruct – None – 2014-02-27T14:19:56.197

You have to give the -deconstruct parameter when creating the gif: (besides, your command has some style mistakes) convert -deconstruct -delay 10 $(echo *.ppm | sort -n) -loop 0 vortex.gif – mniip – 2014-02-27T14:48:30.520

@mniip - no change in size - did exactly as you wrote: $ convert -deconstruct -delay 10 $(echo *.ppm | sort -n) -loop 0 vortex.gif $ ls -l *.gif -rw-rw-r-- 1 yimin yimin 20437942 Feb 27 14:55 vortex.gif – None – 2014-02-27T19:59:36.033

1@Zom-B Imagemagic (which convert is a part) is available for Windows, too – masterX244 – 2014-03-01T22:02:14.277

40

Java

Variations of a color picker in 512x512. Elegant code it is not, but I do like the pretty pictures:

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;

import javax.imageio.ImageIO;

public class EighteenBitColors {

    static boolean shuffle_block = false;
    static int shuffle_radius = 0;

    public static void main(String[] args) {
        BufferedImage img = new BufferedImage(512, 512, BufferedImage.TYPE_INT_RGB);
        for(int r=0;r<64;r++)
            for(int g=0;g<64;g++)
                for(int b=0;b<64;b++)
                    img.setRGB((r * 8) + (b / 8), (g * 8) + (b % 8), ((r * 4) << 8 | (g * 4)) << 8 | (b * 4));

        if(shuffle_block)
            blockShuffle(img);
        else
            shuffle(img, shuffle_radius);

        try {           
            ImageIO.write(img, "png", new File(getFileName()));
        } catch(IOException e){
            System.out.println("suck it");
        }
    }

    public static void shuffle(BufferedImage img, int radius){
        if(radius < 1)
            return;
        int width = img.getWidth();
        int height = img.getHeight();
        Random rand = new Random();
        for(int x=0;x<512;x++){
            for(int y=0;y<512;y++){
                int xx = -1;
                int yy = -1;
                while(xx < 0 || xx >= width){
                    xx = x + rand.nextInt(radius*2+1) - radius;
                }
                while(yy < 0 || yy >= height){
                    yy = y + rand.nextInt(radius*2+1) - radius;
                }
                int tmp = img.getRGB(xx, yy);
                img.setRGB(xx, yy, img.getRGB(x, y));
                img.setRGB(x,y,tmp);
            }
        }
    }

    public static void blockShuffle(BufferedImage img){
        int tmp;
        Random rand = new Random();
        for(int bx=0;bx<8;bx++){
            for(int by=0;by<8;by++){
                for(int x=0;x<64;x++){
                    for(int y=0;y<64;y++){
                        int xx = bx*64+x;
                        int yy = by*64+y;
                        int xxx = bx*64+rand.nextInt(64);
                        int yyy = by*64+rand.nextInt(64);
                        tmp = img.getRGB(xxx, yyy);
                        img.setRGB(xxx, yyy, img.getRGB(xx, yy));
                        img.setRGB(xx,yy,tmp);
                    }
                }
            }
        }
    }

    public static String getFileName(){
        String fileName = "allrgb_";
        if(shuffle_block){
            fileName += "block";
        } else if(shuffle_radius > 0){
            fileName += "radius_" + shuffle_radius;
        } else {
            fileName += "no_shuffle";
        }
        return fileName + ".png";
    }
}

As written, it outputs:

no shuffle

If you run it with shuffle_block = true, it shuffles the colors in each 64x64 block:

block shuffle

Else, if you run it with shuffle_radius > 0, it shuffles each pixel with a random pixel within shuffle_radius in x/y. After playing with various sizes, I like a 32 pixel radius, as it blurs the lines without moving stuff around too much:

enter image description here

Geobits

Posted 2014-02-25T21:16:53.557

Reputation: 19 061

These are really great – Matthew – 2017-07-02T21:14:55.747

3ooh these pictures are the prettiest – sevenseacat – 2014-02-27T08:57:07.303

37

Processing

I'm just getting started with C (having programmed in other languages) but found the graphics in Visual C tough to follow, so I downloaded this Processing program used by @ace.

Here's my code and my algorithm.

void setup(){
  size(256,128);
  background(0);
  frameRate(1000000000);
  noLoop();
 }

int x,y,r,g,b,c;
void draw() {
  for(y=0;y<128;y++)for(x=0;x<128;x++){
    r=(x&3)+(y&3)*4;
    g=x>>2;
    b=y>>2;
    c=0;
    //c=x*x+y*y<10000? 1:0; 
    stroke((r^16*c)<<3,g<<3,b<<3);
    point(x,y);
    stroke((r^16*(1-c))<<3,g<<3,b<<3);
    point(255-x,y);  
  } 
}

Algorithm

Start with 4x4 squares of all possible combinations of 32 values of green and blue, in x,y. format, making a 128x128 square Each 4x4 square has 16 pixels, so make a mirror image beside it to give 32 pixels of each possible combination of green and blue, per image below.

(bizarrely the full green looks brighter than the full cyan. This must be an optical illusion. clarified in comments)

In the lefthand square, add in the red values 0-15. For the righthand square, XOR these values with 16, to make the values 16-31.

enter image description here

Output 256x128

This gives the output in the top image below.

However, every pixel differs from its mirror image only in the most significant bit of the red value. So, I can apply a condition with the variable c, to reverse the XOR, which has the same effect as exchanging these two pixels.

An example of this is given in the bottom image below (if we uncomment the line of code which is currently commented out.)

enter image description here

512 x 512 - A tribute to Andy Warhol's Marylin

Inspired by Quincunx's answer to this question with an "evil grin" in freehand red circles, here is my version of the famous picture. The original actually had 25 coloured Marylins and 25 black & white Marylins and was Warhol's tribute to Marylin after her untimely death. See http://en.wikipedia.org/wiki/Marilyn_Diptych

I changed to different functions after discovering that Processing renders the ones I used in 256x128 as semitransparent. The new ones are opaque.

And although the image isn't completely algorithmic, I rather like it.

int x,y,r,g,b,c;
PImage img;
color p;
void setup(){
  size(512,512);
  background(0);
  img = loadImage("marylin256.png");
  frameRate(1000000000);
  noLoop();
 }

void draw() {

   image(img,0,0);

   for(y=0;y<256;y++)for(x=0;x<256;x++){
      // Note the multiplication by 0 in the next line. 
      // Replace the 0 with an 8 and the reds are blended checkerboard style
      // This reduces the grain size, but on balance I decided I like the grain.
      r=((x&3)+(y&3)*4)^0*((x&1)^(y&1));
      g=x>>2;
      b=y>>2; 
      c=brightness(get(x,y))>100? 32:0;
      p=color((r^c)<<2,g<<2,b<<2);
      set(x,y,p);
      p=color((r^16^c)<<2,g<<2,b<<2);
      set(256+x,y,p);  
      p=color((r^32^c)<<2,g<<2,b<<2);
      set(x,256+y,p);
      p=color((r^48^c)<<2,g<<2,b<<2);
      set(256+x,256+y,p);  
 } 
 save("warholmarylin.png");

}

enter image description here

512x512 Twilight over a lake with mountains in the distance

Here, a fully algorithmic picture. I've played around with changing which colour I modulate with the condition, but I just come back to the conclusion that red works best. Similar to the Marylin picture, I draw the mountains first, then pick the brightness from that picture to overwrite the positive RGB image, while copying to the negative half. A slight difference is that the bottom of many of the mountains (because they are all drawn the same size) extends below the read area, so this area is simply cropped during the reading process (which therefore gives the desired impression of different size mountains.)

In this one I use an 8x4 cell of 32 reds for the positive, and the remaining 32 reds for the negative.

Note the expicit command frameRate(1) at the end of my code. I discovered that without this command, Processing would use 100% of one core of my CPU, even though it had finished drawing. As far as I can tell there is no Sleep function, all you can do is reduce the frequency of polling.

int i,j,x,y,r,g,b,c;
PImage img;
color p;
void setup(){
  size(512,512);
  background(255,255,255);
  frameRate(1000000000);
  noLoop();
 }

void draw() {
  for(i=0; i<40; i++){
    x=round(random(512));
    y=round(random(64,256));
    for(j=-256; j<256; j+=12) line(x,y,x+j,y+256);  
  }
  for(y=0;y<256;y++)for(x=0;x<512;x++){
    r=(x&7)+(y&3)*8;
    b=x>>3;
    g=(255-y)>>2;
    c=brightness(get(x,y))>100? 32:0;
    p=color((r^c)<<2,g<<2,b<<2);
    set(x,y,p);
    p=color((r^32^c)<<2,g<<2,b<<2);
    set(x,511-y,p);  
  }
  save("mountainK.png");
  frameRate(1);
}

enter image description here

Level River St

Posted 2014-02-25T21:16:53.557

Reputation: 22 049

Because its not full cyan at all. It's (0,217,217). All 32 combinations are present though, just not stretched [0,255]. Edit: You're using steps of 7 but I can't find this in the code. Must be a Processing thing. – Mark Jeronimus – 2014-02-27T06:20:29.383

@steveverrill In Processing, you can do save("filename.png") to save the current frame buffer to an image. Other image formats are also supported. It'll save you the trouble of taking screenshots. The image is saved to the sketch's folder.

– Jason C – 2014-02-27T07:56:25.343

@Jasonc thanks for the tip, I was sure there must be a way, but I don't think I'll edit these. I left the frame around the images partially to separate them (2 files for such small images was overkill.) I want to do some images in 512x512 (and there's one in particular I have an idea for) so I will upload those in the way you suggest. – Level River St – 2014-02-27T11:10:32.047

1@steveverrill Haha, the Warhols are a nice touch. – Jason C – 2014-02-27T22:52:19.597

@Zom-B Processing seems to do many things that (annoyingly) aren't mentioned in its documentation: not using the full 256 logical colour channel values in its physical output, blending colours when you don't want, using a full core of my CPU even after it's finished drawing. Still it's simple to get into and you can work around these issues once you know they are there (except the first one, I haven't solved that yet...) – Level River St – 2014-03-02T14:27:09.547

Something about this color scheme is playing tricks on my eyes. Like my brain is fighting over which color is part of the foreground. I quite like all of these! – Jordan P – 2014-03-05T05:31:51.667

The Marilyns are great...reminiscent of film photos with light-leaking Holga cameras. – Jonathan Van Matre – 2014-03-08T22:00:01.330

35

C#: Iterative local similarity optimization

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace AllColors
{
    class Program
    {
        static Random _random = new Random();

        const int ImageWidth = 256;
        const int ImageHeight = 128;
        const int PixelCount = ImageWidth * ImageHeight;
        const int ValuesPerChannel = 32;
        const int ChannelValueDelta = 256 / ValuesPerChannel;

        static readonly int[,] Kernel;
        static readonly int KernelWidth;
        static readonly int KernelHeight;

        static Program()
        {
            // Version 1
            Kernel = new int[,] { { 0, 1, 0, },
                                  { 1, 0, 1, },
                                  { 0, 1, 0, } };
            // Version 2
            //Kernel = new int[,] { { 0, 0, 1, 0, 0 },
            //                      { 0, 2, 3, 2, 0 },
            //                      { 1, 3, 0, 3, 1 },
            //                      { 0, 2, 3, 2, 0 },
            //                      { 0, 0, 1, 0, 0 } };
            // Version 3
            //Kernel = new int[,] { { 3, 0, 0, 0, 3 },
            //                      { 0, 1, 0, 1, 0 },
            //                      { 0, 0, 0, 0, 0 },
            //                      { 0, 1, 0, 1, 0 },
            //                      { 3, 0, 0, 0, 3 } };
            // Version 4
            //Kernel = new int[,] { { -9, -9, -9, -9, -9 },
            //                      {  1,  2,  3,  2,  1 },
            //                      {  2,  3,  0,  3,  2 },
            //                      {  1,  2,  3,  2,  1 },
            //                      {  0,  0,  0,  0,  0 } };
            // Version 5
            //Kernel = new int[,] { { 0, 0, 1, 0, 0, 0, 0 },
            //                      { 0, 1, 2, 1, 0, 0, 0 },
            //                      { 1, 2, 3, 0, 1, 0, 0 },
            //                      { 0, 1, 2, 0, 0, 0, 0 },
            //                      { 0, 0, 1, 0, 0, 0, 0 } };
            KernelWidth = Kernel.GetLength(1);
            KernelHeight = Kernel.GetLength(0);

            if (KernelWidth % 2 == 0 || KernelHeight % 2 == 0)
            {
                throw new InvalidOperationException("Invalid kernel size");
            }
        }

        private static Color[] CreateAllColors()
        {
            int i = 0;
            Color[] colors = new Color[PixelCount];
            for (int r = 0; r < ValuesPerChannel; r++)
            {
                for (int g = 0; g < ValuesPerChannel; g++)
                {
                    for (int b = 0; b < ValuesPerChannel; b++)
                    {
                        colors[i] = Color.FromArgb(255, r * ChannelValueDelta, g * ChannelValueDelta, b * ChannelValueDelta);
                        i++;
                    }
                }
            }
            return colors;
        }

        private static void Shuffle(Color[] colors)
        {
            // Knuth-Fisher-Yates shuffle
            for (int i = colors.Length - 1; i > 0; i--)
            {
                int n = _random.Next(i + 1);
                Swap(colors, i, n);
            }
        }

        private static void Swap(Color[] colors, int index1, int index2)
        {
            var temp = colors[index1];
            colors[index1] = colors[index2];
            colors[index2] = temp;
        }

        private static Bitmap ToBitmap(Color[] pixels)
        {
            Bitmap bitmap = new Bitmap(ImageWidth, ImageHeight);
            int x = 0;
            int y = 0;
            for (int i = 0; i < PixelCount; i++)
            {
                bitmap.SetPixel(x, y, pixels[i]);
                x++;
                if (x == ImageWidth)
                {
                    x = 0;
                    y++;
                }
            }
            return bitmap;
        }

        private static int GetNeighborDelta(Color[] pixels, int index1, int index2)
        {
            return GetNeighborDelta(pixels, index1) + GetNeighborDelta(pixels, index2);
        }

        private static int GetNeighborDelta(Color[] pixels, int index)
        {
            Color center = pixels[index];
            int sum = 0;
            for (int x = 0; x < KernelWidth; x++)
            {
                for (int y = 0; y < KernelHeight; y++)
                {
                    int weight = Kernel[y, x];
                    if (weight == 0)
                    {
                        continue;
                    }

                    int xOffset = x - (KernelWidth / 2);
                    int yOffset = y - (KernelHeight / 2);
                    int i = index + xOffset + yOffset * ImageWidth;

                    if (i >= 0 && i < PixelCount)
                    {
                        sum += GetDelta(pixels[i], center) * weight;
                    }
                }
            }

            return sum;
        }

        private static int GetDelta(Color c1, Color c2)
        {
            int sum = 0;
            sum += Math.Abs(c1.R - c2.R);
            sum += Math.Abs(c1.G - c2.G);
            sum += Math.Abs(c1.B - c2.B);
            return sum;
        }

        private static bool TryRandomSwap(Color[] pixels)
        {
            int index1 = _random.Next(PixelCount);
            int index2 = _random.Next(PixelCount);

            int delta = GetNeighborDelta(pixels, index1, index2);
            Swap(pixels, index1, index2);
            int newDelta = GetNeighborDelta(pixels, index1, index2);

            if (newDelta < delta)
            {
                return true;
            }
            else
            {
                // Swap back
                Swap(pixels, index1, index2);
                return false;
            }
        }

        static void Main(string[] args)
        {
            string fileNameFormat = "{0:D10}.png";
            var image = CreateAllColors();
            ToBitmap(image).Save("start.png");
            Shuffle(image);
            ToBitmap(image).Save(string.Format(fileNameFormat, 0));

            long generation = 0;
            while (true)
            {
                bool swapped = TryRandomSwap(image);
                if (swapped)
                {
                    generation++;
                    if (generation % 1000 == 0)
                    {
                        ToBitmap(image).Save(string.Format(fileNameFormat, generation));
                    }
                }
            }
        }
    }
}

Idea

First we start with a random shuffle:

enter image description here

Then we randomly select two pixels and swap them. If this does not increase the similarity of the pixels to their neighbors, we swap back and try again. We repeat this process over and over again.

After just a few generations (5000) the differences are not that obvious...

enter image description here

But the longer it runs (25000), ...

enter image description here

...the more certain patterns start to emerge (100000).

enter image description here

Using different definitions for neighborhood, we can influence these patterns and whether they are stable or not. The Kernel is a matrix similar to the ones used for filters in image processing. It specifies the weights of each neighbor used for the RGB delta calculation.

Results

Here are some of the results I created. The videos show the iterative process (1 frame == 1000 generations), but sadly the quality is not the best (vimeo, YouTube etc. do not properly support such small dimensions). I may later try to create videos of better quality.

0 1 0
1 X 1
0 1 0

185000 generations:

enter image description here Video (00:06)


0 0 1 0 0
0 2 3 2 0
1 3 X 3 1
0 2 3 2 0
0 0 1 0 0

243000 generations:

enter image description here Video (00:07)


3 0 0 0 3
0 1 0 1 0
0 0 X 0 0
0 1 0 1 0
3 0 0 0 3

230000 generations:

enter image description here Video (00:07)


0 0 1 0 0 0 0
0 1 2 1 0 0 0
1 2 3 X 1 0 0
0 1 2 0 0 0 0
0 0 1 0 0 0 0

This kernel is interesting because due to its asymmetry the patterns are not stable and the whole image moves to the right as the generations go by.

2331000 generations:

enter image description here Video (01:10)


Large Results (512x512)

Using the kernels above with a larger image dimension creates the same local patterns, spaning a larger total area. A 512x512 image takes between 1 and 2 million generations to stabilize.

enter image description here enter image description here enter image description here


OK, now let's get serious and create larger, less local patterns with a 15x15 radial kernel:

0 0 0 0 0 1 1 1 1 1 0 0 0 0 0
0 0 0 1 1 2 2 2 2 2 1 1 0 0 0
0 0 1 2 2 3 3 3 3 3 2 2 1 0 0
0 1 2 2 3 4 4 4 4 4 3 2 2 1 0
0 1 2 3 4 4 5 5 5 4 4 3 2 1 0
1 2 3 4 4 5 6 6 6 5 4 4 3 2 1
1 2 3 4 5 6 7 7 7 6 5 4 3 2 1
1 2 3 4 5 6 7 X 7 6 5 4 3 2 1
1 2 3 4 5 6 7 7 7 6 5 4 3 2 1
1 2 3 4 4 5 6 6 6 5 4 4 3 2 1
0 1 2 3 4 4 5 5 5 4 4 3 2 1 0
0 1 2 2 3 4 4 4 4 4 3 2 2 1 0
0 0 1 2 2 3 3 3 3 3 2 2 1 0 0
0 0 0 1 1 2 2 2 2 2 1 1 0 0 0
0 0 0 0 0 1 1 1 1 1 0 0 0 0 0

This drastically increases the computation time per generation. 1.71 million generations and 20 hours later:

enter image description here

Sebastian Negraszus

Posted 2014-02-25T21:16:53.557

Reputation: 1 471

Interesting coincidence, I have an article on this same topic: https://www.nayuki.io/page/simulated-annealing-demo

– Nayuki – 2016-11-21T21:10:19.660

1Takes a while to get there, but the end result is quite nice. – primo – 2014-03-04T00:38:50.957

35

I just arranged all 16-bit colors (5r,6g,5b) on a Hilbert curve in JavaScript.

hilbert curve colors

Previous, (not Hilbert curve) image:

hilbert curve

JSfiddle: jsfiddle.net/LCsLQ/3

JavaScript

// ported code from http://en.wikipedia.org/wiki/Hilbert_curve
function xy2d (n, p) {
    p = {x: p.x, y: p.y};
    var r = {x: 0, y: 0},
        s,
        d=0;
    for (s=(n/2)|0; s>0; s=(s/2)|0) {
        r.x = (p.x & s) > 0 ? 1 : 0;
        r.y = (p.y & s) > 0 ? 1 : 0;
        d += s * s * ((3 * r.x) ^ r.y);
        rot(s, p, r);
    }
    return d;
}

//convert d to (x,y)
function d2xy(n, d) {
    var r = {x: 0, y: 0},
        p = {x: 0, y: 0},
        s,
        t=d;
    for (s=1; s<n; s*=2) {
        r.x = 1 & (t/2);
        r.y = 1 & (t ^ rx);
        rot(s, p, r);
        p.x += s * r.x;
        p.y += s * r.y;
        t /= 4;
    }
    return p;
}

//rotate/flip a quadrant appropriately
function rot(n, p, r) {
    if (r.y === 0) {
        if (r.x === 1) {
            p.x = n-1 - p.x;
            p.y = n-1 - p.y;
        }

        //Swap x and y
        var t  = p.x;
        p.x = p.y;
        p.y = t;
    }
}
function v2rgb(v) {
    return ((v & 0xf800) << 8) | ((v & 0x7e0) << 5) | ((v & 0x1f) << 3); 
}
function putData(arr, size, coord, v) {
    var pos = (coord.x + size * coord.y) * 4,
        rgb = v2rgb(v);

    arr[pos] = (rgb & 0xff0000) >> 16;
    arr[pos + 1] = (rgb & 0xff00) >> 8;
    arr[pos + 2] = rgb & 0xff;
    arr[pos + 3] = 0xff;
}
var size = 256,
    context = a.getContext('2d'),
    data = context.getImageData(0, 0, size, size);

for (var i = 0; i < size; i++) {
    for (var j = 0; j < size; j++) {
        var p = {x: j, y: i};
        putData(data.data, size, p, xy2d(size, p));
    }
}
context.putImageData(data, 0, 0);

Edit: It turns out that there was a bug in my function to calculate Hilbert curve and it was incorrect; namely, r.x = (p.x & s) > 0; r.y = (p.y & s) > 0; changed to r.x = (p.x & s) > 0 ? 1 : 0; r.y = (p.y & s) > 0 ? 1 : 0;

Edit 2: Another fractal:

sierpinsky

http://jsfiddle.net/jej2d/5/

m1el

Posted 2014-02-25T21:16:53.557

Reputation: 461

Nice! Welcome to PPCG. – Jonathan Van Matre – 2014-03-03T23:13:23.413

What does it look like when the walk through the color cube also a 3D Hilbert curve? [Edit] nm. someone did just that. – Mark Jeronimus – 2014-03-04T08:19:57.713

30

Java

With a few variations on my other answer, we can get some very interesting outputs.

import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;

/**
 *
 * @author Quincunx
 */
public class AllColorImage {

    public static void main(String[] args) {
        BufferedImage img = new BufferedImage(4096, 4096, BufferedImage.TYPE_INT_RGB);

        int num = 0;
        ArrayList<Point> points = new ArrayList<>();
        for (int y = 0; y < 4096; y++) {
            for (int x = 0; x < 4096; x++) {
                points.add(new Point(x, y));
            }
        }
        Collections.sort(points, new Comparator<Point>() {

            @Override
            public int compare(Point t, Point t1) {
                int compareVal = (Integer.bitCount(t.x) + Integer.bitCount(t.y))
                        - (Integer.bitCount(t1.x) + Integer.bitCount(t1.y));
                return compareVal < 0 ? -1 : compareVal == 0 ? 0 : 1;
            }

        });
        for (Point p : points) {
            int x = p.x;
            int y = p.y;

            img.setRGB(x, y, num);
            num++;
        }
        try {
            ImageIO.write(img, "png", new File("Filepath"));
        } catch (IOException ex) {
            Logger.getLogger(AllColorImage.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

The important code is here:

Collections.sort(points, new Comparator<Point>() {

    @Override
    public int compare(Point t, Point t1) {
        int compareVal = (Integer.bitCount(t.x) + Integer.bitCount(t.y))
                - (Integer.bitCount(t1.x) + Integer.bitCount(t1.y));
        return compareVal < 0 ? -1 : compareVal == 0 ? 0 : 1;
    }

});

Output (screenshot):

enter image description here

Change the comparator to this:

public int compare(Point t, Point t1) {
    int compareVal = (Integer.bitCount(t.x + t.y))
            - (Integer.bitCount(t1.x + t1.y));
    return compareVal < 0 ? -1 : compareVal == 0 ? 0 : 1;
}

And we get this:

enter image description here

Another variation:

public int compare(Point t, Point t1) {
    int compareVal = (t.x + t.y)
            - (t1.x + t1.y);
    return compareVal < 0 ? -1 : compareVal == 0 ? 0 : 1;
}

enter image description here

Yet another variation (reminds me of cellular automata):

public int compare(Point t, Point t1) {
    int compareVal = (t1.x - t.y)
            + (t.x - t1.y);
    return compareVal < 0 ? -1 : compareVal == 0 ? 0 : 1;
}

enter image description here

Yet another another variation (new personal favorite):

public int compare(Point t, Point t1) {
    int compareVal = (Integer.bitCount(t.x ^ t.y))
            - (Integer.bitCount(t1.x ^ t1.y));
    return compareVal < 0 ? -1 : compareVal == 0 ? 0 : 1;
}

enter image description here

It looks so fractal-ly. XOR is so beautiful, especially closeup:

enter image description here

Another closeup:

enter image description here

And now the Sierpinski Triangle, tilted:

public int compare(Point t, Point t1) {
    int compareVal = (Integer.bitCount(t.x | t.y))
            - (Integer.bitCount(t1.x | t1.y));
    return compareVal < 0 ? -1 : compareVal == 0 ? 0 : 1;
}

enter image description here

Justin

Posted 2014-02-25T21:16:53.557

Reputation: 19 757

These are really cool; they kinda remind me of a broken arcade game or an nes. – Jason C – 2016-10-13T19:41:31.670

8First image looks like a CPU or memory die photo – Nick T – 2014-02-27T00:20:49.340

@NickT Here is a memory die (according to Google Images) for contrast: http://files.macbidouille.com/mbv2/news/news_05_10/25-nm-die.jpg

– Justin – 2014-02-27T00:46:04.703

4

Right, memory is so formless...probably a very multi-core processor then: http://www.extremetech.com/wp-content/uploads/2012/07/Aubrey_Isle_die.jpg

– Nick T – 2014-02-27T00:54:45.097

1I really like these latter ones. Very glitchy-looking but with an underlying organizing structure. I want a rug woven like that XOR design! – Jonathan Van Matre – 2014-03-08T21:57:40.447

29

Java

I wasn't actually sure how to create 15- or 18-bit colors, so I just left off the least significant bit of each channel's byte to make 2^18 different 24-bit colors. Most of the noise is removed by sorting, but effective noise removal looks like it would require comparison of more than just two elements at a time the way Comparator does. I'll try manipulation using larger kernels, but in the mean time, this is about the best I've been able to do.

enter image description here

Click for HD image #2

Low resolution image #2

import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import java.util.*;

public class ColorSpan extends JFrame{
    private int h, w = h = 512;
    private BufferedImage image = 
            new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB);
    private WritableRaster raster = image.getRaster();
    private DataBufferInt dbInt = (DataBufferInt) 
            (raster.getDataBuffer());
    private int[] data = dbInt.getData();

    private JLabel imageLabel = new JLabel(new ImageIcon(image));
    private JPanel bordered = new JPanel(new BorderLayout());


    public <T> void transpose(ArrayList<T> objects){
        for(int i = 0; i < w; i++){
            for(int j = 0; j < i; j++){
                Collections.swap(objects,i+j*w,j+i*h);
            }
        }
    }

    public <T> void sortByLine(ArrayList<T> objects, Comparator<T> comp){
        for(int i = 0; i < h; i++){
            Collections.sort(objects.subList(i*w, (i+1)*w), comp);
        }
    }

    public void init(){
        ArrayList<Integer> colors = new ArrayList<Integer>();
        for(int i = 0, max = 1<<18; i < max; i++){
            int r = i>>12, g = (i>>6)&63, b = i&63;
            colors.add(((r<<16)+(g<<8)+b)<<2);
        }

        Comparator<Integer> comp1 = new Comparator<Integer>(){
            public int compare(Integer left, Integer right){
                int a = left.intValue(), b = right.intValue();

                int rA = a>>16, rB = b>>16,
                    gA = (a>>8)&255, gB = (b>>8)&255;
                /*double thA = Math.acos(gA*2d/255-1),
                        thB = Math.acos(gB*2d/255-1);*/
                double thA = Math.atan2(rA/255d-.5,gA/255d-.5),
                        thB = Math.atan2(rB/255d-.5,gB/255d-.5);
                return -Double.compare(thA,thB);
            }
        }, comp2 = new Comparator<Integer>(){
            public int compare(Integer left, Integer right){
                int a = left.intValue(), b = right.intValue();

                int rA = a>>16, rB = b>>16,
                    gA = (a>>8)&255, gB = (b>>8)&255,
                    bA = a&255, bB = b&255;
                double dA = Math.hypot(gA-rA,bA-rA),
                        dB = Math.hypot(gB-rB,bB-rB);
                return Double.compare(dA,dB);
            }
        }, comp3 = new Comparator<Integer>(){
            public int compare(Integer left, Integer right){
                int a = left.intValue(), b = right.intValue();

                int rA = a>>16, rB = b>>16,
                    gA = (a>>8)&255, gB = (b>>8)&255,
                    bA = a&255, bB = b&255;

                    return Integer.compare(rA+gA+bA,rB+gB+bB);
            }
        };

        /* Start: Image 1 */
        Collections.sort(colors, comp2);
        transpose(colors);
        sortByLine(colors,comp2);
        transpose(colors);
        sortByLine(colors,comp1);
        transpose(colors);
        sortByLine(colors,comp2);
        sortByLine(colors,comp3);
        /* End: Image 1 */

        /* Start: Image 2 */
        Collections.sort(colors, comp1);
        sortByLine(colors,comp2);

        transpose(colors);
        sortByLine(colors,comp2);
        transpose(colors);
        sortByLine(colors,comp1);
        transpose(colors);
        sortByLine(colors,comp1);
        /* End: Image 2 */

        int index = 0;
        for(Integer color : colors){
            int cInt = color.intValue();
            data[index] = cInt;
            index++;
        }

    }

    public ColorSpan(){
        super("512x512 Unique Colors");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        init();

        bordered.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
        bordered.add(imageLabel,BorderLayout.CENTER);
        add(bordered,BorderLayout.CENTER);
        pack();

    }

    public static void main(String[] args){
        new ColorSpan().setVisible(true);
    }
}

John P

Posted 2014-02-25T21:16:53.557

Reputation: 401

2There's a problem with the download. – SuperJedi224 – 2015-08-02T20:56:22.860

1That second one really deserves to have a 4096 x 4096 24bit version... – trichoplax – 2014-04-15T20:32:53.430

Imgur has been processing the image for about a half an hour. I guess it's probably trying to compress it. Anyway, I added a link: https://SSend.it/hj4ovh

– John P – 2014-05-03T16:51:55.973

28

Scala

I order all of the colors by walking a 3-dimensional Hilbert Curve via an L-System. I then walk the pixels in the output image along a 2-dimensional Hilbert Curve and lay out all of the colors.

512 x 512 output:

enter image description here

Here's the code. Most of it covers just the logic and math of moving through three dimensions via pitch/roll/yaw. I'm sure there was a better way to do that part, but oh well.

import scala.annotation.tailrec
import java.awt.image.BufferedImage
import javax.imageio.ImageIO
import java.io.File

object AllColors {

  case class Vector(val x: Int, val y: Int, val z: Int) {
    def applyTransformation(m: Matrix): Vector = {
      Vector(m.r1.x * x + m.r1.y * y + m.r1.z * z, m.r2.x * x + m.r2.y * y + m.r2.z * z, m.r3.x * x + m.r3.y * y + m.r3.z * z)
    }
    def +(v: Vector): Vector = {
      Vector(x + v.x, y + v.y, z + v.z)
    }
    def unary_-(): Vector = Vector(-x, -y, -z)
  }

  case class Heading(d: Vector, s: Vector) {
    def roll(positive: Boolean): Heading = {
      val (axis, b) = getAxis(d)
      Heading(d, s.applyTransformation(rotationAbout(axis, !(positive ^ b))))
    }

    def yaw(positive: Boolean): Heading = {
      val (axis, b) = getAxis(s)
      Heading(d.applyTransformation(rotationAbout(axis, positive ^ b)), s)
    }

    def pitch(positive: Boolean): Heading = {
      if (positive) {
        Heading(s, -d)
      } else {
        Heading(-s, d)
      }
    }

    def applyCommand(c: Char): Heading = c match {
      case '+' => yaw(true)
      case '-' => yaw(false)
      case '^' => pitch(true)
      case 'v' => pitch(false)
      case '>' => roll(true)
      case '<' => roll(false)
    }
  }

  def getAxis(v: Vector): (Char, Boolean) = v match {
    case Vector(1, 0, 0) => ('x', true)
    case Vector(-1, 0, 0) => ('x', false)
    case Vector(0, 1, 0) => ('y', true)
    case Vector(0, -1, 0) => ('y', false)
    case Vector(0, 0, 1) => ('z', true)
    case Vector(0, 0, -1) => ('z', false)
  }

  def rotationAbout(axis: Char, positive: Boolean) = (axis, positive) match {
    case ('x', true) => XP
    case ('x', false) => XN
    case ('y', true) => YP
    case ('y', false) => YN
    case ('z', true) => ZP
    case ('z', false) => ZN
  }

  case class Matrix(val r1: Vector, val r2: Vector, val r3: Vector)

  val ZP = Matrix(Vector(0,-1,0),Vector(1,0,0),Vector(0,0,1))
  val ZN = Matrix(Vector(0,1,0),Vector(-1,0,0),Vector(0,0,1))

  val XP = Matrix(Vector(1,0,0),Vector(0,0,-1),Vector(0,1,0))
  val XN = Matrix(Vector(1,0,0),Vector(0,0,1),Vector(0,-1,0))

  val YP = Matrix(Vector(0,0,1),Vector(0,1,0),Vector(-1,0,0))
  val YN = Matrix(Vector(0,0,-1),Vector(0,1,0),Vector(1,0,0))

  @tailrec def applyLSystem(current: Stream[Char], rules: Map[Char, List[Char]], iterations: Int): Stream[Char] = {
    if (iterations == 0) {
      current
    } else {
      val nextStep = current flatMap { c => rules.getOrElse(c, List(c)) }
      applyLSystem(nextStep, rules, iterations - 1)
    }
  }

  def walk(x: Vector, h: Heading, steps: Stream[Char]): Stream[Vector] = steps match {
    case Stream() => Stream(x)
    case 'f' #:: rest => x #:: walk(x + h.d, h, rest)
    case c #:: rest => walk(x, h.applyCommand(c), rest)
  }

  def hilbert3d(n: Int): Stream[Vector] = {
    val rules = Map('x' -> "^>x<f+>>x<<f>>x<<+fvxfxvf+>>x<<f>>x<<+f>x<^".toList)
    val steps = applyLSystem(Stream('x'), rules, n) filterNot (_ == 'x')
    walk(Vector(0, 0, 0), Heading(Vector(1, 0, 0), Vector(0, 1, 0)), steps)
  }

  def hilbert2d(n: Int): Stream[Vector] = {
    val rules = Map('a' -> "-bf+afa+fb-".toList, 'b' -> "+af-bfb-fa+".toList)
    val steps = applyLSystem(Stream('a'), rules, n) filterNot (c => c == 'a' || c == 'b')
    walk(Vector(0, 0, 0), Heading(Vector(1, 0, 0), Vector(0, 0, 1)), steps)
  }

  def main(args: Array[String]): Unit = {
    val n = 4
    val img = new BufferedImage(1 << (3 * n), 1 << (3 * n), BufferedImage.TYPE_INT_RGB)
    hilbert3d(n * 2).zip(hilbert2d(n * 3)) foreach { case (Vector(r,g,b), Vector(x,y,_)) => img.setRGB(x, y, (r << (24 - 2 * n)) | (g << (16 - 2 * n)) | (b << (8 - 2 * n))) }
    ImageIO.write(img, "png", new File(s"out_$n.png"))
  }
}

Joe K

Posted 2014-02-25T21:16:53.557

Reputation: 1 065

28

C#

Wow, really cool things in this challenge. I took a stab at this at in C# and generated a 4096x4096 image in about 3 minutes (i7 CPU) using every single color via Random Walk logic.

Ok, so for the code. After being frustrated with hours of research and trying to generate every single HSL color using for loops in code, I settled for creating a flat file to read HSL colors from. What I did was create every single RGB color into a List, then I ordered by Hue, Luminosity, then Saturation. Then I saved the List to a text file. ColorData is just a small class I wrote that accepts an RGB color and also stores the HSL equivalent. This code is a HUGE RAM eater. Used about 4GB RAM lol.

public class RGB
{
    public double R = 0;
    public double G = 0;
    public double B = 0;
    public override string ToString()
    {
        return "RGB:{" + (int)R + "," + (int)G + "," + (int)B + "}";
    }
}
public class HSL
{
    public double H = 0;
    public double S = 0;
    public double L = 0;
    public override string ToString()
    {
        return "HSL:{" + H + "," + S + "," + L + "}";
    }
}
public class ColorData
{
    public RGB rgb;
    public HSL hsl;
    public ColorData(RGB _rgb)
    {
        rgb = _rgb;
        var _hsl = ColorHelper._color_rgb2hsl(new double[]{rgb.R,rgb.G,rgb.B});
        hsl = new HSL() { H = _hsl[0], S = _hsl[1], L = _hsl[2] };
    }
    public ColorData(double[] _rgb)
    {
        rgb = new RGB() { R = _rgb[0], G = _rgb[1], B = _rgb[2] };
        var _hsl = ColorHelper._color_rgb2hsl(_rgb);
        hsl = new HSL() { H = _hsl[0], S = _hsl[1], L = _hsl[2] };
    }
    public override string ToString()
    {
        return rgb.ToString() + "|" + hsl.ToString();
    }
    public int Compare(ColorData cd)
    {
        if (this.hsl.H > cd.hsl.H)
        {
            return 1;
        }
        if (this.hsl.H < cd.hsl.H)
        {
            return -1;
        }

        if (this.hsl.S > cd.hsl.S)
        {
            return 1;
        }
        if (this.hsl.S < cd.hsl.S)
        {
            return -1;
        }

        if (this.hsl.L > cd.hsl.L)
        {
            return 1;
        }
        if (this.hsl.L < cd.hsl.L)
        {
            return -1;
        }
        return 0;
    }
}
public static class ColorHelper
{


    public static void MakeColorFile(string savePath)
    {
        List<ColorData> Colors = new List<ColorData>();
        System.IO.File.Delete(savePath);

        for (int r = 0; r < 256; r++)
        {
            for (int g = 0; g < 256; g++)
            {
                for (int b = 0; b < 256; b++)
                {
                    double[] rgb = new double[] { r, g, b };
                    ColorData cd = new ColorData(rgb);
                    Colors.Add(cd);
                }
            }
        }
        Colors = Colors.OrderBy(x => x.hsl.H).ThenBy(x => x.hsl.L).ThenBy(x => x.hsl.S).ToList();

        string cS = "";
        using (System.IO.StreamWriter fs = new System.IO.StreamWriter(savePath))
        {

            foreach (var cd in Colors)
            {
                cS = cd.ToString();
                fs.WriteLine(cS);
            }
        }
    }


    public static IEnumerable<Color> NextColorHThenSThenL()
    {
        HashSet<string> used = new HashSet<string>();
        double rMax = 720;
        double gMax = 700;
        double bMax = 700;
        for (double r = 0; r <= rMax; r++)
        {
            for (double g = 0; g <= gMax; g++)
            {
                for (double b = 0; b <= bMax; b++)
                {
                    double h = (r / (double)rMax);
                    double s = (g / (double)gMax);
                    double l = (b / (double)bMax);
                    var c = _color_hsl2rgb(new double[] { h, s, l });
                    Color col = Color.FromArgb((int)c[0], (int)c[1], (int)c[2]);
                    string key = col.R + "-" + col.G + "-" + col.B;
                    if (!used.Contains(key))
                    {
                        used.Add(key);
                        yield return col;
                    }
                    else
                    {
                        continue;
                    }
                }
            }
        }
    }

    public static Color HSL2RGB(double h, double s, double l){
        double[] rgb= _color_hsl2rgb(new double[] { h, s, l });
        return Color.FromArgb((int)rgb[0], (int)rgb[1], (int)rgb[2]);
    }
    public static double[] _color_rgb2hsl(double[] rgb)
    {
        double r = rgb[0]; double g = rgb[1]; double b = rgb[2];
        double min = Math.Min(r, Math.Min(g, b));
        double max = Math.Max(r, Math.Max(g, b));
        double delta = max - min;
        double l = (min + max) / 2.0;
        double s = 0;
        if (l > 0 && l < 1)
        {
            s = delta / (l < 0.5 ? (2 * l) : (2 - 2 * l));
        }
        double h = 0;
        if (delta > 0)
        {
            if (max == r && max != g) h += (g - b) / delta;
            if (max == g && max != b) h += (2 + (b - r) / delta);
            if (max == b && max != r) h += (4 + (r - g) / delta);
            h /= 6;
        } return new double[] { h, s, l };
    }


    public static double[] _color_hsl2rgb(double[] hsl)
    {
        double h = hsl[0];
        double s = hsl[1];
        double l = hsl[2];
        double m2 = (l <= 0.5) ? l * (s + 1) : l + s - l * s;
        double m1 = l * 2 - m2;
        return new double[]{255*_color_hue2rgb(m1, m2, h + 0.33333),
           255*_color_hue2rgb(m1, m2, h),
           255*_color_hue2rgb(m1, m2, h - 0.33333)};
    }


    public static double _color_hue2rgb(double m1, double m2, double h)
    {
        h = (h < 0) ? h + 1 : ((h > 1) ? h - 1 : h);
        if (h * (double)6 < 1) return m1 + (m2 - m1) * h * (double)6;
        if (h * (double)2 < 1) return m2;
        if (h * (double)3 < 2) return m1 + (m2 - m1) * (0.66666 - h) * (double)6;
        return m1;
    }


}

With that out of the way. I wrote a class to get the next color from the generated file. It lets you set the hue start and hue end. In reality, that could and should probably be generalized to whichever dimension the file was sorted by first. Also I realize that for a performance boost here, I could have just put the RGB values into the file and kept each line at a fixed length. That way I could have easily specified the byte offset instead of looping through every line until I reached the line I wanted to start at. But it wasn't that much of a performance hit for me. But here's that class

public class HSLGenerator
{

    double hEnd = 1;
    double hStart = 0;

    double colCount = 256 * 256 * 256;

    public static Color ReadRGBColorFromLine(string line)
    {
        string sp1 = line.Split(new string[] { "RGB:{" }, StringSplitOptions.None)[1];
        string sp2 = sp1.Split('}')[0];
        string[] sp3 = sp2.Split(',');
        return Color.FromArgb(Convert.ToInt32(sp3[0]), Convert.ToInt32(sp3[1]), Convert.ToInt32(sp3[2]));
    }
    public IEnumerable<Color> GetNextFromFile(string colorFile)
    {
        int currentLine = -1;
        int startLine = Convert.ToInt32(hStart * colCount);
        int endLine = Convert.ToInt32(hEnd * colCount);
        string line = "";
        using(System.IO.StreamReader sr = new System.IO.StreamReader(colorFile))
        {

            while (!sr.EndOfStream)
            {
                line = sr.ReadLine();
                currentLine++;
                if (currentLine < startLine) //begin at correct offset
                {
                    continue;
                }
                yield return ReadRGBColorFromLine(line);
                if (currentLine > endLine) 
                {
                    break;
                }
            }
    }

    HashSet<string> used = new HashSet<string>();

    public void SetHueLimits(double hueStart, double hueEnd)
    {
        hEnd = hueEnd;
        hStart = hueStart;
    }
}

So now that we have the color file, and we have a way to read the file, we can now actually make the image. I used a class I found to boost performance of setting pixels in a bitmap, called LockBitmap. LockBitmap source

I created a small Vector2 class to store coordinate locations

public class Vector2
{
    public int X = 0;
    public int Y = 0;
    public Vector2(int x, int y)
    {
        X = x;
        Y = y;
    }
    public Vector2 Center()
    {
        return new Vector2(X / 2, Y / 2);
    }
    public override string ToString()
    {
        return X.ToString() + "-" + Y.ToString();
    }
}

And I also created a class called SearchArea, which was helpful for finding neighboring pixels. You specify the pixel you want to find neighbors for, the bounds to search within, and the size of the "neighbor square" to search. So if the size is 3, that means you're searching a 3x3 square, with the specified pixel right in the center.

public class SearchArea
{
    public int Size = 0;
    public Vector2 Center;
    public Rectangle Bounds;

    public SearchArea(int size, Vector2 center, Rectangle bounds)
    {
        Center = center;
        Size = size;
        Bounds = bounds;
    }
    public bool IsCoordinateInBounds(int x, int y)
    {
        if (!IsXValueInBounds(x)) { return false; }
        if (!IsYValueInBounds(y)) { return false; }
        return true;

    }
    public bool IsXValueInBounds(int x)
    {
        if (x < Bounds.Left || x >= Bounds.Right) { return false; }
        return true;
    }
    public bool IsYValueInBounds(int y)
    {
        if (y < Bounds.Top || y >= Bounds.Bottom) { return false; }
        return true;
    }

}

Here's the class that actually chooses the next neighbor. Basically there's 2 search modes. A) The full square, B) just the perimeter of the square. This was an optimization that I made to prevent searching the full square again after realizing the square was full. The DepthMap was a further optimization to prevent searching the same squares over and over again. However, I didn't fully optimize this. Every call to GetNeighbors will always do the full square search first. I know I could optimize this to only do the perimeter search after completing the initial full square. I just didn't get around to that optimization yet, and even without it the code is pretty fast. The commented out "lock" lines are because I was using Parallel.ForEach at one point, but realized I had to write more code than I wanted for that lol.

public class RandomWalkGenerator
{
    HashSet<string> Visited = new HashSet<string>();
    Dictionary<string, int> DepthMap = new Dictionary<string, int>();
    Rectangle Bounds;
    Random rnd = new Random();
    public int DefaultSearchSize = 3;
    public RandomWalkGenerator(Rectangle bounds)
    {
        Bounds = bounds;
    }
    private SearchArea GetSearchArea(Vector2 center, int size)
    {
        return new SearchArea(size, center, Bounds);
    }

    private List<Vector2> GetNeighborsFullSearch(SearchArea srchArea, Vector2 coord)
    {
        int radius = (int)Math.Floor((double)((double)srchArea.Size / (double)2));
        List<Vector2> pixels = new List<Vector2>();
        for (int rX = -radius; rX <= radius; rX++)
        {
            for (int rY = -radius; rY <= radius; rY++)
            {
                if (rX == 0 && rY == 0) { continue; } //not a new coordinate
                int x = rX + coord.X;
                int y = rY + coord.Y;
                if (!srchArea.IsCoordinateInBounds(x, y)) { continue; }
                var key = x + "-" + y;
                // lock (Visited)
                {
                    if (!Visited.Contains(key))
                    {
                        pixels.Add(new Vector2(x, y));
                    }
                }
            }
        }
        if (pixels.Count == 0)
        {
            int depth = 0;
            string vecKey = coord.ToString();
            if (!DepthMap.ContainsKey(vecKey))
            {
                DepthMap.Add(vecKey, depth);
            }
            else
            {
                depth = DepthMap[vecKey];
            }

            var size = DefaultSearchSize + 2 * depth;
            var sA = GetSearchArea(coord, size);
            pixels = GetNeighborsPerimeterSearch(sA, coord, depth);
        }
        return pixels;
    }
    private Rectangle GetBoundsForPerimeterSearch(SearchArea srchArea, Vector2 coord)
    {
        int radius = (int)Math.Floor((decimal)(srchArea.Size / 2));
        Rectangle r = new Rectangle(-radius + coord.X, -radius + coord.Y, srchArea.Size, srchArea.Size);
        return r;
    }
    private List<Vector2> GetNeighborsPerimeterSearch(SearchArea srchArea, Vector2 coord, int depth = 0)
    {
        string vecKey = coord.ToString();
        if (!DepthMap.ContainsKey(vecKey))
        {
            DepthMap.Add(vecKey, depth);
        }
        else
        {
            DepthMap[vecKey] = depth;
        }
        Rectangle bounds = GetBoundsForPerimeterSearch(srchArea, coord);
        List<Vector2> pixels = new List<Vector2>();
        int depthMax = 1500;

        if (depth > depthMax)
        {
            return pixels;
        }

        int yTop = bounds.Top;
        int yBot = bounds.Bottom;

        //left to right scan
        for (int x = bounds.Left; x < bounds.Right; x++)
        {

            if (srchArea.IsCoordinateInBounds(x, yTop))
            {
                var key = x + "-" + yTop;
                // lock (Visited)
                {
                    if (!Visited.Contains(key))
                    {
                        pixels.Add(new Vector2(x, yTop));
                    }
                }
            }
            if (srchArea.IsCoordinateInBounds(x, yBot))
            {
                var key = x + "-" + yBot;
                // lock (Visited)
                {
                    if (!Visited.Contains(key))
                    {
                        pixels.Add(new Vector2(x, yBot));
                    }
                }
            }
        }

        int xLeft = bounds.Left;
        int xRight = bounds.Right;
        int yMin = bounds.Top + 1;
        int yMax = bounds.Bottom - 1;
        //top to bottom scan
        for (int y = yMin; y < yMax; y++)
        {
            if (srchArea.IsCoordinateInBounds(xLeft, y))
            {
                var key = xLeft + "-" + y;
                // lock (Visited)
                {
                    if (!Visited.Contains(key))
                    {
                        pixels.Add(new Vector2(xLeft, y));
                    }
                }
            }
            if (srchArea.IsCoordinateInBounds(xRight, y))
            {
                var key = xRight + "-" + y;
                // lock (Visited)
                {
                    if (!Visited.Contains(key))
                    {
                        pixels.Add(new Vector2(xRight, y));
                    }
                }
            }
        }

        if (pixels.Count == 0)
        {
            var size = srchArea.Size + 2;
            var sA = GetSearchArea(coord, size);
            pixels = GetNeighborsPerimeterSearch(sA, coord, depth + 1);
        }
        return pixels;
    }
    private List<Vector2> GetNeighbors(SearchArea srchArea, Vector2 coord)
    {
        return GetNeighborsFullSearch(srchArea, coord);
    }
    public Vector2 ChooseNextNeighbor(Vector2 coord)
    {
        SearchArea sA = GetSearchArea(coord, DefaultSearchSize);
        List<Vector2> neighbors = GetNeighbors(sA, coord);
        if (neighbors.Count == 0)
        {
            return null;
        }
        int idx = rnd.Next(0, neighbors.Count);
        Vector2 elm = neighbors.ElementAt(idx);
        string key = elm.ToString();
        // lock (Visited)
        {
            Visited.Add(key);
        }
        return elm;
    }
}

Ok great, so now here's the class that creates the image

public class RandomWalk
{
    Rectangle Bounds;
    Vector2 StartPath = new Vector2(0, 0);
    LockBitmap LockMap;
    RandomWalkGenerator rwg;
    public int RandomWalkSegments = 1;
    string colorFile = "";

    public RandomWalk(int size, string _colorFile)
    {
        colorFile = _colorFile;
        Bounds = new Rectangle(0, 0, size, size);
        rwg = new RandomWalkGenerator(Bounds);
    }
    private void Reset()
    {
        rwg = new RandomWalkGenerator(Bounds);
    }
    public void CreateImage(string savePath)
    {
        Reset();
        Bitmap bmp = new Bitmap(Bounds.Width, Bounds.Height);
        LockMap = new LockBitmap(bmp);
        LockMap.LockBits();
        if (RandomWalkSegments == 1)
        {
            RandomWalkSingle();
        }
        else
        {
            RandomWalkMulti(RandomWalkSegments);
        }
        LockMap.UnlockBits();
        bmp.Save(savePath);

    }
    public void SetStartPath(int X, int Y)
    {
        StartPath.X = X;
        StartPath.Y = Y;
    }
    private void RandomWalkMulti(int buckets)
    {

        int Buckets = buckets;
        int PathsPerSide = (Buckets + 4) / 4;
        List<Vector2> Positions = new List<Vector2>();

        var w = Bounds.Width;
        var h = Bounds.Height;
        var wInc = w / Math.Max((PathsPerSide - 1),1);
        var hInc = h / Math.Max((PathsPerSide - 1),1);

        //top
        for (int i = 0; i < PathsPerSide; i++)
        {
            var x = Math.Min(Bounds.Left + wInc * i, Bounds.Right - 1);
            Positions.Add(new Vector2(x, Bounds.Top));
        }
        //bottom
        for (int i = 0; i < PathsPerSide; i++)
        {
            var x = Math.Max(Bounds.Right -1 - wInc * i, 0);
            Positions.Add(new Vector2(x, Bounds.Bottom - 1));
        }
        //right and left
        for (int i = 1; i < PathsPerSide - 1; i++)
        {
            var y = Math.Min(Bounds.Top + hInc * i, Bounds.Bottom - 1);
            Positions.Add(new Vector2(Bounds.Left, y));
            Positions.Add(new Vector2(Bounds.Right - 1, y));
        }
        Positions = Positions.OrderBy(x => Math.Atan2(x.X, x.Y)).ToList();
        double cnt = 0;
        List<IEnumerator<bool>> _execs = new List<IEnumerator<bool>>();
        foreach (Vector2 startPath in Positions)
        {
            double pct = cnt / (Positions.Count);
            double pctNext = (cnt + 1) / (Positions.Count);

            var enumer = RandomWalkHueSegment(pct, pctNext, startPath).GetEnumerator();

            _execs.Add(enumer);
            cnt++;
        }

        bool hadChange = true;
        while (hadChange)
        {
            hadChange = false;
            foreach (var e in _execs)
            {
                if (e.MoveNext())
                {
                    hadChange = true;
                }
            }
        }

    }
    private IEnumerable<bool> RandomWalkHueSegment(double hueStart, double hueEnd, Vector2 startPath)
    {
        var colors = new HSLGenerator();
        colors.SetHueLimits(hueStart, hueEnd);
        var colorFileEnum = colors.GetNextFromFile(colorFile).GetEnumerator();
        Vector2 coord = new Vector2(startPath.X, startPath.Y);
        LockMap.SetPixel(coord.X, coord.Y, ColorHelper.HSL2RGB(0, 0, 0));

        while (true)
        {
            if (!colorFileEnum.MoveNext())
            {
                break;
            }
            var rgb = colorFileEnum.Current;
            coord = ChooseNextNeighbor(coord);
            if (coord == null)
            {
                break;
            }
            LockMap.SetPixel(coord.X, coord.Y, rgb);
            yield return true;

        }
    }
    private void RandomWalkSingle()
    {
        Vector2 coord = new Vector2(StartPath.X, StartPath.Y);
        LockMap.SetPixel(coord.X, coord.Y, ColorHelper.HSL2RGB(0, 0, 0));
        int cnt = 1;
        var colors = new HSLGenerator();
        var colorFileEnum = colors.GetNextFromFile(colorFile).GetEnumerator();
        while (true)
        {
            if (!colorFileEnum.MoveNext())
            {
                return;
            }
            var rgb = colorFileEnum.Current;
            var newCoord = ChooseNextNeighbor(coord);
            coord = newCoord;
            if (newCoord == null)
            {
                return;
            }
            LockMap.SetPixel(newCoord.X, newCoord.Y, rgb);
            cnt++;

        }

    }

    private Vector2 ChooseNextNeighbor(Vector2 coord)
    {
        return rwg.ChooseNextNeighbor(coord);
    }


}

And here's an example implementation:

class Program
{
    static void Main(string[] args)
    {
        {
           // ColorHelper.MakeColorFile();
          //  return;
        }
        string colorFile = "colors.txt";
        var size = new Vector2(1000,1000);
        var ctr = size.Center();
        RandomWalk r = new RandomWalk(size.X,colorFile);
        r.RandomWalkSegments = 8;
        r.SetStartPath(ctr.X, ctr.Y);
        r.CreateImage("test.bmp");

    }
}

If RandomWalkSegments = 1, then it basically just starts walking wherever you tell it to, and begins at the first first color in the file.

It's not the cleanest code I'll admit, but it runs pretty fast!

Cropped Output

3 Paths

128 Paths

EDIT:

So I've been learning about OpenGL and Shaders. I generated a 4096x4096 using every color blazing fast on the GPU with 2 simple shader scripts. The output is boring, but figured somebody might find this interesting and come up with some cool ideas:

Vertex Shader

attribute vec3 a_position;
varying vec2 vTexCoord;
   void main() {
      vTexCoord = (a_position.xy + 1) / 2;
      gl_Position = vec4(a_position, 1);
  }

Frag Shader

void main(void){
    int num = int(gl_FragCoord.x*4096.0 + gl_FragCoord.y);
    int h = num % 256;
    int s = (num/256) % 256;
    int l = ((num/256)/256) % 256;
    vec4 hsl = vec4(h/255.0,s/255.0,l/255.0,1.0);
    gl_FragColor = hsl_to_rgb(hsl); // you need to implement a conversion method
}

Edit (10/15/16) : Just wanted to show a proof of concept of a genetic algorithm. I am STILL running this code 24 hours later on a 100x100 set of random colors, but so far the output is beautiful! enter image description here

Edit (10/26/16): Ive been running the genetic algorithm code for 12 days now..and its still optimizing the output. Its basically converged to some local minimum but it apparently is finding more improvement still: enter image description here

Edit: 8/12/17 - I wrote a new random walk algorithm - basically you specify a number of "walkers", but instead of walking randomly - they will randomly choose another walker and either avoid them (choose the next available pixel furthest away) - or walk towards them (choose the next available pixel closest to them). An example grayscale output is here (I will be doing a full 4096x4096 color render after I wire up the coloring!) : enter image description here

applejacks01

Posted 2014-02-25T21:16:53.557

Reputation: 989

4A little belated, but welcome to PPCG! This is an excellent first post. – a spaghetto – 2015-12-22T20:47:01.733

1Thank you! I look forward to completing more challenges! I've been doing more image coding stuff lately, it's my new hobby – applejacks01 – 2016-01-25T03:36:12.317

Wow these are awesome; I'm glad I came back to this post today and checked out all the later stuff. – Jason C – 2016-10-13T19:46:47.827

Thank you! I'm actually doing some genetic algorithm coding now to produce interesting gradients. Basically, take 10000 colors, forming 100x100 grid. For each pixel, get the neighbor pixels. For each, get the CIEDE2000 distance. Sum that up. Sum that up for all 10000 pixels. Genetic algorithm attempts to reduce that total sum. Its slow, but for a 20x20 image its output is really interesting – applejacks01 – 2016-10-14T01:39:49.487

I very especially love the output of this solution. – r_alex_hall – 2019-09-24T23:23:24.957

Thank you alex! A late update, but I did eventually try to color in the grayscale edition i was working on, but it didn't come out anything like I thought it would. The grayscale has a nice 3d effect to it like cracked/contoured earth though :) – applejacks01 – 2019-09-25T22:21:45.153

22

HTML5 canvas + JavaScript

I call it randoGraph and you can create as many you want here

Some examples:

example 1

example 2

example 3

example 4

example 5

example 6

example 7

For example in Firefox you can right click in the canvas (when it finish) and save it as image . Producing 4096x4096 image is a kind of problem due to some browsers memory limit.

The idea is quite simple but each image is unique. First we create the palette of colors. Then starting by X points we select random colors from the palette and positions for them (each time we select a color we delete it from the palette) and we record where we put it not to put in the same position next pixel.

For every pixel that is tangent to that we create a number (X) of possible colors and then we select the most relevant to that pixel. This goes on until the image is complete.

The HTML code

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="el">
<head>
<script type="text/javascript" src="randoGraph.js"></script>
</head>
<body>
    <canvas id="randoGraphCanvas"></canvas> 
</body>
</html>

And the JavaScript for randoGraph.js

window.onload=function(){
    randoGraphInstance = new randoGraph("randoGraphCanvas",256,128,1,1);
    randoGraphInstance.setRandomness(500, 0.30, 0.11, 0.59);
    randoGraphInstance.setProccesses(10);
    randoGraphInstance.init(); 
}

function randoGraph(canvasId,width,height,delay,startings)
{
    this.pixels = new Array();
    this.colors = new Array(); 
    this.timeouts = new Array(); 
    this.randomFactor = 500;
    this.redFactor = 0.30;
    this.blueFactor = 0.11;
    this.greenFactor  = 0.59;
    this.processes = 1;
    this.canvas = document.getElementById(canvasId); 
    this.pixelsIn = new Array(); 
    this.stopped = false;

    this.canvas.width = width;
    this.canvas.height = height;
    this.context = this.canvas.getContext("2d");
    this.context.clearRect(0,0, width-1 , height-1);
    this.shadesPerColor = Math.pow(width * height, 1/3);
    this.shadesPerColor = Math.round(this.shadesPerColor * 1000) / 1000;

    this.setRandomness = function(randomFactor,redFactor,blueFactor,greenFactor)
    {
        this.randomFactor = randomFactor;
        this.redFactor = redFactor;
        this.blueFactor = blueFactor;
        this.greenFactor = greenFactor;
    }

    this.setProccesses = function(processes)
    {
        this.processes = processes;
    }

    this.init = function()
    {
        if(this.shadesPerColor > 256 || this.shadesPerColor % 1 > 0) 
        { 
            alert("The dimensions of the image requested to generate are invalid. The product of width multiplied by height must be a cube root of a integer number up to 256."); 
        }
        else 
        {
            var steps = 256 / this.shadesPerColor;
            for(red = steps / 2; red <= 255;)
            {
                for(blue = steps / 2; blue <= 255;)
                {
                    for(green = steps / 2; green <= 255;)
                    {   
                        this.colors.push(new Color(Math.round(red),Math.round(blue),Math.round(green)));
                        green = green + steps;
                    }
                    blue = blue + steps; 
                }
                red = red + steps; 
            }   

            for(var i = 0; i < startings; i++)
            {
                var color = this.colors.splice(randInt(0,this.colors.length - 1),1)[0];
                var pixel = new Pixel(randInt(0,width - 1),randInt(0,height - 1),color);
                this.addPixel(pixel);       
            }

            for(var i = 0; i < this.processes; i++)
            {
                this.timeouts.push(null);
                this.proceed(i);
            }
        }
    }

    this.proceed = function(index) 
    { 
        if(this.pixels.length > 0)
        {
            this.proceedPixel(this.pixels.splice(randInt(0,this.pixels.length - 1),1)[0]);
            this.timeouts[index] = setTimeout(function(that){ if(!that.stopped) { that.proceed(); } },this.delay,this);
        }
    }

    this.proceedPixel = function(pixel)
    {
        for(var nx = pixel.getX() - 1; nx < pixel.getX() + 2; nx++)
        {
            for(var ny = pixel.getY() - 1; ny < pixel.getY() + 2; ny++)
            {
                if(! (this.pixelsIn[nx + "x" + ny] == 1 || ny < 0 || nx < 0 || nx > width - 1 || ny > height - 1 || (nx == pixel.getX() && ny == pixel.getY())) )
                {
                    var color = this.selectRelevantColor(pixel.getColor());
                    var newPixel = new Pixel(nx,ny,color);
                    this.addPixel(newPixel);
                }
            }
        }   
    }

    this.selectRelevantColor = function(color)
    {
        var relevancies = new Array(); 
        var relColors = new Array(); 
        for(var i = 0; i < this.randomFactor && i < this.colors.length; i++)
        {
            var index = randInt(0,this.colors.length - 1);
            var c = this.colors[index];
            var relevancy = Math.pow( ((c.getRed()-color.getRed()) * this.redFactor) , 2)
            + Math.pow( ((c.getBlue()-color.getBlue()) * this.blueFactor), 2)
            + Math.pow( ((c.getGreen()-color.getGreen()) * this.greenFactor) , 2);
            relevancies.push(relevancy); 
            relColors[relevancy+"Color"] = index;
        }
        return this.colors.splice(relColors[relevancies.min()+"Color"],1)[0]
    }

    this.addPixel = function(pixel)
    {
        this.pixels.push(pixel);
        this.pixelsIn[pixel.getX() + "x" + pixel.getY() ] = 1;
        var color = pixel.getColor();
        this.context.fillStyle = "rgb("+color.getRed()+","+color.getBlue()+","+color.getGreen()+")";
        this.context.fillRect( pixel.getX(), pixel.getY(), 1, 1);   
    }

    var toHex = function toHex(num) 
    {
        num = Math.round(num);
        var hex = num.toString(16);
        return hex.length == 1 ? "0" + hex : hex;
    }

    this.clear = function()
    {
        this.stopped = true;
    }
}

function Color(red,blue,green)
{   
    this.getRed = function() { return red; } 
    this.getBlue = function() { return blue; } 
    this.getGreen = function() { return green; } 
}

function Pixel(x,y,color)
{   
    this.getX = function() { return x; } 
    this.getY = function() { return y; } 
    this.getColor = function() { return color; } 
}


function randInt(min, max) 
{
    return Math.floor(Math.random() * (max - min + 1)) + min;
}


// @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min
Array.prototype.min = function() 
{
      return Math.min.apply(null, this);
};

// @see http://stackoverflow.com/questions/5223/length-of-javascript-object-ie-associative-array
Object.size = function(obj) 
{
    var size = 0, key;
    for (key in obj) {
        if (obj.hasOwnProperty(key)) size++;
    }
    return size;
};

konstantinosX

Posted 2014-02-25T21:16:53.557

Reputation: 321

Defining methods of a class inside the constructor instead of using the prototype chain is really inefficient, especially if said class is used several times. I really recommend that you move the methods for Color and Pixel to the prototype chain. I was going to comment also on Object.size until I realized you used that implementation to save memory rather than run quickly. – Patrick Roberts – 2016-08-05T17:05:03.643

1“Defining methods of a class inside the constructor instead of using the prototype chain is really inefficient, especially if said class is used several times.” is very interesting comment Patrick Roberts. Do you have any reference with example that validates that ? , I sincerely would like to know if this claim has any base ( in order to stop using that ) , and what is it. – konstantinosX – 2016-08-08T19:59:52.247

2Regarding the use of prototype: it works in much the same way that a static method would. When you have the function defined in the object literal, every new object you create must create a new copy of the function as well, and store them with that object instance (so 16 million color objects means 16 million copies of that exact same function in memory). By comparison, using prototype will only create it once, to be associated with the "class" rather than the object. This has obvious memory benefits as well as potential speed benefits. – Mwr247 – 2016-10-18T17:20:32.457

That's nice but it looks like the C# answer from fejesjoco. Is it only by chance?

– A.L – 2014-03-09T21:48:34.680

1The algorithms are here and anyone can read and understand that are real different. This answer published after C# answer from fejesjoco declared as winner motivated of how nice its result is. Then I thought a whole different approach of processing and selecting neighbour colours, and this is it. Of course both answers have same basis, like evenly distribution of the colors used along the visible spectrum, the concept of relevant colours, and starting points, maybe fowling those basis someone could think that the images produced have a resemblance in some cases. – konstantinosX – 2014-03-11T03:29:04.033

Okay, I'm sorry if you thought I was criticizing your answer. I just wondered if you were inspired by fejesjoco's answer since the resulting output looks similar. – A.L – 2014-03-13T17:34:08.147

I can't seem to disable the delay in the script – Mark Jeronimus – 2014-03-15T12:51:28.417

You can’t disable the delay at this but you can set it to 0. Because it is rather slow with one process I added the option to have simultaneously processes, as many as your system can handle. The speed also depends by the number of random colors from the preset pallet to select the best match for the next pixel. – konstantinosX – 2014-03-16T11:38:37.073

20

Python

So here's my solution in python, it takes almost an hour to make one, so there's probably some optimization to be done:

import PIL.Image as Image
from random import shuffle
import math

def mulColor(color, factor):
    return (int(color[0]*factor), int(color[1]*factor), int(color[2]*factor))

def makeAllColors(arg):
    colors = []
    for r in range(0, arg):
        for g in range(0, arg):
            for b in range(0, arg):
                colors.append((r, g, b))
    return colors

def distance(color1, color2):
    return math.sqrt(pow(color2[0]-color1[0], 2) + pow(color2[1]-color1[1], 2) + pow(color2[2]-color1[2], 2))

def getClosestColor(to, colors):
    closestColor = colors[0]
    d = distance(to, closestColor)
    for color in colors:
        if distance(to, color) < d:
            closestColor = color
            d = distance(to, closestColor)
    return closestColor

imgsize = (256, 128)
#imgsize = (10, 10)
colors = makeAllColors(32)
shuffle(colors)
factor = 255.0/32.0
img = Image.new("RGB", imgsize, "white")
#start = (imgsize[0]/4, imgsize[1]/4)
start = (imgsize[0]/2, 0)
startColor = colors.pop()
img.putpixel(start, mulColor(startColor, factor))

#color = getClosestColor(startColor, colors)
#img.putpixel((start[0]+1, start[1]), mulColor(color, factor))

edgePixels = [(start, startColor)]
donePositions = [start]
for pixel in edgePixels:
    if len(colors) > 0:
        color = getClosestColor(pixel[1], colors)
    m = [(pixel[0][0]-1, pixel[0][1]), (pixel[0][0]+1, pixel[0][2]), (pixel[0][0], pixel[0][3]-1), (pixel[0][0], pixel[0][4]+1)]
    if len(donePositions) >= imgsize[0]*imgsize[1]:
    #if len(donePositions) >= 100:
        break
    for pos in m:
        if (not pos in donePositions):
            if not (pos[0]<0 or pos[1]<0 or pos[0]>=img.size[0] or pos[1]>=img.size[1]):
                img.putpixel(pos, mulColor(color, factor))
                #print(color)
                donePositions.append(pos)
                edgePixels.append((pos, color))
                colors.remove(color)
                if len(colors) > 0:
                    color = getClosestColor(pixel[1], colors)
    print((len(donePositions) * 1.0) / (imgsize[0]*imgsize[1]))
print len(donePositions)
img.save("colors.png")

Here are some example outputs:

enter image description here enter image description here enter image description here enter image description here enter image description here

Wpapsco

Posted 2014-02-25T21:16:53.557

Reputation: 421

1Looks like some crazy sound waveforms – Mark Jeronimus – 2014-03-15T12:51:09.377

19

Java

I decided to have a try at this challenge. I was inspired by this answer to another code golf. My program generates uglier images, but they have all colors.

Also, my first time code golfing. :)

(4k images were too big for my small upload speed, I tried uploading one but after one hour it hasn't uploaded. You can generate your own.)

Closeup:

Generates an image in 70 seconds on my machine, takes about 1.5GB of memory when generating

Main.java

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Random;

import javax.imageio.ImageIO;


public class Main {
    static char[][] colors = new char[4096 * 4096][3];
    static short[][] pixels = new short[4096 * 4096][2];

    static short[][] iterMap = new short[4096][4096];  

    public static int mandel(double re0, double im0, int MAX_ITERS) {
        double re = re0;
        double im = im0;
        double _r;
        double _i;
        double re2;
        double im2;
        for (int iters = 0; iters < MAX_ITERS; iters++) {
            re2 = re * re;
            im2 = im * im;
            if (re2 + im2 > 4.0) {
                return iters;
            }
            _r = re;
            _i = im;
            _r = re2 - im2;
            _i = 2 * (re * im);
            _r += re0;
            _i += im0;
            re = _r;
            im = _i;
        }
        return MAX_ITERS;
    }

    static void shuffleArray(Object[] ar) {
        Random rnd = new Random();
        for (int i = ar.length - 1; i > 0; i--) {
          int index = rnd.nextInt(i + 1);
          // Simple swap
          Object a = ar[index];
          ar[index] = ar[i];
          ar[i] = a;
        }
      }

    public static void main(String[] args) {
        long startTime = System.nanoTime();

        System.out.println("Generating colors...");

        for (int i = 0; i < 4096 * 4096; i++) {
            colors[i][0] = (char)((i >> 16) & 0xFF); // Red
            colors[i][1] = (char)((i >> 8) & 0xFF);  // Green
            colors[i][2] = (char)(i & 0xFF);         // Blue
        }

        System.out.println("Sorting colors...");

        //shuffleArray(colors); // Not needed

        Arrays.sort(colors, new Comparator<char[]>() {
            @Override
            public int compare(char[] a, char[] b) {
                return (a[0] + a[1] + a[2]) - (b[0] + b[1] + b[2]);
            }
        });

        System.out.println("Generating fractal...");

        for (int y = -2048; y < 2048; y++) {
            for (int x = -2048; x < 2048; x++) {
                short iters = (short) mandel(x / 1024.0, y / 1024.0, 1024);
                iterMap[x + 2048][y + 2048] = iters;
            }
        }

        System.out.println("Organizing pixels in the image...");

        for (short x = 0; x < 4096; x++) {
            for (short y = 0; y < 4096; y++) {
                pixels[x * 4096 + y][0] = x;
                pixels[x * 4096 + y][1] = y;
            }
        }

        shuffleArray(pixels);

        Arrays.sort(pixels, new Comparator<short[]>() {
            @Override
            public int compare(short[] a, short[] b) {
                return iterMap[b[0]][b[1]] - iterMap[a[0]][a[1]];
            }
        });

        System.out.println("Writing image to BufferedImage...");

        BufferedImage img = new BufferedImage(4096, 4096, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = img.createGraphics();

        for (int i = 0; i < 4096 * 4096; i++) {
            g.setColor(new Color(colors[i][0], colors[i][1], colors[i][2]));
            g.fillRect(pixels[i][0], pixels[i][1], 1, 1);
        }

        g.dispose();

        System.out.println("Writing image to file...");

        File imageFile = new File("image.png");

        try {
            ImageIO.write(img, "png", imageFile);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("Done!");
        System.out.println("Took " + ((System.nanoTime() - startTime) / 1000000000.) + " seconds.");
        System.out.println();
        System.out.println("The result is saved in " + imageFile.getAbsolutePath());

    }

}

Mateon1

Posted 2014-02-25T21:16:53.557

Reputation: 323

18

Mathematica

colors = Table[
r = y*256 + x; {BitAnd[r, 2^^111110000000000]/32768., 
BitAnd[r, 2^^1111100000]/1024., BitAnd[r, 2^^11111]/32.}, {y, 0, 
127}, {x, 0, 255}];
SeedRandom[1337];
maxi = 5000000;
Monitor[For[i = 0, i < maxi, i++,
x1 = RandomInteger[{2, 255}];
x2 = RandomInteger[{2, 255}];
y1 = RandomInteger[{2, 127}];
y2 = RandomInteger[{2, 127}];
c1 = colors[[y1, x1]];
c2 = colors[[y2, x2]];
ca1 = (colors[[y1 - 1, x1]] + colors[[y1, x1 - 1]] + 
  colors[[y1 + 1, x1]] + colors[[y1, x1 + 1]])/4.;
ca2 = (colors[[y2 - 1, x2]] + colors[[y2, x2 - 1]] + 
  colors[[y2 + 1, x2]] + colors[[y2, x2 + 1]])/4.;
d1 = Abs[c1[[1]] - ca1[[1]]] + Abs[c1[[2]] - ca1[[2]]] + 
Abs[c1[[3]] - ca1[[3]]];
d1p = Abs[c2[[1]] - ca1[[1]]] + Abs[c2[[2]] - ca1[[2]]] + 
Abs[c2[[3]] - ca1[[3]]];
d2 = Abs[c2[[1]] - ca2[[1]]] + Abs[c2[[2]] - ca2[[2]]] + 
Abs[c2[[3]] - ca2[[3]]];
d2p = Abs[c1[[1]] - ca2[[1]]] + Abs[c1[[2]] - ca2[[2]]] + 
Abs[c1[[3]] - ca2[[3]]];
If[(d1p + d2p < 
  d1 + d2) || (RandomReal[{0, 1}] < 
   Exp[-Log10[i]*(d1p + d2p - (d1 + d2))] && i < 1000000),
temp = colors[[y1, x1]];
colors[[y1, x1]] = colors[[y2, x2]];
colors[[y2, x2]] = temp
]
], ProgressIndicator[i, {1, maxi}]]
Image[colors]

Result (2x):

256x128 2x

Original 256x128 image

Edit:

by replacing Log10[i] with Log10[i]/5 you get: enter image description here

The above code is related to simulated annealing. Seen in this way, the second image is created with a higher "temperature" in the first 10^6 steps. The higher "temperature" causes more permutations among the pixels, whereas in the first image the structure of the ordered image is still slightly visible.

DaveStrider

Posted 2014-02-25T21:16:53.557

Reputation: 281

17

JavaScript

Still a student and my first time posting so my codes probably messy and I'm not 100% sure that my pictures have all the needed colors but I was super happy with my results so I figured I'd post them.

I know the contest is over but I really loved the results of these and I always loved the look of recursive backtracking generated mazes so I thought it might be cool to see what one would look like if it placed colored pixels. So I start by generating all the colors in an array then I do the recursive backtracking while popping colors off the array.

Here's my JSFiddle http://jsfiddle.net/Kuligoawesome/3VsCu/

// Global variables
const FPS = 60;// FrameRate
var canvas = null;
var ctx = null;

var bInstantDraw = false;
var MOVES_PER_UPDATE = 50; //How many pixels get placed down
var bDone = false;
var width; //canvas width
var height; //canvas height
var colorSteps = 32;

var imageData;
var grid;
var colors;

var currentPos;
var prevPositions;

// This is called when the page loads
function Init()
{
    canvas = document.getElementById('canvas'); // Get the HTML element with the ID of 'canvas'
    width = canvas.width;
    height = canvas.height;
    ctx = canvas.getContext('2d'); // This is necessary, but I don't know exactly what it does

    imageData = ctx.createImageData(width,height); //Needed to do pixel manipulation

    grid = []; //Grid for the labyrinth algorithm
    colors = []; //Array of all colors
    prevPositions = []; //Array of previous positions, used for the recursive backtracker algorithm

    for(var r = 0; r < colorSteps; r++)
    {
        for(var g = 0; g < colorSteps; g++)
        {
            for(var b = 0; b < colorSteps; b++)
            {
                colors.push(new Color(r * 255 / (colorSteps - 1), g * 255 / (colorSteps - 1), b * 255 / (colorSteps - 1)));
                //Fill the array with all colors
            }
        }
    }

    colors.sort(function(a,b)
    {
        if (a.r < b.r)
            return -1;
        if (a.r > b.r)
            return 1;
        if (a.g < b.g)
            return -1;
        if (a.g > b.g)
            return 1;
        if (a.b < b.b)
            return -1;
        if (a.b > b.b)
            return 1;
        return 0;
    });

    for(var x = 0; x < width; x++)
    {
        grid.push(new Array());
        for(var y = 0; y < height; y++)
        {
            grid[x].push(0); //Set up the grid
            //ChangePixel(imageData, x, y, colors[x + (y * width)]);
        }
    }

    currentPos = new Point(Math.floor(Math.random() * width),Math.floor(Math.random() * height)); 

    grid[currentPos.x][currentPos.y] = 1;
    prevPositions.push(currentPos);
    ChangePixel(imageData, currentPos.x, currentPos.y, colors.pop());

    if(bInstantDraw)
    {
        do
        {
            var notMoved = true;
            while(notMoved)
            {
                var availableSpaces = CheckForSpaces(grid);

                if(availableSpaces.length > 0)
                {
                    var test = availableSpaces[Math.floor(Math.random() * availableSpaces.length)];
                    prevPositions.push(currentPos);
                    currentPos = test;
                    grid[currentPos.x][currentPos.y] = 1;
                    ChangePixel(imageData, currentPos.x, currentPos.y, colors.pop());
                    notMoved = false;
                }
                else
                {
                    if(prevPositions.length != 0)
                    {
                        currentPos = prevPositions.pop();
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }
        while(prevPositions.length > 0)

        ctx.putImageData(imageData,0,0);
    }
    else
    {
        setInterval(GameLoop, 1000 / FPS);
    }
}

// Main program loop
function GameLoop()
{
    Update();
    Draw();
}

// Game logic goes here
function Update()
{
    if(!bDone)
    {
        var counter = MOVES_PER_UPDATE;
        while(counter > 0) //For speeding up the drawing
        {
            var notMoved = true;
            while(notMoved)
            {
                var availableSpaces = CheckForSpaces(grid); //Find available spaces

                if(availableSpaces.length > 0) //If there are available spaces
                {
                    prevPositions.push(currentPos); //add old position to prevPosition array
                    currentPos = availableSpaces[Math.floor(Math.random() * availableSpaces.length)]; //pick a random available space
                    grid[currentPos.x][currentPos.y] = 1; //set that space to filled
                    ChangePixel(imageData, currentPos.x, currentPos.y, colors.pop()); //pop color of the array and put it in that space
                    notMoved = false;
                }
                else
                {
                    if(prevPositions.length != 0)
                    {
                        currentPos = prevPositions.pop(); //pop to previous position where spaces are available
                    }
                    else
                    {
                        bDone = true;
                        break;
                    }
                }
            }
            counter--;
        }
    }
}
function Draw()
{
    // Clear the screen
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    ctx.fillStyle='#000000';
    ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);

    ctx.putImageData(imageData,0,0);
}

function CheckForSpaces(inGrid) //Checks for available spaces then returns back all available spaces
{
    var availableSpaces = [];

    if(currentPos.x > 0 && inGrid[currentPos.x - 1][currentPos.y] == 0)
    {
        availableSpaces.push(new Point(currentPos.x - 1, currentPos.y));
    }

    if(currentPos.x < width - 1 && inGrid[currentPos.x + 1][currentPos.y] == 0)
    {
        availableSpaces.push(new Point(currentPos.x + 1, currentPos.y));
    }

    if(currentPos.y > 0 && inGrid[currentPos.x][currentPos.y - 1] == 0)
    {
        availableSpaces.push(new Point(currentPos.x, currentPos.y - 1));
    }

    if(currentPos.y < height - 1 && inGrid[currentPos.x][currentPos.y + 1] == 0)
    {
        availableSpaces.push(new Point(currentPos.x, currentPos.y + 1));
    }

    return availableSpaces;
}

function ChangePixel(data, x, y, color) //Quick function to simplify changing pixels
{
    data.data[((x + (y * width)) * 4) + 0] = color.r;
    data.data[((x + (y * width)) * 4) + 1] = color.g;
    data.data[((x + (y * width)) * 4) + 2] = color.b;
    data.data[((x + (y * width)) * 4) + 3] = 255;
}

/*Needed Classes*/
function Point(xIn, yIn)
{
    this.x = xIn;
    this.y = yIn;
}

function Color(r, g, b)
{
    this.r = r;
    this.g = g;
    this.b = b;
    this.hue = Math.atan2(Math.sqrt(3) * (this.g - this.b), 2 * this.r - this.g, this.b);
    this.min = Math.min(this.r, this.g);
    this.min = Math.min(this.min, this.b);
    this.min /= 255;
    this.max = Math.max(this.r, this.g);
    this.max = Math.max(this.max, this.b);
    this.max /= 255;
    this.luminance = (this.min + this.max) / 2;
    if(this.min === this.max)
    {
        this.saturation = 0;
    }
    else if(this.luminance < 0.5)
    {
        this.saturation = (this.max - this.min) / (this.max + this.min);
    }
    else if(this.luminance >= 0.5)
    {
        this.saturation = (this.max - this.min) / (2 - this.max - this.min);
    }
}

256x128 picture, colors sorted red->green->blue
RGB Sorted Colors

256x128 picture, colors sorted blue->green->red
BGR Sorted Colors

256x128 picture, colors sorted hue->luminance->saturation
HLS Sorted Colors

And finally a GIF of one being generated
Color Labyrinth GIF

Kuligoawesome

Posted 2014-02-25T21:16:53.557

Reputation: 171

I love the ordered chaos/noise looking output of this and another solution that produces similar output. – r_alex_hall – 2019-09-24T23:24:41.500

Your colors are clipped in the brightest regions, causing duplicates. Change r * Math.ceil(255 / (colorSteps - 1) to r * Math.floor(255 / (colorSteps - 1), or even better: r * 255 / (colorSteps - 1) (untested, since you didn't supply a jsfiddle) – Mark Jeronimus – 2014-03-09T12:08:56.607

Whoops, yeah I had a feeling that was going to cause problems, hopefully its fixed now, and sorry about the lack of jsfiddle (I didn't know it existed!) Thanks! – Kuligoawesome – 2014-03-09T16:26:13.977

17

C#

So I started working on this just as a fun exercise and ended up with an output that at least to me looks pretty neat. The key difference in my solution to (at least) most others is that I'm generating exactly the number of colors needed to start with and evenly spacing the generation out from pure white to pure black. I'm also setting colors working in an inward spiral and choosing the next color based on the average of the color diff between all neighbors that have been set.

Here is a small sample output that I've produced so far, I'm working on a 4k render but I expect it to take upwards of a day to finish.

Here is a sample of the spec output at 256x128:

Spec Render

Some larger images with still reasonable render times:

Render at 360 x 240

Second run at 360 x 240 produced a much smoother image

Render #2 at 360 x 240

After improving performance I was able to run a HD render which took 2 days, I haven't given up on a 4k yet but it could take weeks.

HD Render

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;

namespace SandBox
{
    class Program
    {
        private static readonly List<Point> directions = new List<Point>
        {
            new Point(1, 0),
            new Point(0, 1),
            new Point(-1, 0),
            new Point(0, -1)
        };

        static void Main(string[] args)
        {
            if (args.Length != 2)
            {
                HelpFile();
                return;
            }
            try
            {
                var config = new ColorGeneratorConfig
                {
                    XLength = int.Parse(args[0]),
                    YLength = int.Parse(args[1])
                };

                Console.WriteLine("Starting image generation with:");
                Console.WriteLine($"\tDimensions:\t\t{config.XLength} X {config.YLength}");
                Console.WriteLine($"\tSteps Per Channel:\t{config.NumOfSteps}");
                Console.WriteLine($"\tStep Size:\t\t{config.ColorStep}");
                Console.WriteLine($"\tSteps to Skip:\t\t{config.StepsToSkip}\n");

                var runner = new TaskRunner();
                var colors = runner.Run(() => GenerateColorList(config), "color selection");
                var pixels = runner.Run(() => BuildPixelArray(colors, config), "pixel array generation");
                runner.Run(() => OutputBitmap(pixels, config), "bitmap creation");
            }
            catch (Exception ex)
            {
               HelpFile("There was an issue in execution");
            }

            Console.ReadLine();
        }

        private static void HelpFile(string errorMessage = "")
        {
            const string Header = "Generates an image with every pixel having a unique color";
            Console.WriteLine(errorMessage == string.Empty ? Header : $"An error has occured: {errorMessage}\n Ensure the Arguments you have provided are valid");
            Console.WriteLine();
            Console.WriteLine($"{AppDomain.CurrentDomain.FriendlyName} X Y");
            Console.WriteLine();
            Console.WriteLine("X\t\tThe Length of the X dimension eg: 256");
            Console.WriteLine("Y\t\tThe Length of the Y dimension eg: 128");
        }

        public static List<Color> GenerateColorList(ColorGeneratorConfig config)
        {

            //Every iteration of our color generation loop will add the iterationfactor to this accumlator which is used to know when to skip
            decimal iterationAccumulator = 0;

            var colors = new List<Color>();
            for (var r = 0; r < config.NumOfSteps; r++)
                for (var g = 0; g < config.NumOfSteps; g++)
                    for (var b = 0; b < config.NumOfSteps; b++)
                    {
                        iterationAccumulator += config.IterationFactor;

                        //If our accumulator has reached 1, then subtract one and skip this iteration
                        if (iterationAccumulator > 1)
                        {
                            iterationAccumulator -= 1;
                            continue;
                        }

                        colors.Add(Color.FromArgb(r*config.ColorStep, g*config.ColorStep,b*config.ColorStep));
                    }
            return colors;
        }

        public static Color?[,] BuildPixelArray(List<Color> colors, ColorGeneratorConfig config)
        {
            //Get a random color to start with.
            var random = new Random(Guid.NewGuid().GetHashCode());
            var nextColor = colors[random.Next(colors.Count)];

            var pixels = new Color?[config.XLength, config.YLength];
            var currPixel = new Point(0, 0);

            var i = 0;

            //Since we've only generated exactly enough colors to fill our image we can remove them from the list as we add them to our image and stop when none are left.
            while (colors.Count > 0)
            {
                i++;

                //Set the current pixel and remove the color from the list.
                pixels[currPixel.X, currPixel.Y] = nextColor;
                colors.RemoveAt(colors.IndexOf(nextColor));

                //Our image generation works in an inward spiral generation GetNext point will retrieve the next pixel given the current top direction.
                var nextPixel = GetNextPoint(currPixel, directions.First());

                //If this next pixel were to be out of bounds (for first circle of spiral) or hit a previously generated pixel (for all other circles)
                //Then we need to cycle the direction and get a new next pixel
                if (nextPixel.X >= config.XLength || nextPixel.Y >= config.YLength || nextPixel.X < 0 || nextPixel.Y < 0 ||
                    pixels[nextPixel.X, nextPixel.Y] != null)
                {
                    var d = directions.First();
                    directions.RemoveAt(0);
                    directions.Add(d);
                    nextPixel = GetNextPoint(currPixel, directions.First());
                }

                //This code sets the pixel to pick a color for and also gets the next color
                //We do this at the end of the loop so that we can also support haveing the first pixel set outside of the loop
                currPixel = nextPixel;

                if (colors.Count == 0) continue;

                var neighbours = GetNeighbours(currPixel, pixels, config);
                nextColor = colors.AsParallel().Aggregate((item1, item2) => GetAvgColorDiff(item1, neighbours) <
                                                                            GetAvgColorDiff(item2, neighbours)
                    ? item1
                    : item2);
            }

            return pixels;
        }

        public static void OutputBitmap(Color?[,] pixels, ColorGeneratorConfig config)
        {
            //Now that we have generated our image in the color array we need to copy it into a bitmap and save it to file.
            var image = new Bitmap(config.XLength, config.YLength);

            for (var x = 0; x < config.XLength; x++)
                for (var y = 0; y < config.YLength; y++)
                    image.SetPixel(x, y, pixels[x, y].Value);

            using (var file = new FileStream($@".\{config.XLength}X{config.YLength}.png", FileMode.Create))
            {
                image.Save(file, ImageFormat.Png);
            }
        }

        static Point GetNextPoint(Point current, Point direction)
        {
            return new Point(current.X + direction.X, current.Y + direction.Y);
        }

        static List<Color> GetNeighbours(Point current, Color?[,] grid, ColorGeneratorConfig config)
        {
            var list = new List<Color>();
            foreach (var direction in directions)
            {
                var xCoord = current.X + direction.X;
                var yCoord = current.Y + direction.Y;
                if (xCoord < 0 || xCoord >= config.XLength|| yCoord < 0 || yCoord >= config.YLength)
                {
                    continue;
                }
                var cell = grid[xCoord, yCoord];
                if (cell.HasValue) list.Add(cell.Value);
            }
            return list;
        }

        static double GetAvgColorDiff(Color source, IList<Color> colors)
        {
            return colors.Average(color => GetColorDiff(source, color));
        }

        static int GetColorDiff(Color color1, Color color2)
        {
            var redDiff = Math.Abs(color1.R - color2.R);
            var greenDiff = Math.Abs(color1.G - color2.G);
            var blueDiff = Math.Abs(color1.B - color2.B);
            return redDiff + greenDiff + blueDiff;
        }
    }

    public class ColorGeneratorConfig
    {
        public int XLength { get; set; }
        public int YLength { get; set; }

        //Get the number of permutations for each color value base on the required number of pixels.
        public int NumOfSteps
            => (int)Math.Ceiling(Math.Pow((ulong)XLength * (ulong)YLength, 1.0 / ColorDimensions));

        //Calculate the increment for each step
        public int ColorStep
            => 255 / (NumOfSteps - 1);

        //Because NumOfSteps will either give the exact number of colors or more (never less) we will sometimes to to skip some
        //this calculation tells how many we need to skip
        public decimal StepsToSkip
            => Convert.ToDecimal(Math.Pow(NumOfSteps, ColorDimensions) - XLength * YLength);

        //This factor will be used to as evenly as possible spread out the colors to be skipped so there are no large gaps in the spectrum
        public decimal IterationFactor => StepsToSkip / Convert.ToDecimal(Math.Pow(NumOfSteps, ColorDimensions));

        private double ColorDimensions => 3.0;
    }

    public class TaskRunner
    {
        private Stopwatch _sw;
        public TaskRunner()
        {
            _sw = new Stopwatch();
        }

        public void Run(Action task, string taskName)
        {
            Console.WriteLine($"Starting {taskName}...");
            _sw.Start();
            task();
            _sw.Stop();
            Console.WriteLine($"Finished {taskName}. Elapsed(ms): {_sw.ElapsedMilliseconds}");
            Console.WriteLine();
            _sw.Reset();
        }

        public T Run<T>(Func<T> task, string taskName)
        {
            Console.WriteLine($"Starting {taskName}...");
            _sw.Start();
            var result = task();
            _sw.Stop();
            Console.WriteLine($"Finished {taskName}. Elapsed(ms): {_sw.ElapsedMilliseconds}");
            Console.WriteLine();
            _sw.Reset();
            return result;
        }
    }
}

user19547

Posted 2014-02-25T21:16:53.557

Reputation:

If anyone has any thoughts on how to improve the performance of the color selection algorithm please let me know, as it stands the 360*240 renders take about 15 minutes. I don't believe it can be parrallelized but I do wonder if there would be a faster way to get the lowest color diff. – None – 2016-08-03T23:24:14.317

How does an image of 360240 constitute 'all colors'? How are you producing cbrt(360240)=44.208377983684639269357874002958 colors per component? – Mark Jeronimus – 2016-08-05T11:05:09.053

What language is this? Randomizing a list sort and Random is a bad idea regardless, because depending on the algorithm and implementation it may cause a biased result or an exception stating that "Comparison method violates its general contract!": because the contract states that (x.compareTo(y)>0 && y.compareTo(z)>0) implies x.compareTo(z)>0. To randomize a list, use some provided Shuffle method. (colors.Shuffle() ?) – Mark Jeronimus – 2016-08-05T13:10:41.717

@MarkJeronimus I admit I missed the spec about the 256x128 image, I will re do the simple renders using those sizes, I was focusing on the every pixel is a unique color aspect of the challenge and larger renders as other submissions have done. – None – 2016-08-05T15:44:07.177

@MarkJeronimus I realize the random sort is bad, in fact there is a comment saying as much. This was just a remnant of another approach I started taking and I was prioritizing getting the large renders done as they take a very long time. – None – 2016-08-05T15:46:07.890

The random code has been cleaned up, and I also found a better method for the sorting i was doing previously. – None – 2016-08-05T15:52:51.007

16

Go

Here's another one from me, I think it gives more interesting results:

package main

import (
    "image"
    "image/color"
    "image/png"
    "os"

    "math"
    "math/rand"
)

func distance(c1, c2 color.Color) float64 {
    r1, g1, b1, _ := c1.RGBA()
    r2, g2, b2, _ := c2.RGBA()
    rd, gd, bd := int(r1)-int(r2), int(g1)-int(g2), int(b1)-int(b2)
    return math.Sqrt(float64(rd*rd + gd*gd + bd*bd))
}

func main() {
    allcolor := image.NewRGBA(image.Rect(0, 0, 256, 128))
    for y := 0; y < 128; y++ {
        for x := 0; x < 256; x++ {
            allcolor.Set(x, y, color.RGBA{uint8(x%32) * 8, uint8(y%32) * 8, uint8(x/32+(y/32*8)) * 8, 255})
        }
    }

    for y := 0; y < 128; y++ {
        for x := 0; x < 256; x++ {
            rx, ry := rand.Intn(256), rand.Intn(128)

            c1, c2 := allcolor.At(x, y), allcolor.At(rx, ry)
            allcolor.Set(x, y, c2)
            allcolor.Set(rx, ry, c1)
        }
    }

    for i := 0; i < 16384; i++ {
        for y := 0; y < 128; y++ {
            for x := 0; x < 256; x++ {
                xl, xr := (x+255)%256, (x+1)%256
                cl, c, cr := allcolor.At(xl, y), allcolor.At(x, y), allcolor.At(xr, y)
                dl, dr := distance(cl, c), distance(c, cr)
                if dl < dr {
                    allcolor.Set(xl, y, c)
                    allcolor.Set(x, y, cl)
                }

                yu, yd := (y+127)%128, (y+1)%128
                cu, c, cd := allcolor.At(x, yu), allcolor.At(x, y), allcolor.At(x, yd)
                du, dd := distance(cu, c), distance(c, cd)
                if du < dd {
                    allcolor.Set(x, yu, c)
                    allcolor.Set(x, y, cu)
                }
            }
        }
    }

    filep, err := os.Create("EveryColor.png")
    if err != nil {
        panic(err)
    }
    err = png.Encode(filep, allcolor)
    if err != nil {
        panic(err)
    }
    filep.Close()
}

It starts with the same pattern as the gif in my other answer. Then, it shuffles it into this:

just noise

The more iterations I run the rather uninspired neighbor-comparison algorithm, the more apparent the rainbow pattern becomes.

Here's 16384:

a very noisy rainbow at 16384 iterations

And 65536:

enter image description here

um3k

Posted 2014-02-25T21:16:53.557

Reputation: 361

6+1 I like that a pattern emerges from that; you should make an animation of it! – Jason C – 2014-02-28T03:47:43.673

16

These images are "Langton's Rainbow". They're drawn rather simply: as Langton's ant moves about, a color is drawn on each pixel the first time that pixel is visitted. The color to draw next is then simply incremented by 1, ensuring that 2^15 colors are used, one for each pixel.

EDIT: I made a version which renders 4096X4096 images, using 2^24 colors. The colors are also 'reflected' so they make nice, smooth gradients. I'll only provide links to them since they're huge (>28 MB)

Langton's Rainbow large, rule LR

Langton's Rainbow large, rule LLRR

//End of edit.

This for the classic LR rule set:

Langton's Rainbow LR

Here is LLRR:

Langton's Rainbow LLRR

Finally, this one uses the LRRRRRLLR ruleset:

Langton's Rainbow LRRRRRLLR

Written in C++, using CImg for graphics. I should also mention how the colors were selected: First, I use an unsigned short to contain the RGB color data. Every time a cell is first visitted, I right shift the bits by some multiple of 5, AND by 31, then multiply by 8. Then the unsigned short color is incremented by 1. This produces values from 0 to 248 at most. However, I've subtract this value from 255 in the red and blue components, therefore R and B are in multiples of 8, starting from 255, down to 7:

c[0]=255-((color&0x1F)*8);
c[2]=255-(((color>>5)&0x1F)*8);
c[1]=(((color>>10)&0x1F)*8);

However, this doesn't apply to the green component, which is in multiples of 8 from 0 to 248. In any case, each pixel should contain a unique color.

Anyways, source code is below:

#include "CImg.h"
using namespace cimg_library;
CImgDisplay screen;
CImg<unsigned char> surf;
#define WIDTH 256
#define HEIGHT 128
#define TOTAL WIDTH*HEIGHT
char board[WIDTH][HEIGHT];


class ant
{
  public:
  int x,y;
  char d;
  unsigned short color;
  void init(int X, int Y,char D)
  {
    x=X;y=Y;d=D;
    color=0;
  }

  void turn()
  {
    ///Have to hard code for the rule set here.
    ///Make sure to set RULECOUNT to the number of rules!
    #define RULECOUNT 9
    //LRRRRRLLR
    char get=board[x][y];
    if(get==0||get==6||get==7){d+=1;}
    else{d-=1;}
    if(d<0){d=3;}
    else if(d>3){d=0;}
  }

  void forward()
  {
    if(d==0){x++;}
    else if(d==1){y--;}
    else if(d==2){x--;}
    else {y++;}
    if(x<0){x=WIDTH-1;}
    else if(x>=WIDTH){x=0;}
    if(y<0){y=HEIGHT-1;}
    else if(y>=HEIGHT){y=0;}
  }

  void draw()
  {
    if(board[x][y]==-1)
    {
      board[x][y]=0;
      unsigned char c[3];
      c[0]=255-((color&0x1F)*8);
      c[2]=255-(((color>>5)&0x1F)*8);
      c[1]=(((color>>10)&0x1F)*8);
      surf.draw_point(x,y,c);
      color++;
    }

    board[x][y]++;
    if(board[x][y]==RULECOUNT){board[x][y]=0;}

  }

  void step()
  {
    draw();
    turn();
    forward();
  }
};

void renderboard()
{
  unsigned char white[]={200,190,180};
  surf.draw_rectangle(0,0,WIDTH,HEIGHT,white);
  for(int x=0;x<WIDTH;x++)
  for(int y=0;y<HEIGHT;y++)
  {
    char get=board[x][y];
    if(get==1){get=1;unsigned char c[]={255*get,255*get,255*get};
    surf.draw_point(x,y,c);}
    else if(get==0){get=0;unsigned char c[]={255*get,255*get,255*get};
    surf.draw_point(x,y,c);}
  }
}

int main(int argc, char** argv)
{

  screen.assign(WIDTH*3,HEIGHT*3);
  surf.assign(WIDTH,HEIGHT,1,3);
  ant a;
  a.init(WIDTH/2,HEIGHT/2,2);
  surf.fill(0);
  for(int x=0;x<WIDTH;x++)
  for(int y=0;y<HEIGHT;y++)
  {
    board[x][y]=-1;
  }

  while(a.color<TOTAL)
  {
    a.step();
  }

  screen=surf;
  while(screen.is_closed()==false)
  {
    screen.wait();
  }
  surf.save_bmp("LangtonsRainbow.bmp");
  return 0;
}

Space Kiwi

Posted 2014-02-25T21:16:53.557

Reputation: 161

3

Image links are dead because Dropbox killed Public Folders.

– user2428118 – 2017-04-27T09:52:14.893

1

Welcome, and join the club. Maybe its interesting to try other turmites from http://code.google.com/p/ruletablerepository/wiki/EdPeggsBusyBeaverTurmiteChallenge (I participated in that)

– Mark Jeronimus – 2014-03-10T18:48:46.157

15

Ruby

I decided I would go ahead and make the PNG from scratch, because I thought that would be interesting. This code is literally outputting the raw binary data into a file.

I did the 512x512 version. (The algorithm is rather uninteresting, though.) It finishes in about 3 seconds on my machine.

require 'zlib'

class RBPNG
  def initialize
    # PNG header
    @data = [137, 80, 78, 71, 13, 10, 26, 10].pack 'C*'
  end

  def chunk name, data = ''
    @data += [data.length].pack 'N'
    @data += name
    @data += data
    @data += [Zlib::crc32(name + data)].pack 'N'
  end

  def IHDR opts = {}
    opts = {bit_depth: 8, color_type: 6, compression: 0, filter: 0, interlace: 0}.merge opts
    raise 'IHDR - Missing width param' if !opts[:width]
    raise 'IHDR - Missing height param' if !opts[:height]

    self.chunk 'IHDR', %w[width height].map {|x| [opts[x.to_sym]].pack 'N'}.join +
                       %w[bit_depth color_type compression filter interlace].map {|x| [opts[x.to_sym]].pack 'C'}.join
  end

  def IDAT data; self.chunk 'IDAT', Zlib.deflate(data); end
  def IEND; self.chunk 'IEND'; end
  def write filename; IO.binwrite filename, @data; end
end

class Color
  attr_accessor :r, :g, :b, :a

  def initialize r = 0, g = 0, b = 0, a = 255
    if r.is_a? Array
      @r, @g, @b, @a = @r
      @a = 255 if !@a
    else
      @r = r
      @g = g
      @b = b
      @a = a
    end
  end

  def hex; '%02X' * 4 % [@r, @g, @b, @a]; end
  def rgbhex; '%02X' * 3 % [@r, @g, @b]; end
end

img = RBPNG.new
img.IHDR({width: 512, height: 512, color_type: 2})
#img.IDAT ['00000000FFFFFF00FFFFFF000000'].pack 'H*'
r = g = b = 0
data = Array.new(512){ Array.new(512){
  c = Color.new r, g, b
  r += 4
  if r == 256
    r = 0
    g += 4
    if g == 256
      g = 0
      b += 4
    end
  end
  c
} }
img.IDAT [data.map {|x| '00' + x.map(&:rgbhex).join }.join].pack 'H*'
img.IEND
img.write 'all_colors.png'

Output (in all_colors.png) (click any of these images to enlarge them):

Output

Somewhat more interesting gradient-ish output (by changing the 4th to last line to }.shuffle }):

Output 2

And by changing it to }.shuffle }.shuffle, you get crazy color lines:

Output 3

Doorknob

Posted 2014-02-25T21:16:53.557

Reputation: 68 138

Thats really cool. Is there a way that you could make it more pretty though? Maybe randomize the pixels? Scoring is by vote. Vote for the most beautiful images made by the most elegant code. – None – 2014-02-25T23:47:01.527

1@LowerClassOverflowian Ok, edited – Doorknob – 2014-02-26T00:20:35.910

Much Better!!!!!!! – None – 2014-02-26T01:10:27.777

The last one is very much like a layer cake

– Justin – 2014-02-26T04:20:55.303

1What will happen if you changed the 4th to last line to }.shuffle }.shuffle }.shuffle? – John Odom – 2014-02-26T14:30:38.160

6@John Erm, syntax error, probably? – Doorknob – 2014-02-26T17:58:09.067

Take that back. That last one looks like kueh.

– Justin – 2014-03-02T08:04:51.507

14

Python

plasma

Using python to sort the colors by luminance, generating a luminance pattern and picking the most appropriate color. The pixels are iterated in random order so that the less favorable luminance matches that naturally happen when the list of available colors gets smaller are evenly spread throughout the picture.

#!/usr/bin/env python

from PIL import Image
from math import pi, sin, cos
import random

WIDTH = 256
HEIGHT = 128

img = Image.new("RGB", (WIDTH, HEIGHT))

colors = [(x >> 10, (x >> 5) & 31, x & 31) for x in range(32768)]
colors = [(x[0] << 3, x[1] << 3, x[2] << 3) for x in colors]
colors.sort(key=lambda x: x[0] * 0.2126 + x[1] * 0.7152 + x[2] * 0.0722)

def get_pixel(lum):
    for i in range(len(colors)):
        c = colors[i]
        if c[0] * 0.2126 + c[1] * 0.7152 + c[2] * 0.0722 > lum:
            break
    return colors.pop(i)

def plasma(x, y):
    x -= WIDTH / 2
    p = sin(pi * x / (32 + 10 * sin(y * pi / 32)))
    p *= cos(pi * y / 64)
    return 128 + 127 * p

xy = []
for x in range(WIDTH):
    for y in range(HEIGHT):
        xy.append((x, y))
random.shuffle(xy)

count = 0
for x, y in xy:
    l = int(plasma(x, y))
    img.putpixel((x, y), get_pixel(plasma(x, y)))
    count += 1
    if not count & 255:
        print "%d pixels rendered" % count

img.save("test.png")

boomlinde

Posted 2014-02-25T21:16:53.557

Reputation: 191

13

Java

import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;

/**
 *
 * @author Quincunx
 */
public class AllColorImage {

    public static void main(String[] args) {
        BufferedImage img = new BufferedImage(4096, 4096, BufferedImage.TYPE_INT_RGB);
        int num = 0;
        ArrayList<Point> points = new ArrayList<>();
        for (int y = 0; y < 4096; y++) {
            for (int x = 0; x < 4096 ; x++) {
                points.add(new Point(x, y));
            }
        }
        for (Point p : points) {
            int x = p.x;
            int y = p.y;

            img.setRGB(x, y, num);
            num++;
        }
        try {
            ImageIO.write(img, "png", new File("Filepath"));
        } catch (IOException ex) {
            Logger.getLogger(AllColorImage.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

I went for 4096 by 4096 because I couldn't figure out how to get all the colors without doing so.

Output:

Too big to fit here. This is a screenshot:

enter image description here

With a little change, we can get a more beautiful picture:

Add Collections.shuffle(points, new Random(0)); between generating the points and doing the colors:

import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;

/**
 *
 * @author Quincunx
 */
public class AllColorImage {

    public static void main(String[] args) {
        BufferedImage img = new BufferedImage(4096, 4096, BufferedImage.TYPE_INT_RGB);
        int num = 0;
        ArrayList<Point> points = new ArrayList<>();
        for (int y = 0; y < 4096; y++) {
            for (int x = 0; x < 4096 ; x++) {
                points.add(new Point(x, y));
            }
        }
        Collections.shuffle(points, new Random(0));
        for (Point p : points) {
            int x = p.x;
            int y = p.y;

            img.setRGB(x, y, num);
            num++;
        }
        try {
            ImageIO.write(img, "png", new File("Filepath"));
        } catch (IOException ex) {
            Logger.getLogger(AllColorImage.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

enter image description here

Closeup:

enter image description here

Justin

Posted 2014-02-25T21:16:53.557

Reputation: 19 757

29You call a big grey blob "beautiful"? – Doorknob – 2014-02-26T02:39:03.410

22@Doorknob Yes. I call it very beautiful. I find it amazing that all the colors can be arranged into a big grey blob. I do find the blob more interesting when I zoom in. With a little bit more detail, you can see how un-random Java's rng is. When we zoom in even more, like the second screen-shot, it becomes clear how many colors are in that thing. When I zoom even further, it looks like a Piet program. – Justin – 2014-02-26T03:05:10.610

I got the colors in smaller versions by dropping the lower bits. – Mark Jeronimus – 2014-02-26T06:29:06.907

Yes, the lower bits for r, g and b separately, but I was dealing with them as one number. – Justin – 2014-02-26T06:35:39.663

I see you have figured out teh b1t magicz in your next answer. On topic, it might be interesting experimenting with your own Random subclass that produces even less ideal random numbers. – Mark Jeronimus – 2014-02-26T06:38:16.040

@Zom-B It shows much more obvious patterns. – Justin – 2014-02-26T13:52:47.963

13

C++11

(Update: only afterwards did I notice that a similar approach has already been tried --- with more patience with regards to the number of iterations.)

For each pixel, I define a set of neighbor pixels. I define the discrepancy between two pixels to be the sum of squares of their R/G/B differences. The penalty of a given pixel is then the sum of the discrepancies between the pixel and its neighbors.

Now, I first generate a random permutation, then start picking random pairs of pixels. If swapping the two pixels reduces the sum of the total penalties of all pixels, the swap goes through. I repeat this for a million times.

The output is in the PPM format, which I have converted into PNG using standard utilities.

Source:

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <random>

static std::mt19937 rng;

class Pixel
{
public:
    int r, g, b;

    Pixel() : r(0), g(0), b(0) {}
    Pixel(int r, int g, int b) : r(r), g(g), b(b) {}

    void swap(Pixel& p)
    {
        int r = this->r,  g = this->g,    b = this->b;
        this->r = p.r;    this->g = p.g;  this->b = p.b;
        p.r = r;          p.g = g;        p.b = b;
    }
};

class Image
{
public:
    static const int width = 256;
    static const int height = 128;
    static const int step = 32;
    Pixel pixel[width*height];
    int penalty[width*height];
    std::vector<int>** neighbors;

    Image()
    {
        if (step*step*step != width*height)
        {
            std::cerr << "parameter mismatch" << std::endl;
            exit(EXIT_FAILURE);
        }

        neighbors = new std::vector<int>*[width*height];

        for (int i = 0; i < width*height; i++)
        {
            penalty[i] = -1;
            neighbors[i] = pixelNeighbors(i);
        }

        int i = 0;
        for (int r = 0; r < step; r++)
        for (int g = 0; g < step; g++)
        for (int b = 0; b < step; b++)
        {
            pixel[i].r = r * 255 / (step-1);
            pixel[i].g = g * 255 / (step-1);
            pixel[i].b = b * 255 / (step-1);
            i++;
        }
    }

    ~Image()
    {
        for (int i = 0; i < width*height; i++)
        {
            delete neighbors[i];
        }
        delete [] neighbors;
    }

    std::vector<int>* pixelNeighbors(const int pi)
    {
        // 01: X-shaped structure
        //const int iRad = 7, jRad = 7;
        //auto condition = [](int i, int j) { return abs(i) == abs(j); };
        //
        // 02: boring blobs
        //const int iRad = 7, jRad = 7;
        //auto condition = [](int i, int j) { return true; };
        //
        // 03: cross-shaped
        //const int iRad = 7, jRad = 7;
        //auto condition = [](int i, int j) { return i==0 || j == 0; };
        //
        // 04: stripes
        const int iRad = 1, jRad = 5;
        auto condition = [](int i, int j) { return i==0 || j == 0; };

        std::vector<int>* v = new std::vector<int>;

        int x = pi % width;
        int y = pi / width;

        for (int i = -iRad; i <= iRad; i++)
        for (int j = -jRad; j <= jRad; j++)
        {
            if (!condition(i,j))
                continue;

            int xx = x + i;
            int yy = y + j;

            if (xx < 0 || xx >= width || yy < 0 || yy >= height)
                continue;

            v->push_back(xx + yy*width);
        }

        return v;
    }

    void shuffle()
    {
        for (int i = 0; i < width*height; i++)
        {
            std::uniform_int_distribution<int> dist(i, width*height - 1);
            int j = dist(rng);
            pixel[i].swap(pixel[j]);
        }
    }

    void writePPM(const char* filename)
    {
        std::ofstream fd;
        fd.open(filename);
        if (!fd.is_open())
        {
            std::cerr << "failed to open file " << filename
                      << "for writing" << std::endl;
            exit(EXIT_FAILURE);
        }
        fd << "P3\n" << width << " " << height << "\n255\n";
        for (int i = 0; i < width*height; i++)
        {
            fd << pixel[i].r << " " << pixel[i].g << " " << pixel[i].b << "\n";
        }
        fd.close();
    }

    void updatePixelNeighborhoodPenalty(const int pi)
    {
        for (auto j : *neighbors[pi])
            updatePixelPenalty(j);
    }

    void updatePixelPenalty(const int pi)
    {
        auto pow2 = [](int x) { return x*x; };
        int pen = 0;
        Pixel* p1 = &pixel[pi];
        for (auto j : *neighbors[pi])
        {
            Pixel* p2 = &pixel[j];
            pen += pow2(p1->r - p2->r) + pow2(p1->g - p2->g) + pow2(p1->b - p2->b);
        }
        penalty[pi] = pen / neighbors[pi]->size();
    }

    int getPixelPenalty(const int pi)
    {
        if (penalty[pi] == (-1))
        {
            updatePixelPenalty(pi);
        }
        return penalty[pi];
    }

    int getPixelNeighborhoodPenalty(const int pi)
    {
        int sum = 0;
        for (auto j : *neighbors[pi])
        {
            sum += getPixelPenalty(j);
        }
        return sum;
    }

    void iterate()
    {
        std::uniform_int_distribution<int> dist(0, width*height - 1);       

        int i = dist(rng);
        int j = dist(rng);

        int sumBefore = getPixelNeighborhoodPenalty(i)
                        + getPixelNeighborhoodPenalty(j);

        int oldPenalty[width*height];
        std::copy(std::begin(penalty), std::end(penalty), std::begin(oldPenalty));

        pixel[i].swap(pixel[j]);
        updatePixelNeighborhoodPenalty(i);
        updatePixelNeighborhoodPenalty(j);

        int sumAfter = getPixelNeighborhoodPenalty(i)
                       + getPixelNeighborhoodPenalty(j);

        if (sumAfter > sumBefore)
        {
            // undo the change
            pixel[i].swap(pixel[j]);
            std::copy(std::begin(oldPenalty), std::end(oldPenalty), std::begin(penalty));
        }
    }
};

int main(int argc, char* argv[])
{
    int seed;
    if (argc >= 2)
    {
        seed = atoi(argv[1]);
    }
    else
    {
        std::random_device rd;
        seed = rd();
    }
    std::cout << "seed = " << seed << std::endl;
    rng.seed(seed);

    const int numIters = 1000000;
    const int progressUpdIvl = numIters / 100;
    Image img;
    img.shuffle();
    for (int i = 0; i < numIters; i++)
    {
        img.iterate();
        if (i % progressUpdIvl == 0)
        {
            std::cout << "\r" << 100 * i / numIters << "%";
            std::flush(std::cout);
        }
    }
    std::cout << "\rfinished!" << std::endl;
    img.writePPM("AllColors2.ppm");

    return EXIT_SUCCESS;
}

Varying the step of neighbors gives different results. This can be tweaked in the function Image::pixelNeighbors(). The code includes examples for four options: (see source)

example 01 example 02 example 03 example 04

Edit: another example similar to the fourth one above but with a bigger kernel and more iterations:

example 05

One more: using

const int iRad = 7, jRad = 7;
auto condition = [](int i, int j) { return (i % 2==0 && j % 2==0); };

and ten million iterations, I got this:

example 06

Jussi M

Posted 2014-02-25T21:16:53.557

Reputation: 179

11

Java

This was a much better idea. This is some very short Java code; the main method is only 13 lines long:

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;

/**
 *
 * @author Quincunx
 */
public class AllColorImage {

    public static void main(String[] args) {
        BufferedImage img = new BufferedImage(4096, 4096, BufferedImage.TYPE_INT_RGB);

        for (int r = 0; r < 256; r++) {
            for (int g = 0; g < 256; g++) {
                for (int b = 0; b < 256; b++) {
                    img.setRGB(((r & 15) << 8) | g, ((r >>> 4) << 8 ) | b, (((r << 8) | g) << 8) | b);
                }
            }
        }
        try {
             ImageIO.write(img, "png", new File("Filepath"));
        } catch (IOException ex) {
            Logger.getLogger(AllColorImage.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

Generates blocks of "color pickers". Basically, in the first block, r=0, in the second, r=1, etc. In each block, g increments with respect to x, and b with respect to y.

I really like bitwise operators. Let me break down the setRGB statement:

img.setRGB(((r & 15) << 8) | g, ((r >>> 4) << 8 ) | b, (((r << 8) | g) << 8) | b);

((r & 15) << 8) | g         is the x-coordinate to be set.
r & 15                      is the same thing as r % 16, because 16 * 256 = 4096
<< 8                        multiplies by 256; this is the distance between each block.
| g                         add the value of g to this.

((r >>> 4) << 8 ) | b       is the y-coordinate to be set.
r >>> 4                     is the same thing as r / 16.
<< 8 ) | b                  multiply by 256 and add b.

(((r << 8) | g) << 8) | b   is the value of the color to be set.
r << 8                      r is 8 bits, so shift it 8 bits to the left so that
| g                         we can add g to it.
<< 8                        shift those left 8 bits again, so that we can
| b                         add b

As a result of the bitwise operators, this takes only 7 seconds to complete. If the r & 15 is replaced with r % 16, it takes 9 seconds.

I chose the 4096 x 4096

Output (screenshot, too big otherwise):

enter image description here

Output with evil grin drawn on it by freehand-red-circles:

enter image description here

Justin

Posted 2014-02-25T21:16:53.557

Reputation: 19 757

2Link to the original so I can verify the validness (count colors) – Mark Jeronimus – 2014-02-26T06:31:23.997

@Zom-B The file is too large. It takes too long to upload it. – Justin – 2014-02-26T06:36:36.543

@Zom-B I mean, it does upload, but this website takes forever before it inserts it, so I don't get a link to it. – Justin – 2014-02-26T06:39:44.200

2Lol! I forgot I can run Java code. The first image passes, and I can't reproduce the second image (lol)☺ – Mark Jeronimus – 2014-02-26T06:40:16.360

16The freehand circles all have the same color, disqualified. :P – Nick T – 2014-02-27T00:16:03.900

3@Quincunx +1 if you can draw a scary face and still keep the color requirements! – Jason C – 2014-02-27T10:29:52.470

2@JasonC See my answer. Credit goes to Quincunx for the inspiration. – Level River St – 2014-02-27T21:18:04.807

11

Not the most elegant code, but interesting on two counts: Computing the number of colors from the dimensions (as long as the product of dimensions is a power of two), and doing trippy color space stuff:

void Main()
{
    var width = 256;
    var height = 128;
    var colorCount = Math.Log(width*height,2);
    var bitsPerChannel = colorCount / 3;
    var channelValues = Math.Pow(2,bitsPerChannel);
    var channelStep = (int)(256/channelValues);

    var colors = new List<Color>();

    var m1 = new double[,] {{0.6068909,0.1735011,0.2003480},{0.2989164,0.5865990,0.1144845},{0.00,0.0660957,1.1162243}};
    for(var r=0;r<255;r+=channelStep)
    for(var g=0;g<255;g+=channelStep)
    for(var b=0;b<255;b+=channelStep)   
    {
        colors.Add(Color.FromArgb(0,r,g,b));
    }
    var sortedColors = colors.Select((c,i)=>
                            ToLookupTuple(MatrixProduct(m1,new[]{c.R/255d,c.G/255d,c.B/255d}),i))
                            .Select(t=>new
                                            {
                                                x = (t.Item1==0 && t.Item2==0 && t.Item3==0) ? 0 : t.Item1/(t.Item1+t.Item2+t.Item3),
                                                y = (t.Item1==0 && t.Item2==0 && t.Item3==0) ? 0 :t.Item2/(t.Item1+t.Item2+t.Item3),
                                                z = (t.Item1==0 && t.Item2==0 && t.Item3==0) ? 0 :t.Item3/(t.Item1+t.Item2+t.Item3),
                                                Y = t.Item2,
                                                i = t.Item4
                                            })
                            .OrderBy(t=>t.x).Select(t=>t.i).ToList();
    if(sortedColors.Count != (width*height))
    {
        throw new Exception(string.Format("Some colors fell on the floor: {0}/{1}",sortedColors.Count,(width*height)));
    }
    using(var bmp = new Bitmap(width,height,PixelFormat.Format24bppRgb))
    {
        for(var i=0;i<colors.Count;i++)
        {
            var y = i % height;
            var x = i / height;

            bmp.SetPixel(x,y,colors[sortedColors[i]]);
        }
        //bmp.Dump(); //For LINQPad use
        bmp.Save("output.png");
    }
}
static Tuple<double,double,double,int>ToLookupTuple(double[] t, int index)
{
    return new Tuple<double,double,double,int>(t[0],t[1],t[2],index);
}

public static double[] MatrixProduct(double[,] matrixA,
    double[] vectorB)
{
    double[] result=new double[3];
    for (int i=0; i<3; ++i) // each row of A
        for (int k=0; k<3; ++k)
            result[i]+=matrixA[i,k]*vectorB[k];
    return result;
}

Some interesting variations can be had just by changing the OrderBy clause:

x:

enter image description here

y:

enter image description here

z:

enter image description here

Y:

enter image description here

I wish I could figure out what was causing the odd lines in the first three

Matt Sieker

Posted 2014-02-25T21:16:53.557

Reputation: 401

2Those odd lines are probably the bias of some sort or look-up method (binary search / quicksort?) – Mark Jeronimus – 2014-02-26T21:55:34.703

I actually really like the lines here. – Jason C – 2014-02-27T22:53:59.697

11

Java

unintentionally inspired by the color chooser solution but in 4096x4096; got some other ideas in pipeline already

import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import javax.imageio.ImageIO;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author LH
 */
class Pix
{
    public static void main(String[] devnull) throws Exception
    {
        int[] x = new int[4096*4096];//colorstream
        int idx=0;
        BufferedImage i = new BufferedImage(4096, 4096, BufferedImage.TYPE_INT_RGB);
        for (int j = 0; j < 256; j++)//red //255=interesting quirk
        {
            for (int k = 0; k < 16; k++)//blue_MSBs
            {
                for (int l = 0; l < 256; l++)//green 255=interesting quirk
                {
                    for (int m = 0; m < 16; m++)//blue_lsbs
                    {

                        int val = (j<<16)+(k<<12)+(l)+(m<<8);
                        //System.out.println("(idx|j|k|l|m|val)=("+idx+"|"+j+"|"+k+"|"+l+"|"+m+"|"+val+"|)");
                        x[idx]=val;
                        idx++;
                    }
                }
            }
        }
        for (int j = 0; j < x.length; j++)
        {
            int h=j/4096;
            int w=j%4096;
            i.setRGB(w, h, x[j]);
        }
        //validator sorting and checking that all values only have 1 difference
        Arrays.sort(x);
        int diff=0;
        for (int j = 1; j < x.length; j++)
        {
            int ndiff=x[j]-x[j-1];
            if(ndiff!=diff)
            {
                System.out.println(ndiff);
            }
            diff=ndiff;

        }
        OutputStream out = new BufferedOutputStream(new FileOutputStream("RGB24.png"));
        ImageIO.write(i, "png", out);
    }
}

Preview (to get full 4096x4096 run the java prog

masterX244

Posted 2014-02-25T21:16:53.557

Reputation: 3 942

11

AWK and friends.

Look mom! I've shaken the colors away!

The 'x' file:

BEGIN {
    N=5
    C=2^N
    print "P3\n"2^(3*N-int(3*N/2))" "2^int(3*N/2)"\n"C-1
    for(x=0;x<C;x++)
        for(y=0;y<C;y++)
            for(z=0;z<C;z++)
                print x^4+y^4+z^4"\t"x" "y" "z | "sort -n | cut -f2-" 
}

Run:

awk -f x > x.ppm

Output:

N=5:

N=5.png

N=6:

N=6.png

user19214

Posted 2014-02-25T21:16:53.557

Reputation:

10

Go

Here's my take, using Golang and its default packages to generate a GIF containing all the colors:

package main

import (
    "image"
    "image/color"
    "image/gif"
    "os"
)

func main() {
    var idx uint8
    frames := make([]*image.Paletted, 256)
    delay := make([]int, 256)
    for x := 0; x < 256; x++ {
        pal := make(color.Palette, 129)
        pal[0] = color.RGBA{0, 0, 0, 0}
        idx = 1
        for y := 0; y < 128; y++ {
            pal[idx] = color.RGBA{uint8(x%32) * 8, uint8(y%32) * 8, uint8(x/32+(y/32*8)) * 8, 255}
            idx++
        }
        img := image.NewPaletted(image.Rect(0, 0, 256, 128), pal)
        idx = 1
        for y := 0; y < 128; y++ {
            img.SetColorIndex(x, y, idx)
            idx++
        }
        frames[x] = img
        delay[x] = 1
    }
    jif := gif.GIF{frames, delay, 1}
    file, err := os.Create("EveryColor.gif")
    if err != nil {
        panic(err)
    }
    err = gif.EncodeAll(file, &jif)
    if err != nil {
        panic(err)
    }
    file.Close()

}

And the output:

animated gif containing 2^15 colors

Not pretty like many of the other answers, but it uses the GIF format in an interesting way.

um3k

Posted 2014-02-25T21:16:53.557

Reputation: 361

8

I used to make these infinite recursive sorters when I was learning to write code, very rewarding to program so little. It will eventually converge around something like below for random colors - but it has the very unique property of continuously walking.

Converges around something like this for random colors.

If you're brave enough to run the 4096x4096 version in your browser with each pixel a different color: god speed to that one poor CPU core handling the JavaScript thread. I am in no way responsible for that whirring fan noise you now hear, or the soon to follow laptop fire.

Source (or here as a gist https://gist.github.com/adrianseeley/9516453):

<canvas id="canvas" width="256", height="256"></canvas>
<script type="text/javascript">
    var canvas = document.getElementById("canvas");
    var ctx = canvas.getContext("2d");
    var imgdata = ctx.createImageData(canvas.width, canvas.height);
    function set_pixel (x, y, r, g, b) { var index = (x + y * imgdata.width) * 4; imgdata.data[index + 0] = r; imgdata.data[index + 1] = g; imgdata.data[index + 2] = b; imgdata.data[index + 3] = 255; };
    function swap_pixels (x1, y1, x2, y2) { var p1 = get_pixel(x1, y1); var p2 = get_pixel(x2, y2); set_pixel(x1, y1, p2[0], p2[1], p2[2]); set_pixel(x2, y2, p1[0], p1[1], p1[2]); };
    function get_pixel (x, y) { var index = (x + y * imgdata.width) * 4; return [imgdata.data[index + 0], imgdata.data[index + 1], imgdata.data[index + 2]]; };
    function draw () { ctx.putImageData(imgdata, 0, 0); };
    function prep () {
        // meant for 4096x4096 canvas, slow as fuck
        var r = 0;
        var g = 0;
        var b = 0;
        for (var x = 0; x < canvas.width; x++) {
            for (var y = 0; y < canvas.height; y++) {
                set_pixel(x, y, r, g, b);
                b++;
                if (b > 255) {
                    b = 0;
                    g++;
                    if (g > 255) {
                        g = 0;
                        r++;
                    }
                }
            }
        }
        draw();
    };
    function preprandom () {
        // way prettier
        for (var x = 0; x < canvas.width; x++) for (var y = 0; y < canvas.height; y++) set_pixel(x, y, Math.floor(Math.random() * 255), Math.floor(Math.random() * 255), Math.floor(Math.random() * 255));
        draw();
    };
    function multi_pass_sort (lambda_x, lambda_y) {
        var sorted = true;
        for (var x = 1; x < canvas.width; x++) for (var y = 0; y < canvas.height; y++) if (lambda_x(get_pixel(x - 1, y), get_pixel(x, y))) { swap_pixels(x - 1, y, x, y); sorted = false; }
        for (var x = 0; x < canvas.width; x++) for (var y = 1; y < canvas.height; y++) if (lambda_y(get_pixel(x, y - 1), get_pixel(x, y))) { swap_pixels(x, y - 1, x, y); sorted = false; }
        draw();
        if (!sorted) setTimeout(function () { multi_pass_sort(lambda_x, lambda_y); }, 0);
        else console.log('done!');
    };
    //prep();
    preprandom();
    setTimeout(function () { multi_pass_sort(function lambda_x (a, b) { return a[0] > b[0] || a[1] > b[1]; }, function lambda_y (a, b) { return a[1] > b[1] || a[2] > b[2]; });
    }, 1000);
</script>

Try experimenting with the lambda_x and lambda_y functions and enjoy your exploration of the multidimensional!

user19449

Posted 2014-02-25T21:16:53.557

Reputation: 181

Can't find the run link. – CalculatorFeline – 2017-06-30T19:25:06.833

1Haha, plus one for "laptop fire"! Welcome to the site. – Jonathan Van Matre – 2014-03-19T15:53:37.217

7

BASH with *nix commands

Some oneliners generating PPM format pictures

Ok... this sure will not be the most popular solution, but I think it is worth being mentioned because it shows (one way) how to build raster graphics with shell commands:

{ printf 'P3\n# x.ppm\n256 128\n31\n' ; printf '%s\n' {0..31}.{0..31}.{0..31} | tr . ' ' ; } >x.ppm

Output:

x

You can add other commands to that pipe to shuffle or rearrange the pixels... let's try 'sort':

{ printf 'P3\n# x.ppm\n256 128\n31\n' ; printf '%s\n' {0..31}.{0..31}.{0..31} | sort | tr . ' ' ; } >y.ppm

y

Ok... that was boring!

How about some random?

{ printf 'P3\n# x.ppm\n256 128\n31\n' ; printf '%s\n' ${RANDOM}.{0..31}.{0..31}.{0..31} | sort | awk -F. '{ print $2" "$3" "$4}' ; } >z.ppm

z

If you think this is fun, read about the PBM, PGM and PPM picture file formats.

They may look like a fossil but who says that this means "no fun!"?

user19214

Posted 2014-02-25T21:16:53.557

Reputation:

It's no fossil if they have practical use, namely plain text format (and not BASE64 encoded or stuff) – Mark Jeronimus – 2014-03-21T11:44:16.490

"They may look like a fossil" is not "they are a fossil". Maybe I should have been more verbose to be understood faster... – None – 2014-03-21T15:15:33.213

6

Python

Each run produces one of ~1.3 trillion possible outputs. Requires ImageMagick's display command.

#!/usr/bin/env python
from itertools import product
from math import log
from subprocess import Popen, PIPE
from random import shuffle

w = 256
h = 128
entropy = int(log(w * h, 2))
colors = 3
byte = 256
color_range = xrange(0, byte, byte / 2**(entropy/colors))
output = bytearray(w * h * colors)
bit_permutation = range(entropy)
shuffle(bit_permutation)

def shuffle_bits(n):
    shuffled = 0
    for b, bit in zip(bit_permutation, bin(n)[2:].zfill(entropy)):
        if bit == '1': shuffled += 1 << b
    return shuffled

for channels, pos in zip(product(color_range, repeat=colors),
                         map(shuffle_bits, xrange(w * h))):
    output[colors*pos:colors*(pos+1)] = ''.join(chr(k) for k in channels)

proc = Popen(
    ['display', '-size', '%sx%s' % (w, h), '-depth', '8', 'rgb:-'], stdin=PIPE)
proc.communicate(str(output))

Some outputs:

output example 1
output example 2
output example 3

Try changing w / h to 512 / 512 or 2048 / 1024 for more colors.

Fraxtil

Posted 2014-02-25T21:16:53.557

Reputation: 2 495

51.3 trillion frames at 30 FPS = 1373 years. Let's get that video started! – Jason C – 2014-03-01T02:51:35.567

6

Java, 24-bit (4096 by 4096)

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.swing.JFrame;


public class ALLTHECOLORS{
    static class ImageComponent extends Component{
        private static final long serialVersionUID = 1L;
        private BufferedImage i;
        public ImageComponent(BufferedImage i){
            this.i=i;
        }
        public int width(){
            return 512;
        }
        public int height(){
            return 512;
        }
        public void paint(Graphics g){
            g.drawImage(i, 0, 0,width(),height(),null);
        }
    }
    static class ColorIterator implements Iterator<Color>{
        int r=0,g=0,b=0;
        public boolean hasNext() {
            return r<256;
        }
        public Color next(){
            Color c=new Color(255-r,255-g,255-b);
            b++;
            if(b>255){
                b=0;
                g++;
            }
            if(g>255){
                g=0;
                r++;
            }
            return c;
        }   
    }
    static double d(Color a,Color b){
        int dr=a.getRed()-b.getRed();
        int dg=a.getGreen()-b.getGreen();
        int db=a.getBlue()-b.getBlue();
        return Math.sqrt(dr*dr+dg*dg+db*db);
    }
    static int hue(Color c){
        return (int)(0.5+90*Color.RGBtoHSB(c.getRed(),c.getGreen(),c.getBlue(),null)[0]);
    }
    static int sat(Color c){
        return (int)(0.5+7*Color.RGBtoHSB(c.getRed(),c.getGreen(),c.getBlue(),null)[1]);
    }
    public static void main(String[]args) throws IOException{
        Random rand=new Random(System.currentTimeMillis()%1000);
        BufferedImage img=new BufferedImage(4096,4096,BufferedImage.TYPE_INT_RGB);
        Iterator<Color>it=new ColorIterator();
        List<Color>col=new ArrayList<>(256*256*256);
        while(it.hasNext())col.add(it.next());
        for(int i=0;i<col.size();i++){
            col.set(i,col.set(rand.nextInt(i+1),col.get(i)));
        }
        col.sort((a,b)->{
            int dels=sat(a)-sat(b);
            int delh=hue(a)-hue(b);
            return dels!=0?dels:delh;
        });
        it=col.iterator();
        for(int x=0;x<4096;x++){
            for(int y=0;y<4096;y++){
                img.setRGB(x, y,it.next().getRGB());
            }
        }
        ImageIO.write(img,"jpg",new File("the_colors.jpg"));
        JFrame frame=new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        ImageComponent c=new ImageComponent(img);
        frame.add(c);
        frame.setSize(new Dimension(c.width(),c.height()));
        frame.setVisible(true);
    }
}

It saves the full 4096 by 4096 version to the file "the_colors.jpg" and then draws a 1/8-size preview onto a JFrame. It typically takes 35-40 seconds to generate the result.

This is a fairly representative result (the 1/8-size preview is shown here, use the link to view at full size)

enter image description here

And here's a 10x close-up of a 25-by-25 portion of one of the blue-green sections: enter image description here

SuperJedi224

Posted 2014-02-25T21:16:53.557

Reputation: 11 342

5

I was inspired by my answer from another popularity contest question which used iterative depth-first search to generate a maze. I used a variation of the algorithm used in a couple other answers that determines the best color to place at a certain pixel given the colors occupying the neighboring pixels. The algorithm starts at the center pixel and works its way visiting every pixel in the image in a random path.

The code is set up so that one can change the fields to give different results, such as adjusting the width and height of the image (and all relevant colors are used depending on the dimensions), random seed, and whether to shuffle or sort the colors before placing them from the list onto the image. Sorting is done using the HSV color model.

The resulting image will appear in a window once the image generation has finished. Scroll panes may be added for convenience in larger images. Assuming searchLimit = 8, on my machine it takes seconds to produce 256x128 and 512x512 images, around ten minutes for 2048x1024 images, and half a day or longer for 4096x4096 images.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.function.BinaryOperator;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

public class RGBImage {
    /**
     * Width and height of the image. Choose either of:
     * 128x256  256x128  512x512  1024x2048  2048x1024  4096x4096
     **/
    private final int width = 512, height = 512;

    /**
     * Number of colors from the front of the list to compare when searching
     * for the best color. Note: the higher this value, the longer it will take
     * to produce the image.
     **/
    private final int searchLimit = 8;

    /**
     * Seed for the color list shuffling and depth-first search algorithm.
     * Using the same seed will yield the same image provided no other
     * algorithm settings were altered. Add 'L' or 'l' after the value; e.g. 1L
     * Using 'null' will create a random image every time the program is run.
     **/
    private final Long seed = null;
    private final Random rand = new Random(seed == null ? System.nanoTime() : seed);

    /**
     * List of all colors.
     **/
    private List<Integer> colors;

    /**
     * Comparator to sort the colors. Use any of
     * null  byHue()  bySaturation()  byValue()  byRed()  byGreen()  byBlue()
     *
     * Using 'null' causes the colors to be sorted in ascending order by their
     * numerical value.
     * You can sort by one method first and then by another; e.g.
     * byHue().thenComparing(bySaturation());
     **/
    private final Comparator<Integer> comparator = null;

    /**
     * Flags for shuffling and sorting the values in the list.
     * If both flags are 'false', the colors in the list will be sorted in
     * ascending order by their numerical value.
     * If both flags are 'true', shuffling will occur first, then sorting. This
     * may give interesting results!
     **/
    private final boolean shuffle = false, sort = false;

    /**
     * The name to save the image as. The extension is "png" and should not be
     * included.
     **/
    private final String fileName = "output";

    public RGBImage() {
        init();

        int[] pixels = new int[width * height];
        Arrays.fill(pixels, -1);
        constructArray(pixels, new short[]{(short) (width/2), (short) (height/2)});

        BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        img.setRGB(0, 0, width, height, pixels, 0, width);

        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame();
            JPanel panel = new JPanel() {
                @Override
                public Dimension getPreferredSize() {
                    return new Dimension(width, height);
                }

                @Override
                protected void paintComponent(Graphics g) {
                    super.paintComponent(g);
                    g.drawImage(img, 0, 0, this);
                }
            };
            frame.add(new JScrollPane(panel), BorderLayout.CENTER);

            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            if (width <= 512)
                frame.pack();
            else
                frame.setSize(750, 750);
            frame.setVisible(true);
        });

        try {
            ImageIO.write(img, "png", new File(fileName + ".png"));
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    private void init() {
        colors = new ArrayList<>(width * height);
        int bitsPerComp = (int) (Math.log(width * height) / Math.log(2)) / 3;
        int pow2 = 1 << bitsPerComp, fact256 = 256 / pow2;

        float adj = 255f / (256 - fact256);
        for (int k = 0; k < width * height; k++) {
            int r = (int) (((k >> (bitsPerComp * 2)) % pow2) * fact256 * adj) << 16,
                g = (int) (((k >> bitsPerComp) % pow2) * fact256 * adj) << 8,
                b = (int) ((k % pow2) * fact256 * adj);
            colors.add(r | g | b);
        }
        if (shuffle)
            Collections.shuffle(colors);
        if (sort)
            Collections.sort(colors, comparator);
    }

    private void constructArray(int[] pixels, short[] coord) {
        Deque<short[]> path = new LinkedList<>();
        path.add(coord);
        boolean up, right, down, left;
        while (true) {
            coord = path.peekFirst();
            int col = bestColor(pixels, coord);
            pixels[coord[1] * width + coord[0]] = col;
            colors.remove(Integer.valueOf(col));

            if (isSurrounded(pixels, coord)) {
                do {
                    coord = path.removeFirst();
                } while (isSurrounded(pixels, coord) && !path.isEmpty());
            }

            if (path.isEmpty())
                break;

            up = coord[1] != 0 && pixels[(coord[1] - 1) * width + coord[0]] == -1;
            right = coord[0] != width - 1 && pixels[coord[1] * width + coord[0] + 1] == -1;
            down = coord[1] != height - 1 && pixels[(coord[1] + 1) * width + coord[0]] == -1;
            left = coord[0] != 0 && pixels[coord[1] * width + coord[0] - 1] == -1;

            byte size = 0;
            if (up) size++;
            if (right) size++;
            if (down) size++;
            if (left) size++;

            byte[] possible = new byte[size];
            if (up) possible[--size] = 0;
            if (right) possible[--size] = 1;
            if (down) possible[--size] = 2;
            if (left) possible[--size] = 3;

            switch (possible[rand.nextInt(possible.length)]) {
                case 0: path.addFirst(new short[] {coord[0], (short) (coord[1] - 1)}); break;
                case 1: path.addFirst(new short[] {(short) (coord[0] + 1), coord[1]}); break;
                case 2: path.addFirst(new short[] {coord[0], (short) (coord[1] + 1)}); break;
                case 3: path.addFirst(new short[] {(short) (coord[0] - 1), coord[1]}); break;
            }
        }
    }

    private boolean isSurrounded(int[] pixels, short[] c) {
        return (c[1] == 0 ? true : pixels[(c[1] - 1) * width + c[0]] != -1) &&
               (c[0] == width - 1 ? true : pixels[c[1] * width + c[0] + 1] != -1) &&
               (c[1] == height - 1 ? true : pixels[(c[1] + 1) * width + c[0]] != -1) &&
               (c[0] == 0 ? true : pixels[c[1] * width + c[0] - 1] != -1);
    }

    private int bestColor(int[] pixels, short[] coord) {
        return colors
                .subList(0, Math.min(colors.size(), searchLimit))
                .parallelStream()
                .reduce(BinaryOperator.minBy(
                    Comparator.comparingDouble(col ->
                        Arrays.stream(getNeighbors(coord))
                            .filter(s -> s != null)
                            .mapToInt(s -> pixels[s[1] * width + s[0]])
                            .filter(i -> i != -1)
                            .mapToDouble(i -> HSVdiff(col, i))
                            .sum()
                    )
                ))
                .orElseThrow(() -> new RuntimeException("No best color found"));
    }

    private short[][] getNeighbors(short[] arr) {
        short[][] res = new short[8][2];
        byte index = 0;
        for (; index < 8; index++)
            res[index] = null;
        index = 0;
        for (short cx = (short) (arr[0] - 1); cx <= arr[0] + 1; cx++) {
            if (cx < 0 || cx == width) continue;
            for (short cy = (short) (arr[1] - 1); cy <= arr[1] + 1; cy++) {
                if (cy < 0 || cy == height) continue;
                if (cx == arr[0] && cy == arr[1]) continue;
                res[index++] = new short[] {cx, cy};
            }
        }
        return res;
    }

    private double HSVdiff(int c1, int c2) {
        float[] hsv1 = Color.RGBtoHSB(c1 >> 16, (c1 >> 8) & 0xff, c1 * 0xff, null),
                hsv2 = Color.RGBtoHSB(c2 >> 16, (c2 >> 8) & 0xff, c2 * 0xff, null);
        return Math.pow(hsv2[0] - hsv1[0], 2) + Math.pow(hsv2[1] - hsv1[1], 2) + Math.pow(hsv2[2] - hsv1[2], 2);
    }

    private Comparator<Integer> byHue() {
        return Comparator.comparingDouble(col -> Color.RGBtoHSB(col >> 16, (col >> 8) & 0xff, col & 0xff, null)[0]);
    }

    private Comparator<Integer> bySaturation() {
        return Comparator.comparingDouble(col -> Color.RGBtoHSB(col >> 16, (col >> 8) & 0xff, col & 0xff, null)[1]);
    }

    private Comparator<Integer> byValue() {
        return Comparator.comparingDouble(col -> Color.RGBtoHSB(col >> 16, (col >> 8) & 0xff, col & 0xff, null)[2]);
    }

    private Comparator<Integer> byRed() {
        return Comparator.comparingInt(col -> col >> 16);
    }

    private Comparator<Integer> byGreen() {
        return Comparator.comparingInt(col -> (col >> 8) & 0xff);
    }

    private Comparator<Integer> byBlue() {
        return Comparator.comparingInt(col -> col & 0xff);
    }

    public static void main(String[] args) {
        new RGBImage();
    }
}

Here's a random 512x512 image, unsorted (colors are stored in the list in ascending numerical order), searchLimit = 8:

512x512_search8

The next three are 512x512, use seed = 24680L, and shuffled the colors in the list without sorting them afterward. The searchLimit was the only thing that changed between them, producing interesting results.

searchLimit = 8:

512x512_search8_seed24680_shuffle

searchLimit = 128:

512x512_search128_seed24680_shuffle

searchLimit = 2048:

512x512_search2048_seed24680_shuffle

Some 256x128 images, seed = 24680, searchLimit = 64, the first sorted by hue, the second sorted by saturation, the third sorted by value, and the fourth shuffled and then sorted by value:

256x128_search64_seed24680_sortHue 256x128_search64_seed24680_sortSat 256x128_search64_seed24680_sortVal 256x128_search64_seed24680_shuffle_sortVal

Linked are 2048x1024 images, with seed = 24680 and the colors sorted by hue. Some interesting differences arise when modifying the searchLimit again. The ones used here are searchLimit = 8 and searchLimit = 512.

And last, but certainly not least, linked is a random 4096x4096 image with searchLimit = 8 and sorted by hue.

TNT

Posted 2014-02-25T21:16:53.557

Reputation: 2 442

4

Processing

Uses the same algorithm as the given example, but gives an animated output! Isn't it fun just to stare at your computer screen as the pixels slowly fill up one by one?

void setup(){
  size(256,128);
  background(0);
  frameRate(2400); //adjust as needed
}
int i=0;
void draw() {
  stroke(i<<3&255,i>>2&255,i>>7&255);
  point(i&255,i/256);
  i++;
}

See it run online here.

Here's a screenshot at the end of the animation:

enter image description here

user12205

Posted 2014-02-25T21:16:53.557

Reputation: 8 752

You should change the point call to point(i&255,floor(i/256)); This will stop the second half being shifted down by one pixel with processing.js – curiousdannii – 2014-06-09T02:31:08.217

4

Javascript (jQuery), HTML and CSS

Result

Javascript will create a table containing one cell for each color. jQuery is used for styling table cells.

$(function() {
    $container = $('body > main > table#container > tbody');

    $td = $('<td>0</td>');

    var reds = [];
    var greens = [];
    var blues = [];

    for (var i = 0; i <= 256; i = i + 8)
    {
        reds.push(i);
        greens.push(i);
        blues.push(i);
    }

    var y_len = (reds.length * 4);
    var x_len = greens.length;
    var z_len = blues.length;

    var z = 0;
    var z_end = 0;

    for (var y = 0; y < y_len; y++)
    {
        $row = $('<tr></tr>');

        tmp_z = ((y % 4) * 8);
        tmp_z_end = (tmp_z + 8);

        for (var x = 0; x < x_len; x++)
        {
            for (var z = tmp_z; z < tmp_z_end; z++)
            {
                $pixel = $td
                    .clone()
                    .css({ 'background-color':
                        'rgb(' + reds[x] +','
                            + greens[Math.floor(y / 4)] + ','
                            + blues[z] + ')' })
                ;

                $row.append($pixel);
            }
        }

        $container.append($row, "\n");
    }
});

Result (warning: your browser will take some time to display the result)

Source

A.L

Posted 2014-02-25T21:16:53.557

Reputation: 1 245

4

Java 8

The program attempts to color a Mandelbrot using every color. Naturally, this ensures that there are two parts of the program that take a long time: the color generation, and the Mandelbrot generation.

For each pixel of the image, I compute whether it is in or out of the Mandelbrot, before estimating the exterior difference for each exterior point. The interior distances are currently all set to 0. At this point, we sort according to the distance to the Mandelbrot Set, then grab the (sorted) color at the same position in the sort as the pixel.

To sort the color, I compute the hue and saturation according to these formulae. I sort by saturation, with hue coming in afterwards.

package mandelbrot;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.List;

public class Mandelbrot {
    public static final int WIDTH = 4096;
    public static final int HEIGHT = 4096;
    public static final int N = 1000; // Number of iterations
    public static final double R = 2; // Bailout radius
    public static final double MIN_X = -2.2;
    public static final double MAX_X = 1.4;
    public static final double MIN_Y = -1.2;
    public static final double MAX_Y = 1.2;

    private static double discreteToReal(int value, int maxValue, double min, double max) {
        return ((double) value) / maxValue * (max - min) + min;
    }

    private static boolean isDefinitelyInside(double x, double y) {
        double temp = x - 0.25;
        double q = temp * temp + y * y;
        return q * (q + (x - 0.25)) < 0.25 * y * y;
    }

    private static boolean isDefinitelyOutside(double x, double y) {
        if (!(-2 <= x && x <= 0.7)) return true;
        if (!(-1.2 <= y && y <= 1.2)) return true;
        if (x * x + y * y < 2 * 2) return true;
        return false;
    }

    private static boolean isInsideMandelbrot(double x0, double y0) {
        if (isDefinitelyInside(x0, y0)) return true;
        if (isDefinitelyOutside(x0, y0)) return false;

        double x = 0.0;
        double y = 0.0;
        int numIterations = 0;
        while (x * x + y * y < R * R && numIterations < N) {
            double xtemp = x * x - y * y + x0;
            y = 2 * x * y + y0;
            x = xtemp;
            numIterations++;
        }

        return x * x + y * y < R * R;
    }

    private static double distanceToMandelbrot(int px, int py) {
        double x0 = discreteToReal(px, WIDTH, MIN_X, MAX_X);
        double y0 = discreteToReal(py, WIDTH, MIN_Y, MAX_Y);

        if (isInsideMandelbrot(x0, y0)) return interiorDistanceEstimate(x0, y0);
        return exteriorDistanceEstimate(x0, y0);
    }

    private static double exteriorDistanceEstimate(double x0, double y0) {
        double pn_x = x0;
        double pn_y = y0;
        double derivative_pn = 1;

        for (int i = 0; i < N; i++) {
            double next_pn_x = pn_x * pn_x - pn_y * pn_y + x0;
            double next_pn_y = 2 * pn_x * pn_y + y0;
            double next_d_pn = 2 * pn_x * derivative_pn + 1;
            pn_x = next_pn_x;
            pn_y = next_pn_y;
            derivative_pn = next_d_pn;
        }

        double temp = Math.hypot(pn_x, pn_y);
        return 2 * temp * Math.log(temp) / Math.abs(derivative_pn);
    }

    private static double interiorDistanceEstimate(double x0, double y0) {
        return 0;
    }

    private static BufferedImage computeMandelbrot(List<Color> colors) {
        double[][] distances = new double[HEIGHT][WIDTH];

        double start = System.currentTimeMillis() * 1e-3;
        System.out.println("\tGenerating points...");

        List<Point> points = new ArrayList<>(WIDTH * HEIGHT);
        for (int x = 0; x < WIDTH; x++) {
            for (int y = 0; y < HEIGHT; y++) {
                points.add(new Point(x, y));
            }
        }
        Collections.shuffle(points, new Random(133));

        System.out.printf("\tElapsed time: %f%n", 1e-3 * System.currentTimeMillis() - start);
        start = 1e-3 * System.currentTimeMillis();
        System.out.println("\tCalculating Mandelbrot...");

        points.parallelStream()
                .forEach(xy -> {
                    int x = xy.x;
                    int y = xy.y;
                    distances[y][x] = distanceToMandelbrot(x, y);
                });

        System.out.printf("\tElapsed time: %f%n", 1e-3 * System.currentTimeMillis() - start);
        start = 1e-3 * System.currentTimeMillis();
        System.out.println("\tAttempting to match with colors...");

        Collections.sort(points, (a, b) -> {
            double bDistance = distances[b.y][b.x];
            double aDistance = distances[a.y][a.x];
            return Double.compare(aDistance, bDistance);
        });

        System.out.printf("\tElapsed time: %f%n", 1e-3 * System.currentTimeMillis() - start);
        start = 1e-3 * System.currentTimeMillis();
        System.out.println("\tConstructing image...");

        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);

        Iterator<Color> colorIterator = colors.iterator();
        for (Point xy : points) {
            image.setRGB(xy.x, xy.y, colorIterator.next().getRGB());
        }

        System.out.printf("\tElapsed time: %f%n", 1e-3 * System.currentTimeMillis() - start);

        return image;
    }

    public static void main(String[] args) throws IOException {
        double start = System.currentTimeMillis() * 1e-3;
        double veryBeginning = start;
        System.out.println("Generating colors...");
        List<Color> colors = new ArrayList<>(WIDTH * HEIGHT);

        for (int rgb = 0; rgb < WIDTH * HEIGHT; rgb++) {
            colors.add(new Color(rgb));
        }
        Collections.shuffle(colors, new Random(0));

        System.out.printf("Elapsed time: %f%n", 1e-3 * System.currentTimeMillis() - start);
        start = 1e-3 * System.currentTimeMillis();
        System.out.println("Sorting colors...");

        colors.sort((a, b) -> {
            BigDecimal r1 = BigDecimal.valueOf(a.getRed(), 100).divide(BigDecimal.valueOf(255), RoundingMode.HALF_DOWN);
            BigDecimal g1 = BigDecimal.valueOf(a.getGreen(), 100).divide(BigDecimal.valueOf(255), RoundingMode.HALF_DOWN);
            BigDecimal b1 = BigDecimal.valueOf(a.getBlue(), 100).divide(BigDecimal.valueOf(255), RoundingMode.HALF_DOWN);

            BigDecimal r2 = BigDecimal.valueOf(b.getRed(), 100).divide(BigDecimal.valueOf(255), RoundingMode.HALF_DOWN);
            BigDecimal g2 = BigDecimal.valueOf(b.getGreen(), 100).divide(BigDecimal.valueOf(255), RoundingMode.HALF_DOWN);
            BigDecimal b2 = BigDecimal.valueOf(b.getBlue(), 100).divide(BigDecimal.valueOf(255), RoundingMode.HALF_DOWN);

            BigDecimal cmax1 = r1.max(g1).max(b1);
            BigDecimal cmin1 = r1.min(g1).min(b1);

            BigDecimal cmax2 = r2.max(g2).max(b2);
            BigDecimal cmin2 = r2.min(g2).min(b2);

            BigDecimal delta1 = cmax1.subtract(cmin1);
            BigDecimal delta2 = cmax2.subtract(cmin2);

            BigDecimal h1 = delta1.compareTo(BigDecimal.ZERO) == 0 ?
                    BigDecimal.ZERO
                    : cmax1.compareTo(r1) == 0 ?
                    BigDecimal.valueOf(60).multiply(g1.subtract(b1).divide(delta1, RoundingMode.HALF_DOWN))
                    : cmax1.compareTo(g1) == 0 ?
                    BigDecimal.valueOf(60).multiply(b1.subtract(r1).divide(delta1, RoundingMode.HALF_DOWN).add(BigDecimal.valueOf(2)))
                    : BigDecimal.valueOf(60).multiply(r1.subtract(g1).divide(delta1, RoundingMode.HALF_DOWN)).add(BigDecimal.valueOf(4));
            if (h1.compareTo(BigDecimal.ZERO) < 0) {
                h1 = h1.add(BigDecimal.valueOf(360));
            }

            BigDecimal h2 = delta2.compareTo(BigDecimal.ZERO) == 0 ?
                    BigDecimal.ZERO
                    : cmax2.compareTo(r2) == 0 ?
                    BigDecimal.valueOf(60).multiply(g2.subtract(b2).divide(delta2, RoundingMode.HALF_DOWN))
                    : cmax2.compareTo(g2) == 0 ?
                    BigDecimal.valueOf(60).multiply(b2.subtract(r2).divide(delta2, RoundingMode.HALF_DOWN).add(BigDecimal.valueOf(2)))
                    : BigDecimal.valueOf(60).multiply(r2.subtract(g2).divide(delta2, RoundingMode.HALF_DOWN)).add(BigDecimal.valueOf(4));
            if (h2.compareTo(BigDecimal.ZERO) < 0) {
                h2 = h2.add(BigDecimal.valueOf(360));
            }

            return h1.compareTo(h2);
        });

        colors.sort((a, b) -> {
            BigDecimal r1 = BigDecimal.valueOf(a.getRed(), 100).divide(BigDecimal.valueOf(255), RoundingMode.HALF_DOWN);
            BigDecimal g1 = BigDecimal.valueOf(a.getGreen(), 100).divide(BigDecimal.valueOf(255), RoundingMode.HALF_DOWN);
            BigDecimal b1 = BigDecimal.valueOf(a.getBlue(), 100).divide(BigDecimal.valueOf(255), RoundingMode.HALF_DOWN);

            BigDecimal r2 = BigDecimal.valueOf(b.getRed(), 100).divide(BigDecimal.valueOf(255), RoundingMode.HALF_DOWN);
            BigDecimal g2 = BigDecimal.valueOf(b.getGreen(), 100).divide(BigDecimal.valueOf(255), RoundingMode.HALF_DOWN);
            BigDecimal b2 = BigDecimal.valueOf(b.getBlue(), 100).divide(BigDecimal.valueOf(255), RoundingMode.HALF_DOWN);

            BigDecimal cmax1 = r1.max(g1).max(b1);
            BigDecimal cmin1 = r1.min(g1).min(b1);

            BigDecimal cmax2 = r2.max(g2).max(b2);
            BigDecimal cmin2 = r2.min(g2).min(b2);

            BigDecimal delta1 = cmax1.subtract(cmin1);
            BigDecimal delta2 = cmax2.subtract(cmin2);

            BigDecimal l1 = cmax1.add(cmin1).divide(BigDecimal.valueOf(2), RoundingMode.HALF_DOWN);
            BigDecimal l2 = cmax2.add(cmax2).divide(BigDecimal.valueOf(2), RoundingMode.HALF_DOWN);

            BigDecimal divisor1 = BigDecimal.ONE.subtract(l1.subtract(BigDecimal.ONE).abs());
            BigDecimal s1 = delta1.compareTo(BigDecimal.ZERO) == 0 || divisor1.compareTo(BigDecimal.ZERO) == 0 ?
                    BigDecimal.ZERO
                    : delta1.divide(divisor1, RoundingMode.HALF_DOWN);

            BigDecimal divisor2 = BigDecimal.ONE.subtract(l2.subtract(BigDecimal.ONE).abs());
            BigDecimal s2 = delta2.compareTo(BigDecimal.ZERO) == 0 || divisor2.compareTo(BigDecimal.ZERO) == 0 ?
                    BigDecimal.ZERO
                    : delta2.divide(divisor2, RoundingMode.HALF_DOWN);

            return s1.compareTo(s2);
        });

        System.out.printf("Elapsed time: %f%n", 1e-3 * System.currentTimeMillis() - start);
        start = 1e-3 * System.currentTimeMillis();
        System.out.println("Generating Mandelbrot...");
        BufferedImage mandelbrot = computeMandelbrot(colors);

        System.out.printf("Elapsed time: %f%n", 1e-3 * System.currentTimeMillis() - start);
        start = 1e-3 * System.currentTimeMillis();
        System.out.println("Writing to file...");
        ImageIO.write(mandelbrot, "png", new File("mandelbrot.png"));
        System.out.printf("Elapsed time: %f%n", 1e-3 * System.currentTimeMillis() - start);

        System.out.printf("%nTotal time: %f%n", 1e-3 * System.currentTimeMillis() - veryBeginning);
    }
}

As is, after running for 219 seconds, the code generates this image (screenshot as file is ~40MB):

enter image description here

I have no idea why the outside of the Mandelbrot is so random; it should have made use of my exterior distance estimate. Perhaps the inside is stealing too many of the colors....

If I remove the randomization on the points, I get this:

enter image description here

I get the feeling my exterior distance code is not working right...

Justin

Posted 2014-02-25T21:16:53.557

Reputation: 19 757

exteriorDistanceEstimate() consistently returns NaN – Mark Jeronimus – 2016-01-25T20:30:36.370

@MarkJeronimus I figured that out. It returns NaN almost all the time (there are some times where it doesn't though) because the double values always get too large, resulting in Infinity, so dividing makes NaN. I've been working out some of the bugs and it's getting better – Justin – 2016-01-25T21:48:47.980

3

Mathematica

Non-competing because the builtt-in HilbertCurve was introduced in Mathematica 11.1 in 2017.

Inspired by Joe K's answer. Generates 9 images in one program. Arranges the colors in three different orderings: the lexicographical order, the Z-order curve, the Hilbert curve; then places them in the image in (the 2-dimensional version of) these three orderings.

f[m_, n_] := {Tuples[Range[2^m] - 1, n],
   FromDigits[Partition[#, n], 2] & /@ Tuples[{0, 1}, n*m],
   First@HilbertCurve[m, n]};

Table[Image@Partition[a[[Ordering@b]]/2^6, 2^9], {a, f[6, 3]}, {b, f[9, 2]}]

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

alephalpha

Posted 2014-02-25T21:16:53.557

Reputation: 23 988

3

Java

This is similar to my other answer, but

By inserting this code:

for (int iy = 0; iy < 256; iy++) {
    for (int ix = 0; ix < 256; ix++) {
        for (int y = 0; y < 16; y++) {
            for (int x = 0; x < 16; x++) {
                int rgb = img_cln.getRGB((x << 8) + ix, (y << 8) + iy);
                img.setRGB(x + (ix << 4), y + (iy << 4), rgb);
            }
        }
    }
}

Like so:

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;

/**
 *
 * @author Quincunx
 */
public class AllColorImage {

    public static void main(String[] args) {
        BufferedImage img = new BufferedImage(4096, 4096, BufferedImage.TYPE_INT_RGB);
        BufferedImage img_cln = new BufferedImage(4096, 4096, BufferedImage.TYPE_INT_RGB);

        for (int r = 0; r < 256; r++) {
            for (int g = 0; g < 256; g++) {
                for (int b = 0; b < 256; b++) {
                    img_cln.setRGB(((r & 15) << 8) | g, ((r >>> 4) << 8) | b, (((r << 8) | g) << 8) | b);
                }
            }
        }

        for (int iy = 0; iy < 256; iy++) {
            for (int ix = 0; ix < 256; ix++) {
                for (int y = 0; y < 16; y++) {
                    for (int x = 0; x < 16; x++) {
                        int rgb = img_cln.getRGB((x << 8) + ix, (y << 8) + iy);
                        img.setRGB(x + (ix << 4), y + (iy << 4), rgb);
                    }
                }
            }
        }

        try {
            ImageIO.write(img, "png", new File("Filepath"));
        } catch (IOException ex) {
            Logger.getLogger(AllColorImage.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

I can get this output:

enter image description here

I'm still trying to figure out how to remove those lines.

Justin

Posted 2014-02-25T21:16:53.557

Reputation: 19 757

10Have you trying turning it off and on again? – Pierre Arlaud – 2014-02-27T12:36:40.100

4those are caused by some LSB bits; almost all of our loop-based ones have those little annoyances and they are a royal pain in the ass to remove – masterX244 – 2014-02-27T13:35:09.557

5@ArlaudPierre Blowing on it might work, too. – Jason C – 2014-02-28T03:48:39.473

3

I know I'm a late to this discussion but I thought I would post my attempt to this thread just in case it is useful/interesting to anyone.

GLSL

vec3 xy2rgb3( in vec2 pos ) {
    vec2 rc = pos * 8.0;
    vec2 mp = fract( rc );
    rc = (rc - mp) * 0.125;
    return vec3( (rc.y * 0.125) + rc.x, mp );
}

The live editable version can be found here xy2rgb3/rgb3toxy2.

enter image description here

lardratboy

Posted 2014-02-25T21:16:53.557

Reputation: 31

2

Mathematica

Flatten[Table[{r, g, b}, {r, 0, 1, 1/31}, {g, 0, 1, 1/31}, {b, 0, 1, 
 1/31}], 2]~Partition~256 // Image

enter image description here

RandomSample@
   Flatten[Table[{r, g, b}, {r, 0, 1, 1/31}, {g, 0, 1, 1/31}, {b, 0, 1, 
 1/31}], 2]~Partition~256 // Image

enter image description here

swish

Posted 2014-02-25T21:16:53.557

Reputation: 7 484

> or grid that can be screenshot and saved at 256×128 Please do this or I'll have to disqualify it – Mark Jeronimus – 2014-02-26T09:13:44.797

Still not exactly right, and also I count way less colors than supposed, including stretches of equal colors delimited by what looks like color-reducing dithering. – Mark Jeronimus – 2014-02-26T09:31:06.510

Better: n=31;ArrayPlot[ Partition[ Flatten@Table[ RGBColor[r/n, g/n, b/n], {r, 0, n}, {g, 0, n}, {b, 0, n}], 256], ImageSize -> {256, 128}] – DavidC – 2014-02-26T17:57:26.540

Zom-B. You may express your disagreement or dislike for a submission. But you cannot, by yourself, disqualify it. – DavidC – 2014-02-26T19:40:51.350

Shorter version: Image[Range[0,1,1/31]~Tuples~3~Partition~256] – Simon Woods – 2014-03-01T10:59:59.567

2

Processing with Xorshift

Xorshift is a type of PRNG which is very fast and somewhat reliable. A 16bit Xorshift generator will cycle through the numbers 1 to 65535, so for this task we skip any with the high bit set. What it won't do is ever produce the number 0, so we start painting at the second pixel. Each seed will produce the same result, and if you like you can try other seeds. Try it online.

enter image description here

void setup() {
    size(256,128);
    background(0);
}

int seed=1;
int xorshift() {
    seed ^= (seed << 13);
    seed ^= (seed >> 9);
    seed ^= (seed << 7);
}

void draw() {
    int i=1;
    while (i < 32768) {
        xorshift();
        while (seed > 32767) {
            xorshift();
        }
        stroke(seed<<3&255,seed>>2&255,seed>>7&255);
        point(i&255,floor(i/256));
        i++;
    }
    exit();
}

(Thank you to ace for the Processing boilerplate code.)

curiousdannii

Posted 2014-02-25T21:16:53.557

Reputation: 657

1Just use all the values above 32767, discard the top bit, and you don't have to skip 0. – Mark Jeronimus – 2014-10-14T11:02:44.963

1

Excel VBA, 165 bytes

A declared subroutine that takes no input and outputs to a 256 x 128 cell image to the range A1:IV128. For this algorithm, red values change as a function of the column number, blue values changes as a function of row number and green values change as a function of both the column and row number, with low values falling at the top left and high values falling at the bottom right.[!

Option Compare Text
Option Explicit
Option Base 0

Public Sub c()

    Dim row As Integer, _
        col As Integer

    Let Cells.ColumnWidth = 2.15
    Let Cells.RowHeight   = 16
    For col = rgbBlack To rgbRed
        For row = rgbBlack To 127
            Let Cells(row + 1, col + 1).Interior.Color = RGB( _
                                                        Red     := 8 * (col Mod 32), _
                                                        Green   := 8 * (Int(col / 32) + 8 * Int(row / 32)), _
                                                        Blue    := 8 * (row Mod 32))
    Next row, col
End Sub

This may be golfed down to a VBE immediate window function worth 165 bytes.

Cells.ColumnWidth=2.15:Cells.RowHeight=16:For c=0To 255:For r=0To 127:Cells(r+1,c+1).Interior.Color=RGB(8*(c Mod 32),8*(Int(c/32)+8*Int(r/32)),8*(r Mod 32)):Next r,c

Output

All Colors from Excel VBA

Taylor Scott

Posted 2014-02-25T21:16:53.557

Reputation: 6 709