Bilder mit allen Farben


433

Erstellen Sie ähnlich wie bei allrgb.com Bilder, bei denen jedes Pixel eine eindeutige Farbe hat (keine Farbe wird zweimal verwendet und keine Farbe fehlt).

Geben Sie ein Programm an, das ein solches Bild erzeugt, zusammen mit einem Screenshot oder einer Datei der Ausgabe (als PNG hochladen).

  • Erstellen Sie das Bild rein algorithmisch.
  • Das Bild muss 256 × 128 Pixel groß sein (oder ein Raster, das als Screenshot angezeigt und bei 256 × 128 Pixel gespeichert werden kann).
  • Verwenden Sie alle 15-Bit-Farben *
  • Keine externe Eingabe erlaubt (auch keine Webabfragen, URLs oder Datenbanken)
  • Keine eingebetteten Bilder erlaubt (Quellcode, der ein Bild ist, ist in Ordnung, zB Piet )
  • Dithering ist erlaubt
  • Dies ist kein Kurzcode-Wettbewerb, auch wenn er Ihnen möglicherweise die Stimmen einbringt.
  • Wenn Sie sich wirklich einer Herausforderung stellen möchten, tun Sie dies mit 512 × 512, 2048 × 1024 oder 4096 × 4096 (in Schritten von 3 Bit).

Die Wertung erfolgt nach Stimmenzahl. Stimmen Sie für die schönsten Bilder ab, die mit dem elegantesten Code und / oder dem interessantesten Algorithmus erstellt wurden.

Zweistufige Algorithmen, bei denen Sie zuerst ein ansprechendes Bild erstellen und dann alle Pixel an eine der verfügbaren Farben anpassen, sind natürlich zulässig, bringen Ihnen jedoch keine Eleganzpunkte ein.

* 15-Bit-Farben sind die 32768-Farben, die durch Mischen von 32 Rottönen, 32 Grüntönen und 32 Blautönen in äquidistanten Schritten und gleichen Bereichen erzeugt werden können. Beispiel: In 24-Bit-Bildern (8 Bit pro Kanal) beträgt der Bereich pro Kanal 0..255 (oder 0..224). Teilen Sie ihn also in 32 gleichmäßig verteilte Schattierungen auf.

Um ganz klar zu sein, sollte das Array von Bildpixeln eine Permutation sein, da alle möglichen Bilder die gleichen Farben haben, nur an verschiedenen Pixelpositionen. Ich werde hier eine triviale Permutation geben, die überhaupt nicht schön ist:

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();
        }
    }
}

Bildbeschreibung hier eingeben

Gewinner

Weil die 7 Tage vorbei sind, erkläre ich einen Sieger

Denken Sie jedoch keinesfalls, dass dies vorbei ist. Ich und alle Leser freuen uns immer über großartigere Designs. Hör nicht auf zu kreieren.

Gewinner: fejesjoco mit 231 Stimmen


8
Was meinen Sie mit "Dithering ist erlaubt"? Ist dies eine Ausnahme von der Regel "Jedes Pixel ist eine eindeutige Farbe"? Wenn nicht, was gestatten Sie, was sonst verboten war?
Peter Taylor

1
Dies bedeutet, dass Sie Farben in ein Muster einfügen können. Wenn Sie diese also mit dem Auge betrachten, werden sie in eine andere Farbe überblendet. Sehen Sie sich zum Beispiel das Bild "clear all RGB" auf der allRGB-Seite und viele andere dort an.
Mark Jeronimus

8
Ich finde Ihr triviales Permutationsbeispiel für das Auge sehr angenehm.
Jason C

2
@ Zom-B Mann, ich liebe diesen Beitrag. Vielen Dank!
Jason C

7
Schöne Ergebnisse / Antworten!
EthanB

Antworten:


534

C #

Ich setze ein zufälliges Pixel in die Mitte und beginne dann, zufällige Pixel in eine Nachbarschaft zu setzen, die ihnen am ähnlichsten ist. Es werden zwei Modi unterstützt: Bei minimaler Auswahl wird jeweils nur ein benachbartes Pixel berücksichtigt. bei durchschnittlicher Auswahl werden alle (1..8) gemittelt. Die minimale Auswahl ist etwas laut, die durchschnittliche Auswahl ist natürlich unschärfer, aber beide sehen tatsächlich wie Gemälde aus. Nach einiger Bearbeitung ist hier die aktuelle, etwas optimierte Version (sie verwendet sogar Parallelverarbeitung!):

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 Pixel, beginnend in der Mitte, Mindestauswahl:

256x128 Pixel, beginnend in der oberen linken Ecke, Mindestauswahl:

256x128 Pixel, beginnend in der Mitte, durchschnittliche Auswahl:

Hier sind zwei 10-Frame-Animgifs, die zeigen, wie die minimale und durchschnittliche Auswahl funktioniert (ein Lob an das GIF-Format, damit es nur mit 256 Farben angezeigt werden kann):

Der minimale Auswahlmodus wächst mit einer kleinen Wellenfront, die wie ein Blob alle Pixel füllt. Wenn jedoch im Durchschnittsmodus zwei verschiedenfarbige Zweige nebeneinander wachsen, entsteht eine kleine schwarze Lücke, da nichts nahe genug an zwei verschiedenen Farben liegt. Aufgrund dieser Lücken wird die Wellenfront eine Größenordnung größer sein, daher wird der Algorithmus viel langsamer sein. Aber es ist schön, weil es wie eine wachsende Koralle aussieht. Wenn ich den Durchschnittsmodus fallen lassen würde, könnte er etwas schneller gemacht werden, da jede neue Farbe mit jedem vorhandenen Pixel ungefähr 2-3 Mal verglichen wird. Ich sehe keine anderen Möglichkeiten, um es zu optimieren, ich denke, es ist gut genug, wie es ist.

Und die große Attraktion: Hier ist ein Rendering mit 512 x 512 Pixel, mittlerer Start, minimale Auswahl:

Ich kann einfach nicht aufhören damit zu spielen! Im obigen Code werden die Farben nach dem Zufallsprinzip sortiert. Wenn wir überhaupt nicht sortieren oder nach Farbton sortieren ( (c1, c2) => c1.GetHue().CompareTo(c2.GetHue())), erhalten wir jeweils die folgenden Werte (sowohl mittlerer Start als auch minimale Auswahl):

Eine andere Kombination, bei der die Korallenform bis zum Ende erhalten bleibt: Farbton bei durchschnittlicher Auswahl, mit einem 30-Frame-Animgif:

UPDATE: ES IST BEREIT !!!

Du wolltest Hi-Res, ich wollte Hi-Res, du warst ungeduldig, ich habe kaum geschlafen. Jetzt freue ich mich bekannt zu geben, dass es endlich fertig ist, Produktionsqualität. Und ich veröffentliche es mit einem Urknall, einem fantastischen 1080p YouTube Video! Klicken Sie hier, um das Video zu sehen. Machen wir es viral, um den Geek-Stil zu fördern. Ich poste auch Dinge in meinem Blog unter http://joco.name/ , es wird einen technischen Beitrag über alle interessanten Details, die Optimierungen, wie ich das Video gemacht habe usw. geben. Und schließlich teile ich die Quelle Code unter GPL. Es ist riesig geworden, so dass ein richtiges Hosting der beste Ort dafür ist. Ich werde den obigen Teil meiner Antwort nicht mehr bearbeiten. Kompilieren Sie unbedingt im Release-Modus! Das Programm lässt sich gut auf viele CPU-Kerne skalieren. Ein 4Kx4K-Render benötigt ca. 2-3 GB RAM.

Ich kann jetzt riesige Bilder in 5-10 Stunden rendern. Ich habe bereits einige 4Kx4K-Renderer, die ich später veröffentlichen werde. Das Programm hat viele Fortschritte gemacht, es wurden unzählige Optimierungen vorgenommen. Ich habe es auch benutzerfreundlich gemacht, damit jeder es leicht benutzen kann, es hat eine schöne Kommandozeile. Das Programm ist auch deterministisch zufällig, dh Sie können einen zufälligen Startwert verwenden und es wird jedes Mal das gleiche Bild generiert.

Hier sind einige große Renderings.

Mein Lieblings 512:


(Quelle: joco.name )

Die 2048er, die in meinem Video erscheinen :


(Quelle: joco.name )


(Quelle: joco.name )


(Quelle: joco.name )


(Quelle: joco.name )

Die ersten 4096 Renderings (TODO: Sie werden hochgeladen, und meine Website kann den großen Datenverkehr nicht verarbeiten, daher werden sie vorübergehend verschoben):


(Quelle: joco.name )


(Quelle: joco.name )


(Quelle: joco.name )


(Quelle: joco.name )


25
Das ist cool!
Jaa-c

5
Sehr schön :-D Jetzt mach ein paar größere!
Squeamish Ossifrage

20
Du bist ein wahrer Künstler! :)
AL

10
Wie viel kostet ein Druck?
Primo

16
Ich arbeite an riesigen Rendern und einem 1080p-Video. Dauert Stunden oder Tage. Ich hoffe, dass jemand in der Lage ist, einen Druck von einem großen Render zu erstellen. Oder sogar ein T-Shirt: Code auf der einen Seite, Bild auf der anderen. Kann jemand das arrangieren?
Fejesjoco

248

wird bearbeitet

Aktualisieren! 4096x4096 Bilder!

Ich habe meinen zweiten Beitrag zu diesem zusammengefasst, indem ich die beiden Programme miteinander kombiniert habe.

Eine vollständige Sammlung ausgewählter Bilder finden Sie hier auf Dropbox . (Hinweis: DropBox kann keine Vorschau für die 4096x4096-Bilder erstellen. Klicken Sie einfach darauf und dann auf "Herunterladen".)

Wenn Sie sich nur einen ansehen, schauen Sie sich diesen an (kachelbar)! Hier ist es verkleinert (und vieles mehr unten), original 2048x1024:

Bildbeschreibung hier eingeben

Dieses Programm geht Pfade von zufällig ausgewählten Punkten im Farbwürfel aus und zeichnet sie dann in zufällig ausgewählte Pfade im Bild. Es gibt viele Möglichkeiten. Konfigurierbare Optionen sind:

  • Maximale Länge des Farbwürfelpfads.
  • Maximaler Schritt zum Durchlaufen des Farbwürfels (größere Werte bewirken eine größere Varianz, minimieren jedoch die Anzahl der kleinen Pfade gegen Ende, wenn es eng wird).
  • Bild kacheln.
  • Derzeit gibt es zwei Bildpfadmodi:
    • Modus 1 (der Modus dieses ursprünglichen Beitrags): Findet einen Block nicht verwendeter Pixel im Bild und rendert diesen Block. Blöcke können entweder zufällig angeordnet oder von links nach rechts sortiert werden.
    • Modus 2 (der Modus meines zweiten Posts, den ich mit diesem zusammengeführt habe): Wählt einen zufälligen Startpunkt im Bild aus und wandert auf einem Pfad durch nicht verwendete Pixel. kann um benutzte Pixel gehen. Optionen für diesen Modus:
      • Festlegen der zu begehenden Richtungen (orthogonal, diagonal oder beides).
      • Gibt an, ob die Richtung nach jedem Schritt geändert werden soll (derzeit im Uhrzeigersinn, der Code ist jedoch flexibel) oder ob die Richtung nur geändert werden soll, wenn ein belegtes Pixel gefunden wird.
      • Option zum Mischen der Reihenfolge der Richtungsänderungen (anstatt im Uhrzeigersinn).

Es funktioniert für alle Größen bis zu 4096x4096.

Die vollständige Verarbeitungsskizze finden Sie hier: Tracer.zip

Ich habe alle Dateien in denselben Codeblock eingefügt, um Platz zu sparen (selbst wenn alle Dateien in einer Datei enthalten sind, handelt es sich immer noch um eine gültige Skizze). Wenn Sie eine der Voreinstellungen verwenden möchten, ändern Sie den Index in der gPresetZuordnung. Wenn Sie dies unter Verarbeitung ausführen, können Sie rwährend der Ausführung auf drücken , um ein neues Bild zu generieren.

  • Update 1: Optimierter Code zum Verfolgen der ersten nicht verwendeten Farbe / Pixel und nicht zum Durchsuchen von bekannten verwendeten Pixeln; Die Generierungszeit von 2048 x 1024 wurde von 10-30 Minuten auf etwa 15 Sekunden und die von 4096 x 4096 von 1-3 Stunden auf etwa 1 Minute verringert. Dropbox-Quelle und Quelle unten aktualisiert.
  • Update 2: Fehler behoben, der die Generierung von 4096x4096-Bildern verhinderte.
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;
  }

}

Hier ist ein vollständiger Satz von 256x128 Bildern, die mir gefallen:

Modus 1:

Mein Favorit aus dem Originalsatz (max_path_length = 512, path_step = 2, zufällig, 2x angezeigt, Link 256x128 ):

Bildbeschreibung hier eingeben

Andere (links zwei bestellt, rechts zwei zufällig, oben zwei Pfadlänge begrenzt, unten zwei unbegrenzt):

ordlimit randlimit Kampfmittelbegrenzung randnolimit

Dieser kann gekachelt werden:

randtile

Modus 2:

Diamanten Blumen Boxfade diagover BigDiamonds boxes2 Scherben

Diese können gekachelt werden:

bigtile Diamantfliesen Geschenkpapier

512x512 Auswahlen:

Kachelbare Diamanten, mein Favorit aus Mode 2; In diesem Beispiel können Sie sehen, wie die Pfade um vorhandene Objekte herumlaufen:

Bildbeschreibung hier eingeben

Größere Pfadstufe und maximale Pfadlänge, kachelbar:

Bildbeschreibung hier eingeben

Zufallsmodus 1, kachelbar:

Bildbeschreibung hier eingeben

Weitere Auswahlmöglichkeiten:

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Alle 512x512-Renderings befinden sich im Dropbox-Ordner (* _64.png).

2048 x 1024 und 4096 x 4096:

Diese sind zu groß, um sie einzubetten, und alle gefundenen Image-Hosts lassen sie auf 1600 x 1200 herunter. Ich rendere gerade einen Satz von 4096x4096 Bildern, sodass bald mehr zur Verfügung stehen. Anstatt alle Links hier einzuschließen, checken Sie sie einfach im Dropbox-Ordner aus (* _128.png und * _256.png, beachten Sie: Die 4096x4096 sind zu groß für die Dropbox-Vorschau, klicken Sie einfach auf "Download"). Hier sind jedoch einige meiner Favoriten:

2048x1024 große, fliesbare Diamanten (derselbe, mit dem ich zu Beginn dieses Beitrags verlinkt habe)

2048x1024 Diamanten (ich liebe diesen!), Verkleinert :

Bildbeschreibung hier eingeben

4096x4096 große, kachelbare Diamanten (Endlich! Klicken Sie auf "Download" im Dropbox-Link; es ist zu groß für die Vorschau), verkleinert:

4096x4096 große fliesbare Diamanten

4096x4096 Zufallsmodus 1 : Bildbeschreibung hier eingeben

4096x4096 eine andere coole

Update: Der voreingestellte Bildsatz 2048 x 1024 ist fertig und in der Dropbox. Das 4096x4096-Set sollte innerhalb einer Stunde fertig sein.

Es gibt jede Menge gute, ich habe es wirklich schwer, die zu posten, also schau bitte unter dem Ordner-Link nach!


6
Es erinnert mich an Nahaufnahmen einiger Mineralien.
Morwenn

3
Nicht Teil des Wettbewerbs, aber ich fand das irgendwie cool . Ich habe eine große Gaußsche Unschärfe und automatische Kontrastverbesserung auf eines der Bilder im Zufallsmodus 1 in Photoshop angewendet und es hat so etwas wie einen schönen Desktop-Hintergrund erzeugt.
Jason C

2
whoa, das sind coole bilder!
Sevenseacat

2
Erinnert mich an Gustav Klimt Texturen.
Kim

2
Wussten Sie, dass Sie Bilder in Dropbox verlinken können? Kopieren Sie einfach die Download - URL, die entfernen dl=1und das token_hash=<something>Teil und machen einen Link zu Ihrem Bild wie folgt aus : [![Alt text of my small preview image](https://i.stack.imgur.com/smallpreview.png)](https://dl.dropbox.com/linktoyourfullsiz‌​eimage.png). Ein weiterer Tipp: Sie können Ihre Bilder komprimieren (mit TruePNG bekomme ich gute Ergebnisse ( Download )). Ich konnte 28,1% der Dateigröße auf diesem Bild speichern .
user2428118

219

Python mit PIL

Dies basiert auf einem Newtonschen Fraktal , speziell für z → z 5 - 1 . Da es fünf Wurzeln und damit fünf Konvergenzpunkte gibt, wird der verfügbare Farbraum basierend auf dem Farbton in fünf Regionen aufgeteilt. Die einzelnen Punkte werden zuerst nach der Anzahl der Iterationen sortiert, die erforderlich sind, um ihren Konvergenzpunkt zu erreichen, und dann nach dem Abstand zu diesem Punkt, wobei früheren Werten eine leuchtendere Farbe zugewiesen wird.

Update: 4096x4096 große Renderer, gehostet auf allrgb.com .

Original (33,7 MB)

Eine Nahaufnahme des Zentrums (tatsächliche Größe):

Ein anderer Aussichtspunkt mit diesen Werten:

xstart = 0
ystart = 0

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

Original (32,2 MB)

Und eine andere mit diesen:

xstart = 0.5
ystart = 0.5

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

Original (27,2 MB)


Animation

Auf Wunsch habe ich eine Zoom-Animation zusammengestellt.

Brennpunkt: ( 0,50051 , -0,50051 ) Zoomfaktor
: 2 1/5

Der Brennpunkt ist ein etwas seltsamer Wert, weil ich keinen schwarzen Punkt vergrößern wollte. Der Zoomfaktor wird so gewählt, dass er sich alle 5 Bilder verdoppelt.

Ein 32x32 Teaser:

Eine 256x256-Version finden Sie hier:
http://www.pictureshack.org/images/66172_frac.gif (5.4MB)

Es kann Punkte geben, die mathematisch "auf sich selbst" zoomen, was eine unendliche Animation ermöglichen würde. Wenn ich welche identifizieren kann, füge ich sie hier hinzu.


Quelle

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")

6
Endlich ein Fraktal :) Liebe die. Außerdem ist dieses Grün bei 144 Grad meine Lieblingsfarbe (im Gegensatz zu reinem Grün bei 120 Grad, was nur langweilig ist).
Mark Jeronimus

2
Ich weiß nicht, ich mag eigentlich die AllRGB-Versionen besser. Die Notwendigkeit, den vollen Luminanzraum zu nutzen, betont die Farbverläufe.
Ilmari Karonen

2
+1 Endlich ein paar gute Fraktale! Der letzte ist mein persönlicher Favorit. Sie sollten ein Video zum Vergrößern machen! (@Quincunx: Hab auch deine gesehen; sie hatte meine Stimme vom ersten Tag an!)
Jason C

1
@ JasonC Ich habe eine Animation hinzugefügt;)
Primo

2
@primo Ich weiß, ich bin spät dran, aber ich wollte nur sagen, dass diese Bilder spektakulär sind.
Ashwin Gupta

130

Ich hatte diese Idee vom Fejesjoco-Algorithmus und wollte ein bisschen spielen, also fing ich an, meinen eigenen Algorithmus von Grund auf neu zu schreiben.

Ich poste dies, weil ich der Meinung bin, dass diese Herausforderung noch nicht abgeschlossen ist, wenn ich etwas Besseres * als das Beste aus euch machen kann. Zum Vergleich: Es gibt einige atemberaubende Designs auf allRGB, die meiner Meinung nach weit über das hier erreichte Niveau hinausgehen, und ich habe keine Ahnung, wie sie es geschafft haben.

*) wird noch durch Stimmen entschieden

Dieser Algorithmus:

  1. Beginnen Sie mit (wenigen) Samen, deren Farben so nah wie möglich an Schwarz sind.
  2. Führen Sie eine Liste aller nicht besuchten Pixel, die mit einem besuchten Punkt verbunden sind.
  3. Wählen Sie einen zufälligen ** Punkt aus dieser Liste
  4. Berechnen Sie die durchschnittliche Farbe aller berechneten Pixel [Bearbeiten ... in einem 9x9-Quadrat unter Verwendung eines Gaußschen Kernels] 8-fach (dies ist der Grund, warum es so glatt aussieht). Wenn keine gefunden werden, nehmen Sie Schwarz.
  5. Suchen Sie in einem 3x3x3-Würfel um diese Farbe nach einer nicht verwendeten Farbe.
    • Wenn mehrere Farben gefunden werden, nehmen Sie die dunkelste.
    • Wenn mehrere gleich dunkle Farben gefunden werden, wählen Sie eine zufällige aus diesen aus.
    • Wenn nichts gefunden wird, aktualisieren Sie den Suchbereich auf 5x5x5, 7x7x7 usw. Wiederholen Sie diesen Vorgang ab 5.
  6. Pixel auftragen, Liste aktualisieren und ab 3 wiederholen

Ich habe auch mit verschiedenen Wahrscheinlichkeiten für die Auswahl von Kandidatenpunkten experimentiert, indem ich gezählt habe, wie viele besuchte Nachbarn das ausgewählte Pixel hat, aber es hat den Algorithmus nur verlangsamt, ohne ihn hübscher zu machen. Der aktuelle Algorithmus verwendet keine Wahrscheinlichkeiten und wählt einen zufälligen Punkt aus der Liste aus. Dies führt dazu, dass sich Punkte mit vielen Nachbarn schnell füllen und es sich nur um eine wachsende feste Kugel mit einer unscharfen Kante handelt. Dies verhindert auch, dass benachbarte Farben nicht verfügbar sind, wenn die Spalten später im Prozess aufgefüllt werden.

Das Bild ist torisch.

Java

Download: com.digitalmodularBibliothek

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
  • ursprünglich 1 Samen
  • 1 Sekunde

Bildbeschreibung hier eingeben

  • 2048 × 1024
  • leicht gekachelt auf 1920 × 1080 Desktop
  • 30 Sekunden
  • negativ in Photoshop

Bildbeschreibung hier eingeben

  • 2048 × 1024
  • 8 Samen
  • 27 Sekunden

Bildbeschreibung hier eingeben

  • 512 × 512
  • 40 zufällige Samen
  • 6 Sekunden

Bildbeschreibung hier eingeben

  • 4096 × 4096
  • 1 Samen
  • Streifen werden deutlich schärfer (da sie aussehen, als könnten sie einen Fisch in Sashimi hacken)
  • Sah so aus, als wäre es in 20 Minuten fertig, aber ... konnte aus irgendeinem Grund nicht beendet werden, sodass ich jetzt über Nacht 7 Instanzen parallel ausführen kann.

[Siehe unten]

[Bearbeiten]
** Ich habe festgestellt, dass meine Methode zur Auswahl von Pixeln überhaupt nicht zufällig war. Ich dachte, eine zufällige Permutation des Suchraums wäre zufällig und schneller als eine echte zufällige Permutation (da ein Punkt nicht zweimal zufällig ausgewählt wird. Wenn ich ihn jedoch durch eine echte zufällige Permutation ersetze, bekomme ich immer mehr Rauschflecken in meinem Bild.

[Code von Version 2 wurde entfernt, weil ich die maximale Zeichenanzahl von 30.000 überschritten habe]

Bildbeschreibung hier eingeben

  • Der anfängliche Suchwürfel wurde auf 5x5x5 erhöht

Bildbeschreibung hier eingeben

  • Noch größer, 9x9x9

Bildbeschreibung hier eingeben

  • Unfall 1. Deaktivieren Sie die Permutation, damit der Suchraum immer linear ist.

Bildbeschreibung hier eingeben

  • Unfall 2. Versuchte eine neue Suchtechnik unter Verwendung einer FIFO-Warteschlange. Ich muss das noch analysieren, aber ich dachte, es lohnt sich zu teilen.

Bildbeschreibung hier eingeben

  • Wählen Sie immer innerhalb von X nicht verwendeten Pixeln von der Mitte aus
  • X reicht von 0 bis 8192 in Schritten von 256

Das Bild kann nicht hochgeladen werden: "Ups! Etwas Schlimmes ist passiert! Du bist es nicht, wir sind es. Das ist unsere Schuld." Bild ist einfach zu groß für Bild. Versuche es woanders ...

Bildbeschreibung hier eingeben

Ich experimentierte mit einem Scheduler-Paket, das ich in der digitalmodularBibliothek gefunden hatte, um die Reihenfolge zu bestimmen, in der die Pixel behandelt werden (anstelle der 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;
    }
}
  • Winkel (8)

Bildbeschreibung hier eingeben

  • Winkel (64)

Bildbeschreibung hier eingeben

  • CRT

Bildbeschreibung hier eingeben

  • Dithering

Bildbeschreibung hier eingeben

  • Blüte (5, X), wobei X im Bereich von 0,5 bis 20 in Schritten von X = X × 1,2 liegt

Bildbeschreibung hier eingeben

  • Mod

Bildbeschreibung hier eingeben

  • Pythagoras

Bildbeschreibung hier eingeben

  • Radial

Bildbeschreibung hier eingeben

  • Zufällig

Bildbeschreibung hier eingeben

  • Scanline

Bildbeschreibung hier eingeben

  • Spirale (X), wobei X im Bereich von 0,1 bis 200 in Schritten von X = X × 1,2 liegt
  • Sie können sehen, es reicht zwischen Radial bis Angular (5)

Bildbeschreibung hier eingeben

  • Teilt

Bildbeschreibung hier eingeben

  • SquareSpiral

Bildbeschreibung hier eingeben

  • XOR

Bildbeschreibung hier eingeben

Neues Eye-Food

  • Auswirkung der Farbauswahl durch max(r, g, b)

Bildbeschreibung hier eingeben

  • Auswirkung der Farbauswahl durch min(r, g, b)
  • Beachten Sie, dass diese genau die gleichen Eigenschaften / Details wie die oben genannten hat, nur mit unterschiedlichen Farben! (gleicher zufälliger Samen)

Bildbeschreibung hier eingeben

  • Auswirkung der Farbauswahl durch max(r, min(g, b))

Bildbeschreibung hier eingeben

  • Auswirkung der Farbauswahl nach Grauwert 299*r + 436*g + 114*b

Bildbeschreibung hier eingeben

  • Auswirkung der Farbauswahl durch 1*r + 10*g + 100*b

Bildbeschreibung hier eingeben

  • Auswirkung der Farbauswahl durch 100*r + 10*g + 1*b

Bildbeschreibung hier eingeben

  • Zufällige Unfälle bei 299*r + 436*g + 114*bÜberlauf in einer 32-Bit-Ganzzahl

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

  • Variante 3, mit Grauwert und Radial Scheduler

Bildbeschreibung hier eingeben

  • Ich habe vergessen, wie ich das geschaffen habe

Bildbeschreibung hier eingeben

  • Der CRT-Scheduler hatte auch einen Happy Integer Overflow-Fehler (die ZIP-Datei wurde aktualisiert), der dazu führte, dass er auf halber Strecke mit 512 × 512 Bildern statt in der Mitte gestartet wurde. So soll es aussehen:

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

  • InverseSpiralScheduler(64) (Neu)

Bildbeschreibung hier eingeben

  • Ein weiteres XOR

Bildbeschreibung hier eingeben

  • Erstes erfolgreiches 4096-Rendering nach dem Bugfix. Ich denke das war Version 3 SpiralScheduler(1)oder so

Bildbeschreibung hier eingeben (50 MB !!)

  • Version 1 4096, aber ich habe versehentlich die Farbkriterien aktiviert max()

Bildbeschreibung hier eingeben (50 MB !!)

  • 4096, jetzt mit min()
  • Beachten Sie, dass diese genau die gleichen Eigenschaften / Details wie die oben genannten hat, nur mit unterschiedlichen Farben! (gleicher zufälliger Samen)
  • Zeit: Ich habe vergessen, es aufzuzeichnen, aber der Zeitstempel der Datei ist 3 Minuten nach dem vorherigen Bild

Bildbeschreibung hier eingeben (50 MB !!)


Cool. Ihr letztes Bild ähnelt einer zweiten Idee, die ich herumgeworfen habe, obwohl ich das Gefühl habe, dass meine nicht so gut aussehen wird. Übrigens gibt es eine ähnliche coole unter allrgb.com/diffusive .
Jason C

Es war nur als Teaser gedacht, aber ich habe es bearbeitet, weil ich befürchtete, markiert zu werden, was anscheinend passiert ist :)
Mark Jeronimus

2
Auch die Unfälle sehen gut aus :). Der Farbwürfel scheint eine sehr gute Idee zu sein, und Ihre Render-Geschwindigkeiten sind im Vergleich zu meinen erstaunlich. Einige Designs auf allrgb haben eine gute Beschreibung, zum Beispiel allrgb.com/dla. Ich wünschte, ich hätte mehr Zeit für weitere Experimente, es gibt so viele Möglichkeiten ...
fejesjoco

Fast hätte ich vergessen, ich habe gerade einige meiner großen Renderings hochgeladen. Ich denke, eines von ihnen, das Regenbogen-Rauch / verschüttete Tinte-Ding, ist besser als alles andere auf allrgb :). Ich stimme zu, die anderen sind nicht so umwerfend, deshalb habe ich ein Video gemacht, um etwas mehr daraus zu machen :).
Fejesjoco

Der Quellcode und der Link zur Digisoft-Bibliothek wurden hinzugefügt, damit Sie meinen Code kompilieren können
Mark Jeronimus

72

C ++ mit Qt

Ich sehe dich Version:

Bildbeschreibung hier eingeben

unter Verwendung der Normalverteilung für die Farben:

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

oder zuerst sortiert nach Rot / Farbton (mit einer kleineren Abweichung):

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

oder einige andere Distributionen:

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Cauchy-Verteilung (hsl / rot):

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Sortierte Spalten nach Helligkeit (hsl):

Bildbeschreibung hier eingeben

Aktualisierter Quellcode - Erzeugt das 6. Bild:

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;
}

Schön gemacht. Ist es jedoch nicht möglich image.pixel(x, y) == 0, das zuerst platzierte Pixel zu überschreiben?
Mark Jeronimus

@ Zom-B: es kann, aber dann wird der letzte schwarz sein, so ist es innerhalb der Regeln ..
Jaa-c

Kein Regelproblem. Ich dachte nur, du hättest es vielleicht verpasst. Könnte auch ab 1 zählen. Ich liebe deine anderen!
Mark Jeronimus

@ Zom-B: danke, ich könnte noch ein paar hinzufügen, es gefällt mir irgendwie: P
Jaa-c

Der mit zwei Kreisen und der darunter sehen irgendwie aus wie ein Affengesicht.
Jason C

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();
        }
    }

}

und ein Eingabebild:

Lemur

Ich generiere so etwas:

acidLemur

unkomprimierte Version hier: https://www.mediafire.com/?7g3fetvaqhoqgh8

Mein Computer benötigt ungefähr 30 Minuten, um ein 4096 ^ 2-Image zu erstellen. Dies ist eine enorme Verbesserung gegenüber den 32 Tagen, die meine erste Implementierung in Anspruch genommen hätte.


1
Autsch; 32 Tage klang nicht lustig ..... der durchschnittliche Algorithmus in fejesjocos Antwort auf 4k vor der Optimierung hätte wahrscheinlich mehrere Monate
gedauert

5
Ich liebe seine Punkaugenbrauen!
Level River St

45

Java mit BubbleSort

(normalerweise ist Bubblesort nicht so beliebt, aber für diese Herausforderung hatte es endlich eine Verwendung :) Erzeugte eine Linie mit allen Elementen in 4096 Schritten Abstand und mischte sie dann; Die Sortierung ging durch und jedes Like hat 1 zu seinem Wert hinzugefügt, während es sortiert wurde, so dass Sie die Werte sortiert und alle Farben erhalten haben

Der Sourcecode wurde aktualisiert, um diese großen Streifen zu entfernen
(benötigt etwas bitweise Magie: 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;
    }
}

Ergebnis:

Alte 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;
    }
}

Ausgabevorschau


Es gibt bereits eine QuickSort-Version auf der allRGB-Seite.
Mark Jeronimus

1
@ Zom-B Quicksort ist ein anderer Algorithmus als Bubblesort
masterX244

43

C

Erzeugt aus Gründen, die ich nicht verstehe, einen Wirbel mit geraden und ungeraden Frames, die völlig unterschiedliche Wirbel enthalten.

Dies ist eine Vorschau der ersten 50 ungeraden Frames:

Vortex-Vorschau

Beispielbild konvertiert von PPM zu Demo vollständige Farbabdeckung:

Beispielbild

Später, wenn alles in Grau übergeht, sieht man immer noch, wie es sich dreht: längere Sequenz .

Code wie folgt. Geben Sie zum Ausführen die Bildnummer ein, z. B .:

./vortex 35 > 35.ppm

Ich habe das benutzt, um ein animiertes GIF zu bekommen:

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;
}

53
Du weißt, es ist C, wenn etwas passiert aus "Gründen, die ich nicht verstehe".
Nit

2
Ja, normalerweise weiß ich, was mich erwartet, aber hier habe ich nur herumgespielt, um zu sehen, welche Muster ich bekommen könnte, und diese nicht abschließende Reihenfolge innerhalb des Chaos ist entstanden.

8
Es wirbelt, weil Ihre Vergleichsfunktion nicht der Dreiecksungleichung folgt. Zum Beispiel ist r> b, b> g, g> r. Ich kann es nicht einmal nach Java portieren, da es sich bei Mergesort um genau diese Eigenschaft handelt. Daher erhalte ich die Ausnahme "Die Vergleichsmethode verstößt gegen den allgemeinen Vertrag!"
Mark Jeronimus

2
Ich werde es versuchen, p->b * 6 - q->g;aber wenn es den Wirbel zerstört, wird es nicht repariert!

4
+1 aus Gründen, die ich nicht verstehe.
Jason C

40

Java

Variationen eines Farbwählers in 512x512. Eleganter Code ist es nicht , aber mir gefallen die hübschen Bilder:

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";
    }
}

Wie geschrieben gibt es aus:

kein mischen

Wenn Sie es mit ausführen shuffle_block = true, werden die Farben in jedem 64x64-Block gemischt:

Mischen blockieren

Wenn Sie es mit ausführen shuffle_radius > 0, wird jedes Pixel mit einem zufälligen Pixel shuffle_radiusin x / y gemischt. Nachdem ich mit verschiedenen Größen gespielt habe, mag ich einen Radius von 32 Pixeln, da dadurch die Linien verwischt werden, ohne dass die Objekte zu stark verschoben werden:

Bildbeschreibung hier eingeben


3
ooh diese Bilder sind die schönsten
Sevenseacat

Das sind wirklich großartige 😍
Matthew

37

wird bearbeitet

Ich habe gerade erst mit C angefangen (nachdem ich in anderen Sprachen programmiert habe), fand es aber schwierig, den Grafiken in Visual C zu folgen, und habe dieses von @ace verwendete Verarbeitungsprogramm heruntergeladen.

Hier ist mein Code und mein Algorithmus.

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);  
  } 
}

Algorithmus

Beginnen Sie mit 4x4 Quadraten aller möglichen Kombinationen von 32 Werten für Grün und Blau in x, y. Format, 128x128 Quadrate erstellen Jedes 4x4-Quadrat hat 16 Pixel. Erstellen Sie daher ein Spiegelbild daneben, um 32 Pixel jeder möglichen Kombination von Grün und Blau pro Bild unten zu erhalten.

(Bizarrerweise sieht das volle Grün heller aus als das volle Cyan. Dies muss eine optische Täuschung sein. In Kommentaren klargestellt.)

Addieren Sie im linken Quadrat die roten Werte 0-15. Für das rechte Quadrat XOR diese Werte mit 16, um die Werte 16-31 zu erhalten.

Bildbeschreibung hier eingeben

Ausgabe 256x128

Dies ergibt die Ausgabe im oberen Bild unten.

Jedes Pixel unterscheidet sich jedoch von seinem Spiegelbild nur in dem höchstwertigen Bit des Rotwerts. Daher kann ich mit der Variablen eine Bedingung anwenden c, um das XOR umzukehren. Dies hat den gleichen Effekt wie das Austauschen dieser beiden Pixel.

Ein Beispiel dafür finden Sie im unteren Bild unten (wenn Sie die Codezeile auskommentieren, die derzeit auskommentiert ist).

Bildbeschreibung hier eingeben

512 x 512 - Eine Hommage an Andy Warhols Marylin

Inspiriert von Quincunx 'Antwort auf diese Frage mit einem "bösen Grinsen" in freihändigen roten Kreisen, ist hier meine Version des berühmten Bildes. Das Original hatte tatsächlich 25 farbige Marylins und 25 schwarz-weiße Marylins und war Warhols Hommage an Marylin nach ihrem frühen Tod. Siehe http://en.wikipedia.org/wiki/Marilyn_Diptych

Nachdem ich festgestellt hatte, dass die Verarbeitung die in 256 x 128 verwendeten Funktionen als semitransparent darstellt, wechselte ich zu anderen Funktionen. Die neuen sind undurchsichtig.

Und obwohl das Bild nicht vollständig algorithmisch ist, gefällt es mir eher.

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");

}

Bildbeschreibung hier eingeben

512x512 Dämmerung über einem See mit Bergen in der Ferne

Hier ein vollständig algorithmisches Bild. Ich habe mit dem Ändern der Farbe gespielt, die ich mit der Bedingung moduliere, aber ich komme gerade zu dem Schluss, dass Rot am besten funktioniert. Ähnlich wie beim Marylin-Bild zeichne ich zuerst die Berge und wähle dann die Helligkeit aus dem Bild, um das positive RGB-Bild zu überschreiben, während ich in die negative Hälfte kopiere. Ein kleiner Unterschied besteht darin, dass sich der Boden vieler Berge (da alle gleich groß gezeichnet sind) unterhalb des Lesebereichs erstreckt, sodass dieser Bereich während des Lesevorgangs einfach zugeschnitten wird (wodurch der gewünschte Eindruck von Bergen unterschiedlicher Größe entsteht). )

In diesem Fall verwende ich eine 8x4-Zelle mit 32 Rottönen für das Positive und die restlichen 32 Rottöne für das Negative.

Beachten Sie den Expicit-Befehl frameRate (1) am Ende meines Codes. Ich stellte fest, dass die Verarbeitung ohne diesen Befehl 100% eines Kerns meiner CPU verwenden würde, obwohl die Zeichnung abgeschlossen war. Soweit ich weiß, gibt es keine Sleep-Funktion, Sie können jedoch die Häufigkeit der Abfragen verringern.

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);
}

Bildbeschreibung hier eingeben


Weil es überhaupt nicht voll cyan ist. Es ist (0,217,217). Alle 32 Kombinationen sind vorhanden, nur nicht gedehnt [0,255]. Bearbeiten: Sie verwenden Schritte von 7, aber ich kann dies nicht im Code finden. Muss eine Verarbeitungssache sein.
Mark Jeronimus

@steveverrill In der Verarbeitung können Sie save("filename.png")den aktuellen Bildpuffer in einem Bild speichern. Andere Bildformate werden ebenfalls unterstützt. Es erspart Ihnen die Mühe, Screenshots zu machen. Das Bild wird im Ordner der Skizze gespeichert.
Jason C

@Jasonc danke für den Tipp, ich war mir sicher, dass es einen Weg geben muss, aber ich glaube nicht, dass ich diese bearbeiten werde. Ich habe den Rahmen um die Bilder teilweise verlassen, um sie voneinander zu trennen (2 Dateien für so kleine Bilder waren überflüssig.) Ich möchte einige Bilder in 512x512 (und es gibt eine, für die ich eine Idee habe) erstellen, also werde ich diese auf die Art und Weise hochladen du schlägst vor.
Level River St

1
@ Steveverrill Haha, die Warhols sind eine nette Geste.
Jason C

@ Zom-B Die Verarbeitung scheint viele Dinge zu tun, die (ärgerlicherweise) nicht in der Dokumentation erwähnt werden: Nicht die vollen 256 logischen Farbkanalwerte in der physischen Ausgabe verwenden, Farben mischen, wenn Sie nicht möchten, mit einem vollen Kern von meine CPU auch nach dem Zeichnen fertig. Trotzdem ist es einfach, darauf einzugehen, und Sie können diese Probleme umgehen, sobald Sie wissen, dass sie vorhanden sind (mit Ausnahme des ersten Problems, das ich noch nicht gelöst habe ...)
Level River St

35

Ich habe gerade alle 16-Bit-Farben (5r, 6g, 5b) in einer Hilbert-Kurve in JavaScript angeordnet.

Hilbert-Kurvenfarben

Vorheriges (nicht Hilbert-Kurve) Bild:

Hilbert-Kurve

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);

Bearbeiten : Es stellte sich heraus, dass es einen Fehler in meiner Funktion zum Berechnen der Hilbert-Kurve gab und dieser fehlerhaft war. nämlich r.x = (p.x & s) > 0; r.y = (p.y & s) > 0;geändert zur.x = (p.x & s) > 0 ? 1 : 0; r.y = (p.y & s) > 0 ? 1 : 0;

Edit 2: Ein weiteres Fraktal:

sierpinsky

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


Nett! Willkommen bei PPCG.
Jonathan Van Matre

Wie sieht es aus, wenn der Gang durch den Farbwürfel auch eine 3D-Hilbert-Kurve zeigt? Nm bearbeiten . Jemand hat genau das getan.
Mark Jeronimus

35

Iterative lokale Ähnlichkeitsoptimierung

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));
                    }
                }
            }
        }
    }
}

Idee

Zuerst beginnen wir mit einem zufälligen Shuffle:

Bildbeschreibung hier eingeben

Dann wählen wir zufällig zwei Pixel aus und tauschen sie aus. Wenn dies die Ähnlichkeit der Pixel mit ihren Nachbarn nicht erhöht, tauschen wir zurück und versuchen es erneut. Diesen Vorgang wiederholen wir immer wieder.

Nach nur wenigen Generationen (5000) sind die Unterschiede nicht mehr so ​​offensichtlich ...

Bildbeschreibung hier eingeben

Aber je länger es läuft (25000), ...

Bildbeschreibung hier eingeben

... je mehr bestimmte Muster auftauchen (100000).

Bildbeschreibung hier eingeben

Mit verschiedenen Definitionen für Nachbarschaft können wir diese Muster beeinflussen und ob sie stabil sind oder nicht. Dies Kernelist eine Matrix ähnlich derjenigen, die für Filter in der Bildverarbeitung verwendet wird . Es gibt die Gewichtung jedes Nachbarn an, der für die RGB-Delta-Berechnung verwendet wird.

Ergebnisse

Hier sind einige der Ergebnisse, die ich erstellt habe. Die Videos zeigen den iterativen Prozess (1 Frame = 1000 Generationen), aber leider ist die Qualität nicht die beste (Vimeo, YouTube usw. unterstützen solche kleinen Dimensionen nicht richtig). Ich kann später versuchen, Videos von besserer Qualität zu erstellen.

0 1 0
1 X 1
0 1 0

185000 Generationen:

Bildbeschreibung hier eingeben 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 Generationen:

Bildbeschreibung hier eingeben 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 Generationen:

Bildbeschreibung hier eingeben 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

Dieser Kernel ist interessant, weil aufgrund seiner Asymmetrie die Muster nicht stabil sind und sich das gesamte Bild im Laufe der Generationen nach rechts bewegt.

2331000 Generationen:

Bildbeschreibung hier eingeben Video (01:10)


Große Ergebnisse (512 x 512)

Wenn Sie die obigen Kernel mit einer größeren Bildgröße verwenden, werden dieselben lokalen Muster erstellt, die sich über eine größere Gesamtfläche erstrecken. Die Stabilisierung eines 512x512-Bildes dauert zwischen 1 und 2 Millionen Generationen.

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben


OK, jetzt machen wir es ernst und erstellen größere, weniger lokale Muster mit einem 15x15-Radialkern:

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

Dies erhöht die Rechenzeit pro Generation drastisch. 1,71 Millionen Generationen und 20 Stunden später:

Bildbeschreibung hier eingeben


1
Dauert eine Weile, aber das Endergebnis ist ganz nett.
Primo

Interessanter Zufall, ich habe einen Artikel zu diesem Thema: nayuki.io/page/simulated-annealing-demo
Nayuki

30

Java

Mit ein paar Variationen meiner anderen Antwort können wir einige sehr interessante Ergebnisse erzielen.

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);
        }
    }
}

Der wichtige Code ist hier:

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;
    }

});

Ausgabe (Screenshot):

Bildbeschreibung hier eingeben

Ändern Sie den Komparator folgendermaßen:

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;
}

Und wir bekommen das:

Bildbeschreibung hier eingeben

Eine andere Variante:

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;
}

Bildbeschreibung hier eingeben

Noch eine Variation (erinnert mich an zellulare Automaten):

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;
}

Bildbeschreibung hier eingeben

Noch eine andere Variante (neuer persönlicher Favorit):

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;
}

Bildbeschreibung hier eingeben

Es sieht so fraktal aus. XOR ist so schön, vor allem Nahaufnahme:

Bildbeschreibung hier eingeben

Eine weitere Nahaufnahme:

Bildbeschreibung hier eingeben

Und jetzt das Sierpinski-Dreieck, gekippt:

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;
}

Bildbeschreibung hier eingeben


8
Das erste Bild sieht aus wie ein CPU- oder Speicher-Die-Foto
Nick T

@ NickT Hier ist ein Memory Die (laut Google Images) für Kontrast: files.macbidouille.com/mbv2/news/news_05_10/25-nm-die.jpg
Justin

4
Richtig, der Speicher ist so formlos ... dann wahrscheinlich ein sehr vielkerniger Prozessor: extremetech.com/wp-content/uploads/2012/07/Aubrey_Isle_die.jpg
Nick T

1
Ich mag diese letzteren wirklich. Sehr unordentlich aussehend, aber mit einer zugrunde liegenden Organisationsstruktur. Ich möchte einen Teppich, der wie dieses XOR-Design gewebt ist!
Jonathan Van Matre

Diese sind wirklich cool; Sie erinnern mich irgendwie an ein kaputtes Arcade-Spiel oder ein Nes.
Jason C

29

Java

Eigentlich war ich mir nicht sicher, wie man 15- oder 18-Bit-Farben erzeugt, also habe ich einfach das niedrigstwertige Bit jedes Kanalbytes weggelassen, um 2 ^ 18 verschiedene 24-Bit-Farben zu erzeugen. Der größte Teil des Rauschens wird durch Sortieren entfernt, aber eine effektive Rauschentfernung würde den Vergleich von mehr als nur zwei Elementen gleichzeitig erfordern, wie dies bei Comparator der Fall ist. Ich werde versuchen, mit größeren Kerneln zu manipulieren, aber in der Zwischenzeit ist dies ungefähr das Beste, was ich tun konnte.

Bildbeschreibung hier eingeben

Klicken Sie für HD-Bild Nr. 2

Bild mit niedriger Auflösung # 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);
    }
}

1
Dieser zweite hat wirklich eine 4096 x 4096 24-Bit-Version verdient ...
Trichoplax

Imgur verarbeitet das Bild seit ungefähr einer halben Stunde. Ich denke, es wird wahrscheinlich versucht, es zu komprimieren. Jedenfalls habe ich einen Link hinzugefügt: SSend.it/hj4ovh
John P

2
Beim Herunterladen ist ein Problem aufgetreten.
SuperJedi224

28

Scala

Ich bestelle alle Farben, indem ich eine dreidimensionale Hilbert-Kurve über ein L-System gehe . Ich gehe dann die Pixel im Ausgabebild entlang einer zweidimensionalen Hilbert-Kurve und lege alle Farben an.

512 x 512 Ausgabe:

Bildbeschreibung hier eingeben

Hier ist der Code. Das meiste deckt nur die Logik und Mathematik des Bewegens durch drei Dimensionen mit Pitch / Roll / Yaw ab. Ich bin mir sicher, dass es einen besseren Weg gab, diesen Teil zu machen, aber na ja.

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"))
  }
}

28

C #

Wow, wirklich coole Sachen in dieser Herausforderung. Ich habe dies in C # ausprobiert und mit jeder einzelnen Farbe über die Random-Walk-Logik in ca. 3 Minuten ein 4096 x 4096-Bild (i7-CPU) erstellt.

Ok, also für den Code. Nachdem ich stundenlang recherchiert und versucht hatte, jede einzelne HSL-Farbe mithilfe von for-Schleifen im Code zu generieren, entschied ich mich für die Erstellung einer flachen Datei, aus der HSL-Farben gelesen werden können. Ich habe jede einzelne RGB-Farbe in eine Liste eingefügt und dann nach Farbton, Leuchtkraft und Sättigung sortiert. Dann habe ich die Liste in eine Textdatei gespeichert. ColorData ist nur eine kleine Klasse, die ich geschrieben habe und die eine RGB-Farbe akzeptiert und auch das HSL-Äquivalent speichert. Dieser Code ist ein RIESIGER RAM-Esser. Gebraucht ca. 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;
    }


}

Damit aus dem Weg. Ich habe eine Klasse geschrieben, um die nächste Farbe aus der generierten Datei zu erhalten. Hier können Sie den Farbtonanfang und das Farbtonende einstellen. In der Realität könnte und sollte dies wahrscheinlich auf die Dimension verallgemeinert werden, nach der die Datei zuerst sortiert wurde. Außerdem stelle ich fest, dass ich für eine Leistungssteigerung hier einfach die RGB-Werte in die Datei hätte einfügen und jede Zeile auf einer festen Länge halten können. Auf diese Weise hätte ich einfach den Byte-Versatz angeben können, anstatt jede Zeile durchzugehen, bis ich die Zeile erreicht hatte, bei der ich beginnen wollte. Aber für mich war es kein so großer Leistungstreffer. Aber hier ist diese Klasse

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;
    }
}

Nachdem wir nun die Farbdatei und eine Möglichkeit zum Lesen der Datei haben, können wir das Bild nun tatsächlich erstellen. Ich habe eine Klasse namens LockBitmap verwendet, um die Leistung beim Festlegen von Pixeln in einer Bitmap zu steigern. LockBitmap-Quelle

Ich habe eine kleine Vector2-Klasse zum Speichern von Koordinatenpositionen erstellt

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();
    }
}

Außerdem habe ich eine Klasse namens SearchArea erstellt, die beim Auffinden benachbarter Pixel hilfreich war. Sie geben das Pixel an, nach dem Sie Nachbarn suchen möchten, die Grenzen, innerhalb derer gesucht werden soll, und die Größe des "Nachbarquadrats", nach dem gesucht werden soll. Wenn also die Größe 3 ist, bedeutet dies, dass Sie ein 3x3-Quadrat mit dem angegebenen Pixel in der Mitte suchen.

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;
    }

}

Hier ist die Klasse, die tatsächlich den nächsten Nachbarn auswählt. Grundsätzlich gibt es 2 Suchmodi. A) Das volle Quadrat, B) nur der Umfang des Quadrats. Dies war eine Optimierung, die ich vorgenommen habe, um zu verhindern, dass das ganze Quadrat erneut durchsucht wird, nachdem festgestellt wurde, dass das Quadrat voll ist. Die DepthMap war eine weitere Optimierung, um zu verhindern, dass dieselben Quadrate immer wieder durchsucht werden. Ich habe dies jedoch nicht vollständig optimiert. Bei jedem Aufruf von GetNeighbors wird immer zuerst die vollständige Quadratsuche durchgeführt. Ich weiß, dass ich dies optimieren kann, um die Umkreissuche erst nach Abschluss des ersten vollen Quadrats durchzuführen. Zu dieser Optimierung bin ich noch nicht gekommen, und auch ohne ist der Code ziemlich schnell. Die auskommentierten "lock" -Zeilen sind, weil ich Parallel.ForEach an einem Punkt verwendet habe, aber festgestellt habe, dass ich mehr Code schreiben musste, als ich für dieses lol wollte.

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, großartig. Hier ist die Klasse, die das Bild erstellt

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);
    }


}

Und hier ist eine Beispielimplementierung:

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");

    }
}

Wenn RandomWalkSegments = 1 ist, geht es einfach los, wohin Sie es wollen, und beginnt bei der ersten ersten Farbe in der Datei.

Es ist nicht der sauberste Code, den ich zugeben werde, aber er läuft ziemlich schnell!

Beschnittene Ausgabe

3 Wege

128 Pfade

BEARBEITEN:

Also habe ich etwas über OpenGL und Shader gelernt. Ich habe eine Größe von 4096 x 4096 generiert, wobei jede Farbe auf der GPU mit 2 einfachen Shader-Skripten blitzschnell dargestellt wurde. Die Ausgabe ist langweilig, aber jemand könnte dies interessant finden und sich ein paar coole Ideen einfallen lassen:

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
}

Bearbeiten (15.10.16): Ich wollte nur einen Proof of Concept eines genetischen Algorithmus zeigen. Ich führe diesen Code noch 24 Stunden später auf einem 100x100-Satz zufälliger Farben aus, aber bis jetzt ist die Ausgabe wunderschön!Bildbeschreibung hier eingeben

Bearbeiten (26.10.16): Ich habe den genetischen Algorithmus-Code nun seit 12 Tagen ausgeführt. Und die Ausgabe wird immer noch optimiert. Es hat sich im Grunde genommen einem lokalen Minimum angenähert, aber es scheint noch weitere Verbesserungen zu finden:Bildbeschreibung hier eingeben

Edit: 8/12/17 - Ich habe einen neuen Random-Walk-Algorithmus geschrieben - im Grunde geben Sie eine Anzahl von "Walkern" an, aber anstatt zufällig zu laufen, wählen sie zufällig einen anderen Walker aus und meiden sie entweder (wählen Sie das nächste verfügbare Pixel, das am weitesten entfernt ist) ) - oder gehen Sie auf sie zu (wählen Sie das nächste verfügbare Pixel in ihrer Nähe). Ein Beispiel für eine Graustufenausgabe finden Sie hier (ich mache einen vollständigen 4096x4096-Farbrender, nachdem ich die Färbung verkabelt habe!):Bildbeschreibung hier eingeben


4
Ein bisschen verspätet, aber willkommen bei PPCG! Dies ist ein ausgezeichneter erster Beitrag.
ein Spaghetto

1
Danke! Ich freue mich auf weitere Herausforderungen! Ich habe in letzter Zeit mehr Bildcodierungs-Sachen gemacht, es ist mein neues Hobby
applejacks01

Wow diese sind fantastisch; Ich bin froh, dass ich heute zu diesem Beitrag zurückgekehrt bin und all die späteren Sachen durchgesehen habe.
Jason C

Danke! Ich mache gerade eine genetische Algorithmuscodierung, um interessante Gradienten zu erzeugen. Nehmen Sie im Allgemeinen 10000 Farben und bilden Sie ein Raster von 100x100. Holen Sie sich für jedes Pixel die Nachbarpixel. Holen Sie sich für jede den CIEDE2000-Abstand. Fassen Sie das zusammen. Summiere das für alle 10000 Pixel. Der genetische Algorithmus versucht, diese Gesamtsumme zu reduzieren. Es ist langsam, aber für ein 20x20-Bild ist die Ausgabe wirklich interessant
applejacks01

Ich mag die Ausgabe dieser Lösung ganz besonders.
r_alex_hall

22

HTML5 Canvas + JavaScript

Ich nenne es randoGraph und Sie können erstellen , wie viele Sie wollen hier

Einige Beispiele:

Beispiel 1

Beispiel 2

Beispiel 3

Beispiel 4

Beispiel 5

Beispiel 6

Beispiel 7

Zum Beispiel können Sie in Firefox mit der rechten Maustaste auf den Canvas klicken (wenn dieser fertig ist) und ihn als Bild speichern. Die Erzeugung eines 4096x4096-Bildes ist aufgrund der Speicherbeschränkung einiger Browser ein Problem.

Die Idee ist recht einfach, aber jedes Bild ist einzigartig. Zuerst erstellen wir die Farbpalette. Beginnen wir dann mit X-Punkten, wählen wir zufällige Farben aus der Palette und deren Positionen aus (jedes Mal, wenn wir eine Farbe auswählen, löschen wir sie aus der Palette) und zeichnen auf, wo wir sie ablegen, damit sie beim nächsten Pixel nicht an derselben Position liegen.

Für jedes Pixel, das tangential dazu ist, erstellen wir eine Anzahl (X) möglicher Farben und wählen dann das für dieses Pixel relevanteste aus. Dies geht so lange weiter, bis das Bild vollständig ist.

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

Und das JavaScript für 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;
};

Das ist schön, aber es sieht aus wie die C # -Antwort von Fejesjoco . Ist es nur zufällig?
AL

1
Die Algorithmen sind hier und jeder kann lesen und verstehen, dass sie wirklich unterschiedlich sind. Diese Antwort wurde veröffentlicht, nachdem C # -Antwort von fejesjoco als Gewinner erklärt wurde und motiviert war, wie gut das Ergebnis ist. Dann dachte ich mir einen ganz anderen Ansatz für die Verarbeitung und Auswahl von Nachbarfarben, und das ist es. Natürlich haben beide Antworten die gleiche Grundlage, wie die gleichmäßige Verteilung der verwendeten Farben entlang des sichtbaren Spektrums, das Konzept der relevanten Farben und die Ausgangspunkte, und vielleicht könnte man denken, dass die erzeugten Bilder in einigen Fällen eine Ähnlichkeit haben.
konstantinosX

Okay, es tut mir leid, wenn Sie dachten, ich würde Ihre Antwort kritisieren. Ich habe mich nur gefragt, ob Sie von der Antwort von fejesjoco inspiriert wurden, da die resultierende Ausgabe ähnlich aussieht.
AL

1
„Das Definieren von Methoden einer Klasse innerhalb des Konstruktors anstelle der Verwendung der Prototypenkette ist wirklich ineffizient, insbesondere wenn diese Klasse mehrmals verwendet wird.“ Ist ein sehr interessanter Kommentar von Patrick Roberts. Haben Sie eine Referenz mit einem Beispiel, das dies bestätigt? Ich möchte aufrichtig wissen, ob diese Behauptung eine Grundlage hat (um sie nicht mehr zu benutzen) und was es ist.
konstantinosX

2
In Bezug auf die Verwendung von Prototypen: Es funktioniert ähnlich wie eine statische Methode. Wenn Sie die Funktion im Objektliteral definiert haben, muss jedes neue Objekt, das Sie erstellen, auch eine neue Kopie der Funktion erstellen und mit dieser Objektinstanz speichern (16 Millionen Farbobjekte bedeuten also 16 Millionen Kopien derselben Funktion in Erinnerung). Im Vergleich dazu wird ein Prototyp nur einmal erstellt, um der "Klasse" und nicht dem Objekt zugeordnet zu werden. Dies hat offensichtliche Gedächtnisvorteile sowie potenzielle Geschwindigkeitsvorteile.
Mwr247

20

Python

Hier ist also meine Lösung in Python. Es dauert fast eine Stunde, um eine zu erstellen. Es ist also wahrscheinlich eine Optimierung erforderlich:

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")

Hier sind einige Beispielausgaben:

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben


1
Sieht aus wie einige verrückte Klangwellenformen
Mark Jeronimus

19

Java

Ich beschloss, mich an dieser Herausforderung zu versuchen. Diese Antwort hat mich inspiriert auf einen anderen Code Golf hat . Mein Programm erzeugt hässlichere Bilder, aber sie haben alle Farben.

Auch mein erstes Mal Code Golf. :)

(4k-Bilder waren zu groß für meine geringe Upload-Geschwindigkeit. Ich habe versucht, eines hochzuladen, aber nach einer Stunde wurde es nicht hochgeladen. Sie können Ihr eigenes generieren.)

Nahansicht:

Erzeugt ein Bild in 70 Sekunden auf meinem Computer und benötigt beim Erzeugen ca. 1,5 GB Speicher

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());

    }

}

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]

Ergebnis (2x):

256x128 2x

Ursprüngliches 256x128 Bild

Bearbeiten:

Wenn Sie Log10 [i] durch Log10 [i] / 5 ersetzen, erhalten Sie: Bildbeschreibung hier eingeben

Der obige Code bezieht sich auf simuliertes Tempern. So gesehen wird in den ersten 10 ^ 6 Schritten das zweite Bild mit einer höheren "Temperatur" erzeugt. Die höhere "Temperatur" bewirkt mehr Permutationen zwischen den Pixeln, während im ersten Bild die Struktur des geordneten Bildes noch etwas sichtbar ist.


17

JavaScript

Ich bin noch Student und habe meine Codes zum ersten Mal gepostet, daher sind sie wahrscheinlich unordentlich. Ich bin mir nicht 100% sicher, ob meine Bilder alle benötigten Farben haben, aber ich war super zufrieden mit meinen Ergebnissen und dachte mir, ich würde sie posten.

Ich weiß, dass der Wettbewerb vorbei ist, aber ich mochte die Ergebnisse dieser und ich mochte immer das Aussehen von durch rekursives Backtracking generierten Labyrinthen, so dass ich dachte, es wäre cool zu sehen, wie man aussehen würde, wenn man farbige Pixel platzieren würde. Also beginne ich damit, alle Farben in einem Array zu generieren, und führe dann das rekursive Backtracking durch, während ich die Farben aus dem Array entferne.

Hier ist meine 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 Bild, Farben sortiert nach Rot-> Grün-> Blau
RGB-sortierte Farben

256x128 Bild, Farben sortiert blau-> grün-> rot
BGR sortierte Farben

256x128 Bild, Farben sortiert nach Farbton-> Luminanz-> Sättigung
HLS-sortierte Farben

Und schließlich wird ein GIF von einem generiert
Farblabyrinth GIF


Ihre Farben werden in den hellsten Bereichen abgeschnitten, was zu Duplikaten führt. Ändern Sie r * Math.ceil(255 / (colorSteps - 1)auf r * Math.floor(255 / (colorSteps - 1)oder noch besser: r * 255 / (colorSteps - 1)(ungetestet, da Sie keine Jsfiddle geliefert haben)
Mark Jeronimus

Hoppla, ja, ich hatte das Gefühl, dass es Probleme geben würde, hoffentlich ist es jetzt behoben, und es tut mir leid, dass es kein Jsfiddle gibt (ich wusste nicht, dass es das gibt!). Danke!
Kuligoawesome

Ich liebe die geordnete Ausgabe im Chaos- / Rausch-Look und eine andere Lösung, die eine ähnliche Ausgabe erzeugt.
r_alex_hall

17

C #

Also habe ich angefangen, nur als lustige Übung daran zu arbeiten, und bin zu einer Ausgabe gekommen, die zumindest für mich ziemlich ordentlich aussieht. Der Hauptunterschied zwischen meiner Lösung und (zumindest) den meisten anderen besteht darin, dass ich genau die Anzahl der Farben generiere, die zu Beginn benötigt werden, und die Generierung gleichmäßig von reinem Weiß zu reinem Schwarz aufteilt. Ich stelle auch Farben ein, die in einer nach innen gerichteten Spirale arbeiten, und wähle die nächste Farbe basierend auf dem Durchschnitt des Farbunterschieds zwischen allen eingestellten Nachbarn aus.

Hier ist eine kleine Beispielausgabe, die ich bisher produziert habe. Ich arbeite an einem 4k-Rendering, aber ich erwarte, dass es einen Tag länger dauern wird, bis es fertig ist.

Hier ist ein Beispiel für die Ausgabe der Spezifikation bei 256 x 128:

Spec Render

Einige größere Bilder mit noch angemessenen Renderzeiten:

Rendern Sie mit 360 x 240

Der zweite Durchlauf bei 360 x 240 ergab ein viel weicheres Bild

Rendern Sie # 2 bei 360 x 240

Nachdem ich die Leistung verbessert hatte, konnte ich einen HD-Render ausführen, der 2 Tage dauerte. Ich habe noch keinen 4k-Render aufgegeben, aber es könnte Wochen dauern.

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;
        }
    }
}

Wenn jemand darüber nachdenkt, wie die Leistung des Farbauswahlalgorithmus verbessert werden kann, lass es mich bitte wissen, da das Rendern mit 360 * 240 etwa 15 Minuten dauert. Ich glaube nicht, dass es parallelisiert werden kann, aber ich frage mich, ob es einen schnelleren Weg geben würde, um den niedrigsten Farbunterschied zu erzielen.
Phaeze

Wie macht ein Bild von 360 * 240 "alle Farben" aus? Wie produzieren Sie cbrt (360 * 240) = 44.208377983684639269357874002958 Farben pro Komponente?
Mark Jeronimus

Welche Sprache ist das? Das zufällige Sortieren einer Liste und des Zufalls ist ungeachtet dessen eine schlechte Idee, da dies je nach Algorithmus und Implementierung zu einem verzerrten Ergebnis oder einer Ausnahme führen kann, die Folgendes besagt "Comparison method violates its general contract!": Der Vertrag besagt dies (x.compareTo(y)>0 && y.compareTo(z)>0) implies x.compareTo(z)>0. Verwenden Sie eine der bereitgestellten Shuffle-Methoden, um eine Liste nach dem Zufallsprinzip zu sortieren. ( colors.Shuffle()?)
Mark Jeronimus

@MarkJeronimus Ich gebe zu, dass ich die Spezifikation für das 256x128-Bild verpasst habe. Ich werde die einfachen Renderings mit diesen Größen wiederholen. Ich habe mich darauf konzentriert, dass jedes Pixel ein einzigartiger Farbaspekt der Herausforderung ist und größere Renderings wie bei anderen Einsendungen.
Phaeze

@MarkJeronimus Mir ist klar, dass die zufällige Sortierung schlecht ist, in der Tat gibt es einen Kommentar, der genau das sagt. Dies war nur ein Überbleibsel eines anderen Ansatzes, den ich eingeschlagen hatte, und ich legte großen Wert darauf, die großen Renderings fertigzustellen, da sie sehr lange dauern.
Phaeze

16

Gehen

Hier ist eine andere von mir, ich denke, es gibt interessantere Ergebnisse:

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()
}

Es beginnt mit dem gleichen Muster wie das GIF in meiner anderen Antwort . Dann mischt es es in diese:

nur Lärm

Je mehr Iterationen ich mit dem eher uninspirierten Nachbarn-Vergleichsalgorithmus durchführe, desto deutlicher wird das Regenbogenmuster.

Hier ist 16384:

ein sehr lauter Regenbogen bei 16384 Iterationen

Und 65536:

Bildbeschreibung hier eingeben


6
+1 Ich mag es, dass daraus ein Muster entsteht; Sie sollten eine Animation davon machen!
Jason C

16

Diese Bilder sind "Langtons Regenbogen". Sie sind recht einfach gezeichnet: Während Langtons Ameise sich bewegt, wird auf jedes Pixel eine Farbe gezeichnet, wenn dieses Pixel zum ersten Mal aufgerufen wird. Die Farbe, die als nächstes gezeichnet werden soll, wird einfach um 1 erhöht, wodurch sichergestellt wird, dass 2 ^ 15 Farben verwendet werden, eine für jedes Pixel.

BEARBEITEN: Ich habe eine Version erstellt, die 4096X4096 Bilder mit 2 ^ 24 Farben rendert. Die Farben werden auch "reflektiert", so dass schöne, gleichmäßige Verläufe entstehen. Ich werde nur Links zu ihnen bereitstellen, da sie riesig sind (> 28 MB)

Langtons Regenbogen groß, Regel LR

Langton's Rainbow groß, Regel LLRR

// Ende der Bearbeitung.

Dies für den klassischen LR-Regelsatz:

Langtons Regenbogen LR

Hier ist LLRR:

Langtons Regenbogen LLRR

Schließlich verwendet dieser den Regelsatz LRRRRRLLR:

Langtons Regenbogen LRRRRRLLR

In C ++ geschrieben, mit CImg für Grafiken. Ich sollte auch erwähnen, wie die Farben ausgewählt wurden: Zuerst verwende ich eine Kurzform ohne Vorzeichen, um die RGB-Farbdaten zu enthalten. Jedes Mal, wenn eine Zelle zum ersten Mal besucht wird, verschiebe ich die Bits nach rechts um ein Vielfaches von 5 und um 31 und multipliziere dann mit 8. Dann wird die vorzeichenlose Kurzfarbe um 1 erhöht. Dies ergibt Werte von höchstens 0 bis 248. Ich habe diesen Wert jedoch von 255 in den roten und blauen Komponenten subtrahiert , daher sind R und B in Vielfachen von 8, beginnend von 255 bis hinunter zu 7:

c[0]=255-((color&0x1F)*8);
c[2]=255-(((color>>5)&0x1F)*8);
c[1]=(((color>>10)&0x1F)*8);

Dies gilt jedoch nicht für die grüne Komponente, die in Vielfachen von 8 von 0 bis 248 vorliegt. In jedem Fall sollte jedes Pixel eine eindeutige Farbe enthalten.

Wie auch immer, der Quellcode ist unten:

#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;
}

1
Willkommen, und treten Sie dem Club bei. Vielleicht ist es interessant, andere Turmites von code.google.com/p/ruletablerepository/wiki/… auszuprobieren (ich habe daran teilgenommen)
Mark Jeronimus

3
Bildverknüpfungen sind tot, weil Dropbox öffentliche Ordner getötet hat .
user2428118

15

Rubin

Ich beschloss, das PNG von Grund auf neu zu erstellen, weil ich das interessant fand. Dieser Code gibt die rohen Binärdaten buchstäblich in eine Datei aus.

Ich habe die 512x512-Version gemacht. (Der Algorithmus ist jedoch ziemlich uninteressant.) Auf meinem Computer dauert es ungefähr 3 Sekunden.

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'

Ausgabe (in all_colors.png) (Klicken Sie auf eines dieser Bilder, um es zu vergrößern):

Ausgabe

Etwas interessantere gradientenartige Ausgabe (durch Ändern der 4. bis letzten Zeile in }.shuffle }):

Ausgang 2

Und wenn Sie es auf ändern }.shuffle }.shuffle, erhalten Sie verrückte Farblinien:

Ausgang 3


Das ist wirklich cool. Gibt es eine Möglichkeit, wie Sie es schöner machen könnten? Vielleicht die Pixel randomisieren? Scoring is by vote. Vote for the most beautiful images made by the most elegant code.

1
@LowerClassOverflowian Ok, bearbeitet
Türklinke

Viel besser!!!!!!!

1
Was passiert, wenn Sie die 4. in die letzte Zeile geändert haben }.shuffle }.shuffle }.shuffle?
John Odom

6
@ John Ähm, Syntaxfehler, wahrscheinlich?
Türklinke

14

Python

Plasma

Verwenden Sie Python, um die Farben nach Luminanz zu sortieren, ein Luminanzmuster zu erstellen und die am besten geeignete Farbe auszuwählen. Die Pixel werden in zufälliger Reihenfolge iteriert, sodass die ungünstigeren Luminanzübereinstimmungen, die natürlich auftreten, wenn die Liste der verfügbaren Farben kleiner wird, gleichmäßig über das Bild verteilt sind.

#!/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")

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);
        }
    }
}

Ich habe 4096 mal 4096 gewählt, weil ich nicht herausfinden konnte, wie ich alle Farben erhalten kann, ohne dies zu tun.

Ausgabe:

Zu groß, um hierher zu passen. Dies ist ein Screenshot:

Bildbeschreibung hier eingeben

Mit einer kleinen Änderung können wir ein schöneres Bild bekommen:

Fügen Sie Collections.shuffle(points, new Random(0));zwischen dem Erzeugen der Punkte und dem Ausführen der Farben Folgendes hinzu:

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);
        }
    }
}

Bildbeschreibung hier eingeben

Nahansicht:

Bildbeschreibung hier eingeben


29
Sie nennen einen großen grauen Fleck "schön"?
Türknauf

22
@Doorknob Ja. Ich nenne es sehr schön. Ich finde es erstaunlich, dass alle Farben in einem großen grauen Fleck angeordnet werden können. Ich finde den Blob interessanter, wenn ich hinein zoome. Mit ein bisschen mehr Detail können Sie sehen, wie unzufällig Javas Rng ist. Wenn wir noch weiter hineinzoomen, wie im zweiten Screenshot, wird deutlich, wie viele Farben in dem Ding sind. Wenn ich noch weiter zoome, sieht es aus wie ein Piet-Programm.
Justin

Ich habe die Farben in kleineren Versionen durch Fallenlassen der unteren Bits erhalten.
Mark Jeronimus

Ja, die unteren Bits für r, gund bgetrennt, aber ich war mit ihnen als eine Nummer zu tun.
Justin

Wie ich sehe, haben Sie in Ihrer nächsten Antwort die Magie herausgefunden. In Bezug auf ein Thema könnte es interessant sein, mit Ihrer eigenen RandomUnterklasse zu experimentieren , die noch weniger ideale Zufallszahlen erzeugt.
Mark Jeronimus

13

C ++ 11

( Update: erst danach ist mir ein ähnlicher Ansatz aufgefallen bereits ausprobiert wurde - mit mehr Geduld hinsichtlich der Anzahl der Iterationen.)

Für jedes Pixel definiere ich eine Menge von Nachbarpixeln. Ich definiere die Diskrepanz zwischen zwei Pixeln als die Summe der Quadrate ihrer R / G / B-Differenzen. Die Strafe für ein bestimmtes Pixel ist dann die Summe der Diskrepanzen zwischen dem Pixel und seinen Nachbarn.

Jetzt erstens erstelle ich eine zufällige Permutation und beginne dann damit, zufällige Pixelpaare auszuwählen. Wenn das Vertauschen der beiden Pixel die Summe der Gesamtstrafen aller Pixel verringert, wird das Vertauschen durchgeführt. Ich wiederhole das millionenfach.

Die Ausgabe erfolgt im PPM-Format, das ich mit Standarddienstprogrammen in PNG konvertiert habe.

Quelle:

#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;
}

Wenn Sie den Schritt der Nachbarn variieren, erhalten Sie unterschiedliche Ergebnisse. Dies kann in der Funktion Image :: pixelNeighbors () angepasst werden. Der Code enthält Beispiele für vier Optionen: (siehe Quelle)

Beispiel 01 Beispiel 02 Beispiel 03 Beispiel 04

Edit: Ein weiteres Beispiel ähnlich dem vierten oben, aber mit einem größeren Kernel und mehr Iterationen:

Beispiel 05

Eins mehr: mit

const int iRad = 7, jRad = 7;
auto condition = [](int i, int j) { return (i % 2==0 && j % 2==0); };

und zehn Millionen Iterationen habe ich Folgendes erhalten:

Beispiel 06


11

Nicht der eleganteste Code, aber in zweierlei Hinsicht interessant: Berechnen der Anzahl der Farben aus den Dimensionen (sofern das Produkt der Dimensionen eine Zweierpotenz ist) und Ausführen trippy Farbraum-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;
}

Einige interessante Variationen können nur durch Ändern der OrderBy-Klausel erhalten werden:

X:

Bildbeschreibung hier eingeben

y:

Bildbeschreibung hier eingeben

z:

Bildbeschreibung hier eingeben

Y:

Bildbeschreibung hier eingeben

Ich wünschte, ich könnte herausfinden, was die ungeraden Zeilen in den ersten drei verursacht hat


2
Diese ungeraden Zeilen sind wahrscheinlich die Tendenz einer Art oder Suchmethode (binäre Suche / Quicksortierung?)
Mark Jeronimus

Ich mag die Zeilen hier wirklich sehr.
Jason C

11

Java

Das war eine viel bessere Idee. Dies ist ein sehr kurzer Java-Code. Die Hauptmethode ist nur 13 Zeilen lang:

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);
        }
    }
}

Erzeugt Blöcke von "Farbwählern". Grundsätzlich im ersten Block, r=0im zweiten r=1usw. In jedem Block werden gInkremente in Bezug auf xund bin Bezug auf y.

Ich mag bitweise Operatoren sehr. Lassen Sie mich die setRGBAussage zusammenfassen:

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

Aufgrund der bitweisen Operatoren dauert dies nur 7 Sekunden. Wenn das r & 15durch ersetzt wirdr % 16 , dauert es 9 Sekunden.

Ich entschied mich für die 4096 x 4096

Ausgabe (Screenshot, sonst zu groß):

Bildbeschreibung hier eingeben

Ausgabe mit bösem Grinsen, gezeichnet von freihändig-roten Kreisen:

Bildbeschreibung hier eingeben


2
Link zum Original, damit ich die Gültigkeit überprüfen kann (Farben zählen)
Mark Jeronimus

2
Lol! Ich habe vergessen, dass ich Java-Code ausführen kann. Das erste Bild ist vorbei und ich kann das zweite Bild nicht reproduzieren (lol)
Mark

16
Die Freihandkreise haben alle die gleiche Farbe, disqualifiziert. : P
Nick T

3
@Quincunx +1, wenn Sie ein gruseliges Gesicht zeichnen und trotzdem die Farbanforderungen einhalten können!
Jason C

2
@ JasonC Siehe meine Antwort. Wir danken Quincunx für die Inspiration.
Level River St
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.